diff --git a/klp-perf/src/main/java/com/klp/perf/controller/PerfDeptSummaryController.java b/klp-perf/src/main/java/com/klp/perf/controller/PerfDeptSummaryController.java index 609c40ea5..df9a916c0 100644 --- a/klp-perf/src/main/java/com/klp/perf/controller/PerfDeptSummaryController.java +++ b/klp-perf/src/main/java/com/klp/perf/controller/PerfDeptSummaryController.java @@ -85,6 +85,17 @@ public class PerfDeptSummaryController extends BaseController { return toAjax(iPerfDeptSummaryService.updateByBo(bo)); } + /** + * 保存车间月度汇总(按 deptId + period 自动新增或更新) + * 前端统计页使用:将当前页面计算/展示的汇总数据落库到 perf_dept_summary 表。 + */ + @Log(title = "车间月度汇总", businessType = BusinessType.OTHER) + @RepeatSubmit() + @PostMapping("/saveByPeriod") + public R saveByPeriod(@RequestBody PerfDeptSummaryBo bo) { + return toAjax(iPerfDeptSummaryService.saveByDeptPeriod(bo)); + } + /** * 删除车间月度汇总 * diff --git a/klp-perf/src/main/java/com/klp/perf/service/IPerfDeptSummaryService.java b/klp-perf/src/main/java/com/klp/perf/service/IPerfDeptSummaryService.java index 517e6e983..351a2ff4e 100644 --- a/klp-perf/src/main/java/com/klp/perf/service/IPerfDeptSummaryService.java +++ b/klp-perf/src/main/java/com/klp/perf/service/IPerfDeptSummaryService.java @@ -46,4 +46,9 @@ public interface IPerfDeptSummaryService { * 校验并批量删除车间月度汇总信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 按 deptId + period 保存汇总(存在则更新,不存在则新增) + */ + Boolean saveByDeptPeriod(PerfDeptSummaryBo bo); } diff --git a/klp-perf/src/main/java/com/klp/perf/service/impl/PerfDeptSummaryServiceImpl.java b/klp-perf/src/main/java/com/klp/perf/service/impl/PerfDeptSummaryServiceImpl.java index 2b0e9a243..49e4665b2 100644 --- a/klp-perf/src/main/java/com/klp/perf/service/impl/PerfDeptSummaryServiceImpl.java +++ b/klp-perf/src/main/java/com/klp/perf/service/impl/PerfDeptSummaryServiceImpl.java @@ -18,6 +18,7 @@ import com.klp.perf.service.IPerfDeptSummaryService; import java.util.List; import java.util.Map; import java.util.Collection; +import java.util.Objects; /** * 车间月度汇总Service业务层处理 @@ -121,4 +122,27 @@ public class PerfDeptSummaryServiceImpl implements IPerfDeptSummaryService { } return baseMapper.deleteBatchIds(ids) > 0; } + + /** + * 按 deptId + period 保存汇总(存在则更新,不存在则新增) + * 用于前端“统计页保存汇总”按钮:将页面计算出的数据落库到 perf_dept_summary 表。 + */ + @Override + public Boolean saveByDeptPeriod(PerfDeptSummaryBo bo) { + if (bo == null || bo.getDeptId() == null || StringUtils.isBlank(bo.getPeriod())) { + return false; + } + if (bo.getId() != null) { + return updateByBo(bo); + } + PerfDeptSummary existed = baseMapper.selectOne(Wrappers.lambdaQuery() + .eq(PerfDeptSummary::getDeptId, bo.getDeptId()) + .eq(PerfDeptSummary::getPeriod, bo.getPeriod()) + .last("limit 1")); + if (Objects.nonNull(existed)) { + bo.setId(existed.getId()); + return updateByBo(bo); + } + return insertByBo(bo); + } } diff --git a/klp-ui/src/api/perf/deptSummary.js b/klp-ui/src/api/perf/deptSummary.js index 8501ac1c0..406b3f268 100644 --- a/klp-ui/src/api/perf/deptSummary.js +++ b/klp-ui/src/api/perf/deptSummary.js @@ -35,6 +35,15 @@ export function updatePerfDeptSummary(data) { }) } +// 保存车间月度汇总(按 deptId + period 自动新增或更新) +export function savePerfDeptSummaryByPeriod(data) { + return request({ + url: '/perf/deptSummary/saveByPeriod', + method: 'post', + data: data + }) +} + // 删除车间月度汇总 export function delPerfDeptSummary(ids) { return request({ diff --git a/klp-ui/src/views/perf/index/PerfConfigPanel.vue b/klp-ui/src/views/perf/index/PerfConfigPanel.vue index e9382fe17..013e1367f 100644 --- a/klp-ui/src/views/perf/index/PerfConfigPanel.vue +++ b/klp-ui/src/views/perf/index/PerfConfigPanel.vue @@ -1,57 +1,92 @@ - +
点击 tab 查看维度,右键 tab 可编辑或删除模板
- +
-
+ @@ -179,7 +219,7 @@ -
+ @@ -234,7 +274,7 @@ -
+ @@ -262,7 +302,13 @@ export default { cfgLoading: false, cfgSubmitting: false, cfgReady: false, + // cfgId 存在表示该部门该月份已存在配置记录;为空表示首次配置(未落库) cfgId: null, + // 方案C:通过“快照对比”识别是否改动未保存 + cfgSnapshot: '', + cfgDirty: false, + // 后端回填表单时锁定快照对比,避免回填过程误判为“已修改未保存” + cfgSnapshotLock: false, cfgForm: { monthlyProductionTarget: '', productionUnit: '吨', perfUnitPrice: '', productionShifts: '', guaranteedShifts: '', deptCoeff: '', salaryMode: 0, baseDivisor: '', remark: '' }, cfgRules: { monthlyProductionTarget: [{ required: true, message: '请输入月产量目标', trigger: 'blur' }], @@ -315,13 +361,48 @@ export default { computed: { activeTpl() { return this.templateList.find(t => String(t.id) === this.activeTemplateId) || null + }, + cfgStatus() { + if (this.cfgDirty) return { type: 'danger', text: '已修改未保存' } + if (this.cfgId) return { type: 'success', text: '已保存' } + return { type: 'warning', text: '未保存(首次配置)' } + }, + cfgBtnText() { + return this.cfgId ? '保存修改' : '新增并保存' + }, + cfgBtnType() { + return this.cfgDirty ? 'danger' : 'primary' } }, watch: { deptId: { immediate: true, handler: 'loadAll' }, - period: 'loadAll' + period: 'loadAll', + cfgForm: { + deep: true, + handler() { + // 表单未就绪或正在回填时,不做“脏状态”判断 + if (!this.cfgReady) return + if (this.cfgSnapshotLock) return + this.cfgDirty = this.calcCfgSnapshot() !== this.cfgSnapshot + } + } }, methods: { + // 将考核参数表单按字段归一化,用于快照比对(避免 undefined/null 造成误判) + calcCfgSnapshot() { + const f = this.cfgForm || {} + return JSON.stringify({ + monthlyProductionTarget: f.monthlyProductionTarget ?? '', + productionUnit: f.productionUnit ?? '', + perfUnitPrice: f.perfUnitPrice ?? '', + productionShifts: f.productionShifts ?? '', + guaranteedShifts: f.guaranteedShifts ?? '', + deptCoeff: f.deptCoeff ?? '', + salaryMode: f.salaryMode ?? 0, + baseDivisor: f.baseDivisor ?? '', + remark: f.remark ?? '' + }) + }, loadAll() { if (!this.deptId) return this.loadDeptConfig() @@ -332,6 +413,8 @@ export default { loadDeptConfig() { this.cfgLoading = true this.cfgReady = false + this.cfgSnapshotLock = true + // 约定:后端按 month=YYYY-MM-01 查询,前端 period 为 YYYY-MM listPerfDeptConfig({ deptId: this.deptId, month: this.period + '-01' }).then(res => { const rows = res.rows || [] if (rows.length > 0) { @@ -353,13 +436,22 @@ export default { this.cfgForm = { monthlyProductionTarget: '', productionUnit: '吨', perfUnitPrice: '', productionShifts: '', guaranteedShifts: '', deptCoeff: '', salaryMode: 0, baseDivisor: '', remark: '' } } this.cfgReady = true - }).finally(() => { this.cfgLoading = false }) + this.$nextTick(() => { + // 表单回填完成后建立快照,并重置“脏状态” + this.cfgSnapshot = this.calcCfgSnapshot() + this.cfgDirty = false + this.cfgSnapshotLock = false + }) + }).finally(() => { + this.cfgLoading = false + }) }, handleSaveCfg() { this.$refs.cfgFormRef.validate(valid => { if (!valid) return this.cfgSubmitting = true const data = { ...this.cfgForm, deptId: this.deptId, month: this.period + '-01' } + // cfgId 存在走更新;不存在走新增(首次保存) const api = this.cfgId ? updatePerfDeptConfig : addPerfDeptConfig if (this.cfgId) data.id = this.cfgId api(data).then(() => { @@ -539,44 +631,59 @@ export default { overflow-y: auto; } -.config-section { - background-color: #fff; - margin-bottom: 12px; +.config-card { + border-radius: 0; + box-shadow: none; + border: 1px solid #ebeef5; + ::v-deep .el-card { + border-radius: 0; + } + ::v-deep .el-card__header { + padding: 10px 12px; + border-bottom: 1px solid #ebeef5; + } + ::v-deep .el-card__body { + padding: 12px; + } } -.section-header { +.card-header { display: flex; - justify-content: space-between; align-items: center; - padding: 10px 12px; - border-bottom: 1px solid #f0f0f0; + justify-content: space-between; } -.section-title { +.card-left { + display: flex; + align-items: center; + gap: 8px; +} + +.cfg-status { + line-height: 1; +} + +.card-title { font-size: 14px; font-weight: 600; color: #303133; } -.section-hint { +.card-hint { font-weight: 400; font-size: 12px; color: #c0c4cc; margin-left: 8px; } -.cfg-form { - padding: 12px; - - ::v-deep .el-form-item { - margin-bottom: 8px; - margin-right: 16px; - } +.card-body { + padding: 0; } -::v-deep .el-table { - border: none; - &::before, &::after { display: none; } +.cfg-form { + ::v-deep .el-form-item { + margin-bottom: 10px; + } } /* 自定义模板 tabs */ @@ -584,17 +691,17 @@ export default { display: flex; flex-wrap: wrap; gap: 6px; - padding: 8px 12px; + padding: 0 0 10px; border-bottom: 1px solid #ebeef5; } .template-tab { display: inline-block; - padding: 4px 14px; + padding: 4px 12px; font-size: 12px; color: #606266; background: #f4f4f5; - border-radius: 12px; + border-radius: 0; cursor: pointer; user-select: none; transition: all 0.15s; @@ -617,14 +724,14 @@ export default { /* 维度区域 */ .dim-section { - padding: 8px 0 0; + padding: 10px 0 0; } .dim-toolbar { display: flex; justify-content: space-between; align-items: center; - padding: 0 12px 8px; + padding: 0 0 8px; } .dim-subtitle { @@ -645,8 +752,8 @@ export default { position: fixed; z-index: 9999; background: #fff; - border: 1px solid #e4e7ed; - border-radius: 4px; + border: none; + border-radius: 0; box-shadow: 0 2px 12px rgba(0,0,0,.12); padding: 4px 0; min-width: 120px; @@ -666,4 +773,16 @@ export default { &:hover { background-color: #fef0f0; } } } + +.op-actions { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + white-space: nowrap; +} + +.op-actions ::v-deep .el-button--text { + padding: 0; +} diff --git a/klp-ui/src/views/perf/index/SalaryPanel.vue b/klp-ui/src/views/perf/index/SalaryPanel.vue index 06fd2bd8a..908bb2274 100644 --- a/klp-ui/src/views/perf/index/SalaryPanel.vue +++ b/klp-ui/src/views/perf/index/SalaryPanel.vue @@ -4,11 +4,12 @@
- + + 查询
@@ -22,11 +23,12 @@ - + +