diff --git a/klp-ui/src/views/cost/comprehensive.vue b/klp-ui/src/views/cost/comprehensive.vue index 1d5f83ce..7f3d7b2d 100644 --- a/klp-ui/src/views/cost/comprehensive.vue +++ b/klp-ui/src/views/cost/comprehensive.vue @@ -22,28 +22,38 @@ 保存 列配置 价格管理 +反填 {{ inputMode ? '录入' : '查看' }} - - + + - + + + {{ col.itemName }}{{ col.unit ? '('+col.unit+')' : '' }} + - + + + + - + + + {{ col.itemName }}{{ col.unit ? '('+col.unit+')' : '' }} + - 甲 - 乙 + 甲 + 乙 @@ -56,8 +66,13 @@ - - 删除 + + + + 删除 + 添加日期行 @@ -273,6 +288,23 @@ + + + + + + + + 等待中 + 执行中 + 成功 + 失败 + + + + + 关 闭 + @@ -285,9 +317,10 @@ import { listProdMetric, addProdMetric, updateProdMetric, delProdMetric, getProd import { listItem } from "@/api/cost/item" import { listLightPendingAction } from "@/api/wms/pendingAction" import { getCoilStatisticsList } from "@/api/wms/coil" -import { listAuxiliaryConsume } from "@/api/eqp/auxiliaryConsume" +import { listAuxiliaryConsume, addAuxiliaryConsume, updateAuxiliaryConsume } from "@/api/eqp/auxiliaryConsume" import { listProductionLine } from "@/api/wms/productionLine" import { listRollGrindAll } from "@/api/mes/roll/rollGrind" +import { listEnergyRecord, addEnergyRecord, updateEnergyRecord } from "@/api/ems/energyRecord" function parseDateRange(detailDate) { const d = (detailDate || '').slice(0, 10) @@ -306,6 +339,11 @@ export function registerQueryHandler(category, handler) { queryHandlers[category] = handler } +const backfillHandlers = {} +export function registerBackfillHandler(category, handler) { + backfillHandlers[category] = handler +} + const teamMap = { '1': '甲', '2': '乙' } registerQueryHandler('原料', async (queryCondition, row, col, report, shift) => { @@ -367,6 +405,30 @@ registerQueryHandler('轧辊', async (queryCondition, row, col, report, shift) = return total || null }) +registerBackfillHandler('辅料', async (queryCondition, row, col, report, shift, value) => { + if (!row.detailDate) return + const d = row.detailDate.slice(0, 10) + const res = await listAuxiliaryConsume({ recordDate: d, typeId: queryCondition, pageSize: 9999 }) + const items = res.rows || [] + if (items.length > 0) { + await updateAuxiliaryConsume({ consumeId: items[0].consumeId, consume: value }) + } else { + await addAuxiliaryConsume({ recordDate: d, typeId: queryCondition, consume: value }) + } +}) + +registerBackfillHandler('能耗', async (queryCondition, row, col, report, shift, value) => { + if (!row.detailDate) return + const d = row.detailDate.slice(0, 10) + const res = await listEnergyRecord({ recordDate: d, meterId: queryCondition, pageSize: 9999 }) + const items = res.rows || [] + if (items.length > 0) { + await updateEnergyRecord({ energyRecordId: items[0].energyRecordId, consumption: value }) + } else { + await addEnergyRecord({ recordDate: d, meterId: queryCondition, consumption: value }) + } +}) + export default { name: "CostComprehensive", data() { @@ -383,12 +445,14 @@ export default { mgrOpen: false, mgrList: [], defOpen: false, defTitle: '', defForm: {}, copyCfgOpen: false, copyReports: [], copySrc: null, configOpen: false, - autoLoading: {}, + autoLoading: {}, backfillLoading: {}, lineOptions: [], lineType: null, noLineType: false, inputMode: false, - priceOpen: false, priceList: [], priceSaving: false + priceOpen: false, priceList: [], priceSaving: false, + backfilling: false, + progressOpen: false, progressTitle: '', progressTasks: [] } }, computed: { @@ -774,6 +838,180 @@ export default { } }, + async backfillCost() { + const detailCols = this.allCols.filter(c => c.$type === 'detail' && (c.category === '辅料' || c.category === '能耗') && c.queryCondition) + if (!detailCols.length) { this.$modal.msgWarning('无可用反填列(仅辅料/能耗)'); return } + this.backfilling = true + try { + let count = 0 + for (const row of this.gridRows) { + if (!row.detailDate) continue + for (const col of detailCols) { + const handler = backfillHandlers[col.category] + if (!handler) continue + if (col.isShift) { + const v1 = row['q' + col.itemId + '_1'] + if (v1 != null && v1 !== '') { await handler(col.queryCondition, row, col, this.activeReport, '1', v1); count++ } + const v2 = row['q' + col.itemId + '_2'] + if (v2 != null && v2 !== '') { await handler(col.queryCondition, row, col, this.activeReport, '2', v2); count++ } + } else { + const v = row['q' + col.itemId] + if (v != null && v !== '') { await handler(col.queryCondition, row, col, this.activeReport, null, v); count++ } + } + } + } + this.$modal.msgSuccess(`反填完成,共处理${count}条`) + } catch (e) { + this.$modal.msgError('反填失败') + } finally { + this.backfilling = false + } + }, + + async backfillCell(col, row, shift) { + if (!col.queryCondition || this.backfillLoading[col.itemId]) return + const handler = backfillHandlers[col.category] + if (!handler) { this.$modal.msgWarning(`类别 "${col.category}" 未注册反填处理器`); return } + const val = shift ? row['q' + col.itemId + '_' + shift] : row['q' + col.itemId] + if (val == null || val === '') { this.$modal.msgWarning('当前单元格无数据'); return } + this.$set(this.backfillLoading, col.itemId, true) + try { + await handler(col.queryCondition, row, col, this.activeReport, shift || null, val) + this.$modal.msgSuccess('反填成功') + } catch (e) { + this.$modal.msgError('反填失败') + } finally { + this.$set(this.backfillLoading, col.itemId, false) + } + }, + + async runBatchTasks(title, tasks) { + this.progressTitle = title + this.progressTasks = tasks.map(t => ({ label: t.label, status: 'pending', error: '', run: t.run })) + this.progressOpen = true + await this.$nextTick() + for (const task of this.progressTasks) { + if (!this.progressOpen) break + task.status = 'running' + try { + await task.run() + task.status = 'success' + } catch (e) { + task.status = 'fail' + task.error = e.message || '执行失败' + } + } + }, + + async batchFetchCol(col) { + if (!col.queryCondition) return + const handler = queryHandlers[col.category] || queryHandlers['default'] + if (!handler) { this.$modal.msgWarning(`类别 "${col.category}" 未注册查询处理器`); return } + const round3 = n => Math.round(n * 1000) / 1000 + const tasks = [] + for (const row of this.gridRows) { + if (!row.detailDate) continue + tasks.push({ + label: `${row.detailDate} ${col.itemName}`, + run: async () => { + const val = await handler(col.queryCondition, row, col, this.activeReport) + if (val != null) { + if (col.isShift && Array.isArray(val)) { + this.$set(row, 'q' + col.itemId + '_1', round3(val[0])) + this.$set(row, 'q' + col.itemId + '_2', round3(val[1])) + } else { + this.$set(row, 'q' + col.itemId, round3(val)) + } + } + } + }) + } + if (!tasks.length) { this.$modal.msgWarning('无可用行'); return } + this.$set(this.autoLoading, col.itemId, true) + await this.runBatchTasks(`批量抓取 - ${col.itemName}`, tasks) + this.$set(this.autoLoading, col.itemId, false) + this.recalcAll() + }, + + async batchBackfillCol(col) { + if (!col.queryCondition) return + const handler = backfillHandlers[col.category] + if (!handler) { this.$modal.msgWarning(`类别 "${col.category}" 未注册反填处理器`); return } + const tasks = [] + for (const row of this.gridRows) { + if (!row.detailDate) continue + if (col.isShift) { + const v1 = row['q' + col.itemId + '_1'] + if (v1 != null && v1 !== '') { + tasks.push({ label: `${row.detailDate} ${col.itemName}(甲)`, run: () => handler(col.queryCondition, row, col, this.activeReport, '1', v1) }) + } + const v2 = row['q' + col.itemId + '_2'] + if (v2 != null && v2 !== '') { + tasks.push({ label: `${row.detailDate} ${col.itemName}(乙)`, run: () => handler(col.queryCondition, row, col, this.activeReport, '2', v2) }) + } + } else { + const v = row['q' + col.itemId] + if (v != null && v !== '') { + tasks.push({ label: `${row.detailDate} ${col.itemName}`, run: () => handler(col.queryCondition, row, col, this.activeReport, null, v) }) + } + } + } + if (!tasks.length) { this.$modal.msgWarning('无可用数据'); return } + this.$set(this.backfillLoading, col.itemId, true) + await this.runBatchTasks(`批量反填 - ${col.itemName}`, tasks) + this.$set(this.backfillLoading, col.itemId, false) + }, + + async saveRow(row) { + if (!row.detailDate) return + const rid = this.activeReport.reportId; if (!rid) return + try { + const exist = await listProdDetail({ reportId: rid, pageNum: 1, pageSize: 9999 }) + const dateStr = row.detailDate + const removeIds = (exist.rows || []).filter(d => d.detailDate === dateStr).map(d => d.detailId) + const detailCols = this.allCols.filter(c => c.$type === 'detail') + const detailList = [] + detailCols.forEach(col => { + const push = (shift, sfx) => { const qty = row['q' + col.itemId + sfx]; if (qty != null && qty !== '') detailList.push({ reportId: rid, detailDate: dateStr, shift: shift || '0', itemId: col.itemId, quantity: qty }) } + if (col.isShift) { push('1', '_1'); push('2', '_2') } else push(null, '') + }) + await batchSaveProdDetail({ detailIds: removeIds, prodDetailList: detailList }) + this.$modal.msgSuccess('行保存成功') + } catch (e) { + this.$modal.msgError('行保存失败') + } + }, + + async fetchRow(row) { + if (!row.detailDate) return + const cols = this.allCols.filter(c => c.$type === 'detail' && c.queryCondition) + for (const col of cols) { + if (col.isShift) { await this.fetchAutoData(col, row, '1'); await this.fetchAutoData(col, row, '2') } + else { await this.fetchAutoData(col, row) } + } + this.$modal.msgSuccess('行抓取完成') + }, + + async backfillRow(row) { + if (!row.detailDate) return + const cols = this.allCols.filter(c => c.$type === 'detail' && (c.category === '辅料' || c.category === '能耗') && c.queryCondition) + if (!cols.length) { this.$modal.msgWarning('无可用反填列'); return } + for (const col of cols) { + const handler = backfillHandlers[col.category] + if (!handler) continue + if (col.isShift) { + const v1 = row['q' + col.itemId + '_1'] + if (v1 != null && v1 !== '') { await handler(col.queryCondition, row, col, this.activeReport, '1', v1) } + const v2 = row['q' + col.itemId + '_2'] + if (v2 != null && v2 !== '') { await handler(col.queryCondition, row, col, this.activeReport, '2', v2) } + } else { + const v = row['q' + col.itemId] + if (v != null && v !== '') { await handler(col.queryCondition, row, col, this.activeReport, null, v) } + } + } + this.$modal.msgSuccess('行反填完成') + }, + /* helpers */ async loadLines() { const r = await listProductionLine({ pageSize: 999 }); this.lineOptions = r.rows || [] }, lineName(row) { @@ -828,4 +1066,12 @@ export default { .drag-handle { cursor: grab; font-size: 14px; color: #909399; padding: 2px; display: inline-flex; align-items: center; } .drag-handle:active { cursor: grabbing; } .drag-handle:hover { color: #409eff; } +.col-hd { font-size: 12px; line-height: 1.3; } +.col-hd-actions { display: flex; align-items: center; gap: 4px; margin-top: 1px; font-size: 10px; } +.col-hd-actions .el-link { font-size: 10px; } +.input-suffix-actions { display: inline-flex; align-items: center; gap: 1px; } +.ica { cursor: pointer; font-size: 13px; line-height: 24px; } +.ica-fetch { color: #409eff; } +.ica-backfill { color: #67c23a; margin-right: 1px; } +.ica:hover { opacity: 0.7; }