diff --git a/ruoyi-mill/src/main/java/com/ruoyi/mill/domain/MillProductionActual.java b/ruoyi-mill/src/main/java/com/ruoyi/mill/domain/MillProductionActual.java index bb88c0b6..d8477ca3 100644 --- a/ruoyi-mill/src/main/java/com/ruoyi/mill/domain/MillProductionActual.java +++ b/ruoyi-mill/src/main/java/com/ruoyi/mill/domain/MillProductionActual.java @@ -126,18 +126,18 @@ public class MillProductionActual extends BaseEntity private String customer; /** 上线时间 */ - @JsonFormat(pattern = "yyyy-MM-dd") - @Excel(name = "上线时间", width = 30, dateFormat = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "上线时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") private Date onlineTime; /** 开始时间 */ - @JsonFormat(pattern = "yyyy-MM-dd") - @Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") private Date startTime; /** 结束时间 */ - @JsonFormat(pattern = "yyyy-MM-dd") - @Excel(name = "结束时间", width = 30, dateFormat = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "结束时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") private Date endTime; /** 机组号 */ diff --git a/ruoyi-mill/src/main/java/com/ruoyi/mill/mapper/MillProductionActualMapper.java b/ruoyi-mill/src/main/java/com/ruoyi/mill/mapper/MillProductionActualMapper.java index aee3aff1..4ab433ab 100644 --- a/ruoyi-mill/src/main/java/com/ruoyi/mill/mapper/MillProductionActualMapper.java +++ b/ruoyi-mill/src/main/java/com/ruoyi/mill/mapper/MillProductionActualMapper.java @@ -27,6 +27,14 @@ public interface MillProductionActualMapper */ public List selectMillProductionActualList(MillProductionActual millProductionActual); + /** + * 查询某计划是否已生成实绩 + * + * @param planId 计划ID + * @return 数量 + */ + public int countByPlanId(Long planId); + /** * 新增轧线生产实绩 * diff --git a/ruoyi-mill/src/main/java/com/ruoyi/mill/service/impl/MillProductionPlanServiceImpl.java b/ruoyi-mill/src/main/java/com/ruoyi/mill/service/impl/MillProductionPlanServiceImpl.java index eba94c7a..5e5903cf 100644 --- a/ruoyi-mill/src/main/java/com/ruoyi/mill/service/impl/MillProductionPlanServiceImpl.java +++ b/ruoyi-mill/src/main/java/com/ruoyi/mill/service/impl/MillProductionPlanServiceImpl.java @@ -96,28 +96,32 @@ public class MillProductionPlanServiceImpl implements IMillProductionPlanService throw new RuntimeException("计划不存在: " + planId); } - // 2. 更新计划状态为完成(2) - plan.setProdStatus("Done"); - plan.setUpdateBy(SecurityUtils.getUsername()); - int updateResult = planMapper.update(plan); - if (updateResult <= 0) { - throw new RuntimeException("更新计划状态失败"); + // 2. 更新计划状态为完成(幂等) + if (!"Done".equals(plan.getProdStatus())) { + plan.setProdStatus("Done"); + plan.setUpdateBy(SecurityUtils.getUsername()); + int updateResult = planMapper.update(plan); + if (updateResult <= 0) { + throw new RuntimeException("更新计划状态失败"); + } } - // 3. 创建生产实绩记录 - MillProductionActual actual = new MillProductionActual(); - actual.setPlanId(plan.getPlanId()); - actual.setPlanNo(plan.getPlanNo()); - actual.setEntryMatId(plan.getInMatNo()); // 来料卷号 = 生产计划钢卷号 - actual.setSteelGrade(plan.getAlloyNo()); // 钢种 - actual.setStatus("已完成"); // 实绩状态:已完成 - actual.setEndTime(new Date()); // 结束时间 - actual.setCreateBy(SecurityUtils.getUsername()); - actual.setDelFlag("0"); // 正常状态 + // 3. 创建生产实绩记录(幂等) + if (actualMapper.countByPlanId(planId) <= 0) { + MillProductionActual actual = new MillProductionActual(); + actual.setPlanId(plan.getPlanId()); + actual.setPlanNo(plan.getPlanNo()); + actual.setEntryMatId(plan.getInMatNo()); + actual.setSteelGrade(plan.getAlloyNo()); + actual.setStatus("已完成"); + actual.setEndTime(new Date()); + actual.setCreateBy(SecurityUtils.getUsername()); + actual.setDelFlag("0"); - int actualResult = actualMapper.insertMillProductionActual(actual); - if (actualResult <= 0) { - throw new RuntimeException("创建生产实绩记录失败"); + int actualResult = actualMapper.insertMillProductionActual(actual); + if (actualResult <= 0) { + throw new RuntimeException("创建生产实绩记录失败"); + } } return 1; diff --git a/ruoyi-mill/src/main/resources/mapper/mill/MillProductionActualMapper.xml b/ruoyi-mill/src/main/resources/mapper/mill/MillProductionActualMapper.xml index 84c33c94..1174cb22 100644 --- a/ruoyi-mill/src/main/resources/mapper/mill/MillProductionActualMapper.xml +++ b/ruoyi-mill/src/main/resources/mapper/mill/MillProductionActualMapper.xml @@ -101,6 +101,10 @@ where actual_id = #{actualId} + + insert into mill_production_actual diff --git a/ruoyi-mill/src/main/resources/mapper/mill/MillProductionPlanMapper.xml b/ruoyi-mill/src/main/resources/mapper/mill/MillProductionPlanMapper.xml index 51411cd9..edf31395 100644 --- a/ruoyi-mill/src/main/resources/mapper/mill/MillProductionPlanMapper.xml +++ b/ruoyi-mill/src/main/resources/mapper/mill/MillProductionPlanMapper.xml @@ -48,6 +48,9 @@ AND plan_status = #{planStatus} + + AND prod_status != 'Done' + AND DATE(create_time) >= #{params.beginTime} diff --git a/ruoyi-ui/src/assets/styles/sidebar.scss b/ruoyi-ui/src/assets/styles/sidebar.scss index f7a0f0ea..64f6edb3 100644 --- a/ruoyi-ui/src/assets/styles/sidebar.scss +++ b/ruoyi-ui/src/assets/styles/sidebar.scss @@ -89,9 +89,9 @@ } .el-menu-item.is-active { - background-color: #1d4e89 !important; + background-color: rgba(93, 173, 226, 0.24) !important; color: #ffffff !important; - border-left: 3px solid #5dade2; + border-left: 3px solid #85c1e9; } & .nest-menu .el-submenu>.el-submenu__title, diff --git a/ruoyi-ui/src/layout/components/Sidebar/Logo.vue b/ruoyi-ui/src/layout/components/Sidebar/Logo.vue index 05df4c75..59f5bd53 100644 --- a/ruoyi-ui/src/layout/components/Sidebar/Logo.vue +++ b/ruoyi-ui/src/layout/components/Sidebar/Logo.vue @@ -74,8 +74,10 @@ export default { & .sidebar-logo-inner { display: flex; align-items: center; - justify-content: center; + justify-content: flex-start; gap: 10px; + width: 100%; + padding: 0 12px; } & .sidebar-logo { @@ -87,19 +89,20 @@ export default { } & .sidebar-title { - display: inline-block; + display: block; margin: 0; color: #fff; font-weight: 700; - line-height: 56px; - font-size: 15px; + line-height: 18px; + font-size: 13px; font-family: Avenir, Helvetica Neue, Arial, 'PingFang SC', 'Microsoft YaHei', sans-serif; vertical-align: middle; - letter-spacing: 0.5px; - white-space: nowrap; + letter-spacing: 0.2px; + white-space: normal; + word-break: break-all; overflow: hidden; - text-overflow: ellipsis; - max-width: 140px; + max-height: 36px; + text-align: left; } } diff --git a/ruoyi-ui/src/layout/components/TagsView/index.vue b/ruoyi-ui/src/layout/components/TagsView/index.vue index 44eff7d4..5db28902 100644 --- a/ruoyi-ui/src/layout/components/TagsView/index.vue +++ b/ruoyi-ui/src/layout/components/TagsView/index.vue @@ -1,328 +1,329 @@ - - - - - - - + + + + + + + diff --git a/ruoyi-ui/src/views/mill/pdo.vue b/ruoyi-ui/src/views/mill/pdo.vue index 2ec8f5cb..9f92cb68 100644 --- a/ruoyi-ui/src/views/mill/pdo.vue +++ b/ruoyi-ui/src/views/mill/pdo.vue @@ -96,7 +96,7 @@ - + @@ -108,9 +108,9 @@ - + @@ -476,6 +476,9 @@ export default { }, form: {}, rules: { + exitMatId: [{ required: true, message: '请输入成品卷号', trigger: 'blur' }], + entryMatId: [{ required: true, message: '请输入来料卷号', trigger: 'blur' }], + planNo: [{ required: true, message: '请输入计划号', trigger: 'blur' }], } }; }, @@ -486,9 +489,17 @@ export default { getList() { this.loading = true; listActual(this.queryParams).then(response => { - this.actualList = response.rows; + this.actualList = response.rows || []; this.total = response.total; this.loading = false; + this.ids = []; + this.single = true; + this.multiple = true; + if (this.$refs.actualTable) this.$refs.actualTable.clearSelection(); + if (this.currentRow && this.currentRow.actualId != null) { + const updated = this.actualList.find(r => r.actualId === this.currentRow.actualId); + this.currentRow = updated || null; + } }); }, cancel() { @@ -549,6 +560,11 @@ export default { }, resetQuery() { this.resetForm("queryForm"); + this.currentRow = null; + this.ids = []; + this.single = true; + this.multiple = true; + if (this.$refs.actualTable) this.$refs.actualTable.clearSelection(); this.handleQuery(); }, handleSelectionChange(selection) { @@ -558,6 +574,10 @@ export default { }, handleRowClick(row) { this.currentRow = row; + if (this.$refs.actualTable) { + this.$refs.actualTable.clearSelection(); + this.$refs.actualTable.toggleRowSelection(row, true); + } }, handleAdd() { this.reset(); @@ -600,6 +620,8 @@ export default { this.$modal.confirm('是否确认删除轧线生产实绩编号为"' + actualIds + '"的数据项?').then(function() { return delActual(actualIds); }).then(() => { + const deleted = String(actualIds).split(',').map(v => Number(v)); + if (this.currentRow && deleted.includes(Number(this.currentRow.actualId))) this.currentRow = null; this.getList(); this.$modal.msgSuccess("删除成功"); }).catch(() => {}); diff --git a/ruoyi-ui/src/views/mill/plan.vue b/ruoyi-ui/src/views/mill/plan.vue index 936aa796..590adadd 100644 --- a/ruoyi-ui/src/views/mill/plan.vue +++ b/ruoyi-ui/src/views/mill/plan.vue @@ -173,7 +173,7 @@
钢卷增加 修改 - 完成 + 完成 删除
@@ -404,11 +404,24 @@ export default { }, methods: { refreshRecipeOptions() { - listAllRecipe({}).then(res => { this.recipeOptions = res.data || [] }) + listAllRecipe({}).then(res => { + const all = res.data || [] + const keepIds = new Set() + if (this.selectedPlan && this.selectedPlan.recipeId) keepIds.add(this.selectedPlan.recipeId) + if (this.form && this.form.recipeId) keepIds.add(this.form.recipeId) + if (this.selectRecipeId) keepIds.add(this.selectRecipeId) + + const visible = all.filter(r => !String(r.recipeNo || '').startsWith('__')) + all.forEach(r => { + if (keepIds.has(r.recipeId) && !visible.some(x => x.recipeId === r.recipeId)) visible.push(r) + }) + this.recipeOptions = visible + }) }, handleFinish() { - const planId = this.selectedPlan.planId + const planId = this.selectedPlan && this.selectedPlan.planId if (!planId) return this.$message.warning('请先选择钢卷') + if (this.selectedPlan.prodStatus === 'Done') return this.$message.info('该计划已完成') finishPlan(planId).then(res => { if (res.code === 200) { this.$message.success('完成成功') @@ -419,12 +432,13 @@ export default { }) }, loadList() { - const params = { inMatNo: this.query.inMatNo } - if (this.query.hideFinished) params.Idle = 'Idle' + const params = { inMatNo: this.query.inMatNo, params: {} } + if (this.query.hideFinished) params.params.excludeDone = true if (this.query.dateRange && this.query.dateRange.length === 2) { - params.beginTime = this.query.dateRange[0] - params.endTime = this.query.dateRange[1] + params.params.beginTime = this.query.dateRange[0] + params.params.endTime = this.query.dateRange[1] } + if (!Object.keys(params.params).length) delete params.params listPlan(params).then(res => { this.planList = res.data || [] }) }, resetQuery() { diff --git a/ruoyi-ui/src/views/mill/process.vue b/ruoyi-ui/src/views/mill/process.vue index 44e524d8..5907a8bb 100644 --- a/ruoyi-ui/src/views/mill/process.vue +++ b/ruoyi-ui/src/views/mill/process.vue @@ -175,7 +175,7 @@ const emptyPass = (no) => ({ }) const emptyForm = () => ({ - id: null, recipeNo: '', alloyNo: '', passCount: 0, + recipeId: null, recipeNo: '', alloyNo: '', passCount: 0, inThick: '', outThick: '', outWidth: '', status: '0', remark: '' }) @@ -200,16 +200,29 @@ export default { mounted() { this.loadList() }, methods: { loadList() { - listRecipe({ recipeNo: this.searchKey, alloyNo: this.searchKey }).then(res => { - this.recipeList = res.rows || [] + return listRecipe({ recipeNo: this.searchKey, alloyNo: this.searchKey }).then(res => { + const list = res.rows || [] + this.recipeList = list + if (this.isNew) return + if (this.selectedId != null && !list.some(item => item.recipeId === this.selectedId)) { + this.clearSelection() + } }) }, + clearSelection() { + this.selectedId = null + this.isNew = false + this.form = emptyForm() + this.passList = [] + if (this.$refs.formRef) this.$refs.formRef.clearValidate() + }, handleSelect(r) { this.selectedId = r.recipeId this.isNew = false getRecipeDetail(r.recipeId).then(res => { this.form = { ...res.data } this.passList = res.data.passList || [] + if (this.$refs.formRef) this.$refs.formRef.clearValidate() }) }, handleAdd() { @@ -217,6 +230,7 @@ export default { this.selectedId = null this.form = emptyForm() this.passList = [] + if (this.$refs.formRef) this.$refs.formRef.clearValidate() }, handleSave() { this.$refs.formRef.validate(valid => { @@ -224,23 +238,27 @@ export default { this.form.passList = this.passList this.form.passCount = this.passList.length const api = this.isNew ? addRecipe : updateRecipe - api(this.form).then(() => { + api(this.form).then((res) => { this.$message.success('保存成功') - this.loadList() + const targetId = this.isNew ? res.data : this.form.recipeId this.isNew = false + this.loadList().then(() => { + if (targetId != null) this.handleSelect({ recipeId: targetId }) + else this.clearSelection() + }) }) }) }, handleReset() { if (this.isNew) { this.form = emptyForm(); this.passList = [] } - else this.handleSelect({ id: this.selectedId }) + else if (this.selectedId != null) this.handleSelect({ recipeId: this.selectedId }) + else this.clearSelection() }, handleDelete() { this.$confirm('确定删除该方案?', '提示', { type: 'warning' }).then(() => { delRecipe([this.form.recipeId]).then(() => { this.$message.success('删除成功') - this.form = emptyForm(); this.passList = [] - this.selectedId = null; this.isNew = false + this.clearSelection() this.loadList() }) })