feat(cost/comprehensive): 新增产线自动填充和数据一键获取功能
1. 新增产线名称转换方法,统一展示产线中文名 2. 为详情输入框添加自动获取数据按钮,支持原料、产出、辅料、轧辊四类数据自动查询 3. 改造报表表单的产线选择器,使用正式产线数据而非硬编码选项 4. 完善报表编辑时的产线数据映射逻辑 5. 新增自动加载状态管理,避免重复请求
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
<el-card class="mb8">
|
||||
<div slot="header" class="entry-header">
|
||||
<span class="entry-title">{{ activeReport.reportTitle }}</span>
|
||||
<el-tag size="mini" style="margin-left:6px">{{ activeReport.lineType==='acid'?'酸轧':'镀锌' }}</el-tag>
|
||||
<el-tag size="mini" style="margin-left:6px">{{ lineName(activeReport) }}</el-tag>
|
||||
<span class="entry-meta">{{ parseTime(activeReport.reportDate,'{y}-{m}-{d}') }} 投入{{ activeReport.inputWeight }}t 产出{{ activeReport.outputWeight }}t</span>
|
||||
<el-button type="primary" size="mini" style="float:right;margin-left:8px" @click="saveGrid" :loading="saving">保存</el-button>
|
||||
<el-button size="mini" style="float:right" @click="openColCfg">列配置</el-button>
|
||||
@@ -28,13 +28,15 @@
|
||||
<template v-for="col in allCols">
|
||||
<el-table-column v-if="col.$type==='detail' && !col.isShift" :key="'d'+col.itemId" :label="col.itemName+(col.unit?'('+col.unit+')':'')" width="105" align="center">
|
||||
<template slot-scope="s">
|
||||
<el-input v-model="s.row['q'+col.itemId]" size="mini" @input="recalcAll" />
|
||||
<el-input v-model="s.row['q'+col.itemId]" size="mini" @input="recalcAll">
|
||||
<i slot="suffix" v-if="col.queryCondition" :class="autoLoading[col.itemId]?'el-icon-loading':'el-icon-refresh'" style="cursor:pointer;font-size:13px;line-height:24px;color:#409eff" @click.stop="fetchAutoData(col, s.row)" />
|
||||
</el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-else-if="col.$type==='detail' && col.isShift" :key="'ds'+col.itemId" :label="col.itemName+(col.unit?'('+col.unit+')':'')" width="120" align="center">
|
||||
<template slot-scope="s">
|
||||
<div class="shift-cell"><span class="shift-tag">甲</span><el-input v-model="s.row['q'+col.itemId+'_1']" size="mini" class="shift-input" @input="recalcAll" /></div>
|
||||
<div class="shift-cell"><span class="shift-tag">乙</span><el-input v-model="s.row['q'+col.itemId+'_2']" size="mini" class="shift-input" @input="recalcAll" /></div>
|
||||
<div class="shift-cell"><span class="shift-tag">甲</span><el-input v-model="s.row['q'+col.itemId+'_1']" size="mini" class="shift-input" @input="recalcAll"><i slot="suffix" v-if="col.queryCondition" :class="autoLoading[col.itemId]?'el-icon-loading':'el-icon-refresh'" style="cursor:pointer;font-size:12px;line-height:24px;color:#409eff" @click.stop="fetchAutoData(col, s.row, '1')" /></el-input></div>
|
||||
<div class="shift-cell"><span class="shift-tag">乙</span><el-input v-model="s.row['q'+col.itemId+'_2']" size="mini" class="shift-input" @input="recalcAll"><i slot="suffix" v-if="col.queryCondition" :class="autoLoading[col.itemId]?'el-icon-loading':'el-icon-refresh'" style="cursor:pointer;font-size:12px;line-height:24px;color:#409eff" @click.stop="fetchAutoData(col, s.row, '2')" /></el-input></div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-else-if="col.$type==='metric' && !col.isShift" :key="'m'+col.mIdx" :label="col.metricName+(col.unit?'('+col.unit+')':'')" width="85" align="center">
|
||||
@@ -184,7 +186,7 @@
|
||||
<el-table :data="copyReports" border stripe size="mini" highlight-current-row @current-change="copySrc=$event">
|
||||
<el-table-column label="报表标题" prop="reportTitle" />
|
||||
<el-table-column label="日期" width="110"><template slot-scope="s">{{ parseTime(s.row.reportDate,'{y}-{m}-{d}') }}</template></el-table-column>
|
||||
<el-table-column label="产线" prop="lineType" width="70" />
|
||||
<el-table-column label="产线" width="70"><template slot-scope="s">{{ lineName(s.row) }}</template></el-table-column>
|
||||
</el-table>
|
||||
<div slot="footer">
|
||||
<el-button type="primary" :disabled="!copySrc" @click="doCopyCfg">确认复制</el-button>
|
||||
@@ -211,7 +213,7 @@
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="报表标题" prop="reportTitle" />
|
||||
<el-table-column label="日期" width="120"><template slot-scope="s">{{ parseTime(s.row.reportDate,'{y}-{m}-{d}') }}</template></el-table-column>
|
||||
<el-table-column label="产线" prop="lineType" width="70" />
|
||||
<el-table-column label="产线" width="70"><template slot-scope="s">{{ lineName(s.row) }}</template></el-table-column>
|
||||
<el-table-column label="投入(t)" prop="inputWeight" width="80" />
|
||||
<el-table-column label="产出(t)" prop="outputWeight" width="80" />
|
||||
</el-table>
|
||||
@@ -221,7 +223,7 @@
|
||||
<el-form ref="rpf" :model="rpForm" :rules="{reportTitle:[{required:true,message:'请输入',trigger:'blur'}]}" label-width="100px">
|
||||
<el-form-item label="报表标题" prop="reportTitle"><el-input v-model="rpForm.reportTitle" /></el-form-item>
|
||||
<el-form-item label="报表日期" prop="reportDate"><el-date-picker v-model="rpForm.reportDate" type="date" value-format="yyyy-MM-dd" style="width:100%" /></el-form-item>
|
||||
<el-form-item label="产线类型" prop="lineType"><el-select v-model="rpForm.lineType" style="width:100%"><el-option label="酸轧" value="acid" /><el-option label="镀锌" value="galvanized" /></el-select></el-form-item>
|
||||
<el-form-item label="产线" prop="lineType"><el-select v-model="rpForm.lineType" style="width:100%" placeholder="请选择产线"><el-option v-for="ln in lineOptions" :key="ln.lineId" :label="ln.lineName" :value="ln.lineId" /></el-select></el-form-item>
|
||||
<el-form-item label="投入量(吨)" prop="inputWeight"><el-input-number v-model="rpForm.inputWeight" :precision="2" :min="0" style="width:100%" /></el-form-item>
|
||||
<el-form-item label="产出量(吨)" prop="outputWeight"><el-input-number v-model="rpForm.outputWeight" :precision="2" :min="0" style="width:100%" /></el-form-item>
|
||||
<el-form-item label="备注" prop="remark"><el-input v-model="rpForm.remark" type="textarea" /></el-form-item>
|
||||
@@ -249,6 +251,89 @@ import { listProdReport, getProdReport, addProdReport, updateProdReport, delProd
|
||||
import { listProdDetail, batchSaveProdDetail } from "@/api/cost/prodDetail"
|
||||
import { listProdMetric, addProdMetric, updateProdMetric, delProdMetric, getProdMetric } from "@/api/cost/prodMetric"
|
||||
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 { listProductionLine } from "@/api/wms/productionLine"
|
||||
import { listRollGrindAll } from "@/api/mes/roll/rollGrind"
|
||||
|
||||
function parseDateRange(detailDate) {
|
||||
const d = (detailDate || '').slice(0, 10)
|
||||
return {
|
||||
startTime: d + ' 00:00:00',
|
||||
endTime: d + ' 23:59:59'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动查询处理器注册表。
|
||||
* key: item.category, value: async (queryCondition, row, col, report, shift) => fetchedValue
|
||||
*/
|
||||
const queryHandlers = {}
|
||||
export function registerQueryHandler(category, handler) {
|
||||
queryHandlers[category] = handler
|
||||
}
|
||||
|
||||
const teamMap = { '1': '甲', '2': '乙' }
|
||||
|
||||
registerQueryHandler('原料', async (queryCondition, row, col, report, shift) => {
|
||||
if (!row.detailDate) return null
|
||||
const { startTime, endTime } = parseDateRange(row.detailDate)
|
||||
const res = await listLightPendingAction({ actionStatus: 2, actionTypes: queryCondition, startTime, endTime, pageSize: 99999 })
|
||||
const items = Array.isArray(res.data) ? res.data : (res.rows || [])
|
||||
const ids = [...new Set(items.map(i => i.coilId).filter(Boolean))]
|
||||
if (!ids.length) return null
|
||||
const params = { coilIds: ids.join(',') }
|
||||
if (shift && teamMap[shift]) params.team = teamMap[shift]
|
||||
const stat = await getCoilStatisticsList(params)
|
||||
const net = stat.data && stat.data.total_net_weight
|
||||
return net != null ? net : null
|
||||
})
|
||||
|
||||
registerQueryHandler('产出', async (queryCondition, row, col, report, shift) => {
|
||||
if (!row.detailDate) return null
|
||||
const { startTime, endTime } = parseDateRange(row.detailDate)
|
||||
const res = await listLightPendingAction({ actionStatus: 2, actionTypes: queryCondition, startTime, endTime, pageSize: 99999 })
|
||||
const items = Array.isArray(res.data) ? res.data : (res.rows || [])
|
||||
const ids = []
|
||||
for (const i of items) {
|
||||
if (i.processedCoilIds) {
|
||||
i.processedCoilIds.split(',').forEach(id => { id = id.trim(); if (id) ids.push(id) })
|
||||
}
|
||||
}
|
||||
if (!ids.length) return null
|
||||
const params = { coilIds: [...new Set(ids)].join(',') }
|
||||
if (shift && teamMap[shift]) params.team = teamMap[shift]
|
||||
const stat = await getCoilStatisticsList(params)
|
||||
const net = stat.data && stat.data.total_net_weight
|
||||
return net != null ? net : null
|
||||
})
|
||||
|
||||
registerQueryHandler('辅料', async (queryCondition, row, col, report, shift) => {
|
||||
if (!row.detailDate) return null
|
||||
const d = (row.detailDate || '').slice(0, 10)
|
||||
const res = await listAuxiliaryConsume({ recordDate: d, typeId: queryCondition, pageSize: 9999 })
|
||||
const items = res.rows || []
|
||||
const total = items.reduce((s, item) => s + (parseFloat(item.consume) || 0), 0)
|
||||
if (col.isShift) {
|
||||
const half = total / 2
|
||||
return [half, half]
|
||||
}
|
||||
return total || null
|
||||
})
|
||||
|
||||
registerQueryHandler('轧辊', async (queryCondition, row, col, report, shift) => {
|
||||
if (!row.detailDate || !report.lineType) return null
|
||||
const d = (row.detailDate || '').slice(0, 10)
|
||||
const params = { lineId: report.lineType, beginTime: d + ' 00:00:00', endTime: d + ' 23:59:59', rollType: queryCondition }
|
||||
if (shift && teamMap[shift]) params.team = teamMap[shift] + '班'
|
||||
const res = await listRollGrindAll(params)
|
||||
const items = res.data || []
|
||||
if (!items.length) return null
|
||||
let total = 0
|
||||
items.forEach(item => { total += parseFloat(item.grindAmount) || 0 })
|
||||
return total || null
|
||||
})
|
||||
|
||||
export default {
|
||||
name: "CostComprehensive",
|
||||
@@ -265,7 +350,9 @@ export default {
|
||||
metricPickOpen: false, metricPickList: [], selMp: [],
|
||||
mgrOpen: false, mgrList: [], defOpen: false, defTitle: '', defForm: {},
|
||||
copyCfgOpen: false, copyReports: [], copySrc: null,
|
||||
configOpen: false
|
||||
configOpen: false,
|
||||
autoLoading: {},
|
||||
lineOptions: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -286,7 +373,7 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: { configOpen(v) { if (!v) this.rpOpen = false } },
|
||||
created() { this.getTabList(); this.loadItems() },
|
||||
created() { this.getTabList(); this.loadItems(); this.loadLines() },
|
||||
methods: {
|
||||
/* report */
|
||||
getList() { this.loading = true; listProdReport(this.q).then(r=>{this.list=r.rows;this.total=r.total}).finally(()=>this.loading=false) },
|
||||
@@ -294,12 +381,22 @@ export default {
|
||||
search() { this.q.pageNum = 1; this.getList() },
|
||||
resetQ() { this.resetForm("qf"); this.search() },
|
||||
addRp() { this.rpForm = {}; this.rpTitle = "新增"; this.rpOpen = true },
|
||||
editRp(row) { const id = (row&&row.reportId)||this.selIds[0]; if(!id)return; getProdReport(id).then(r=>{this.rpForm=r.data;this.rpTitle="修改";this.rpOpen=true}) },
|
||||
editRp(row) {
|
||||
const id = (row&&row.reportId)||this.selIds[0]; if(!id)return
|
||||
getProdReport(id).then(r=>{
|
||||
const d = r.data || {}
|
||||
if (d.lineType) {
|
||||
const match = this.lineOptions.find(l => l.lineName.includes(d.lineType === 'acid' ? '酸轧' : '镀锌'))
|
||||
if (match) d.lineId = match.lineId
|
||||
}
|
||||
this.rpForm = d; this.rpTitle = "修改"; this.rpOpen = true
|
||||
})
|
||||
},
|
||||
submitRp() {
|
||||
this.$refs.rpf.validate(v=>{if(!v)return;this.rpBtnLoading=true;const fn=this.rpForm.reportId?updateProdReport:addProdReport;fn(this.rpForm).then(()=>{
|
||||
this.$modal.msgSuccess("成功"); this.rpOpen = false
|
||||
if (this.activeReport && this.activeReport.reportId === this.rpForm.reportId) {
|
||||
Object.assign(this.activeReport, { reportTitle: this.rpForm.reportTitle, reportDate: this.rpForm.reportDate, lineType: this.rpForm.lineType, inputWeight: this.rpForm.inputWeight, outputWeight: this.rpForm.outputWeight })
|
||||
Object.assign(this.activeReport, { reportTitle: this.rpForm.reportTitle, reportDate: this.rpForm.reportDate, lineId: this.rpForm.lineId, inputWeight: this.rpForm.inputWeight, outputWeight: this.rpForm.outputWeight })
|
||||
}
|
||||
this.getTabList(); this.getList()
|
||||
}).finally(()=>this.rpBtnLoading=false)})
|
||||
@@ -356,7 +453,7 @@ export default {
|
||||
batchAddDetailCols() {
|
||||
this.selAdd.forEach(item => {
|
||||
if (!this.allCols.find(c => c.$type === 'detail' && String(c.itemId) === String(item.itemId)))
|
||||
this.allCols.push({ $type: 'detail', itemId: item.itemId, itemCode: item.itemCode, itemName: item.itemName, unit: item.unit, isShift: false, color: null })
|
||||
this.allCols.push({ $type: 'detail', itemId: item.itemId, itemCode: item.itemCode, itemName: item.itemName, unit: item.unit, isShift: false, color: null, queryCondition: item.queryCondition, category: item.category })
|
||||
})
|
||||
this.showAddDetail = false; this.selAdd = []
|
||||
},
|
||||
@@ -456,7 +553,7 @@ export default {
|
||||
if (c.t === 'd') {
|
||||
const id = String(c.id)
|
||||
const item = this.allItems.find(i => String(i.itemId) === id)
|
||||
if (item) cols.push({ $type: 'detail', itemId: item.itemId, itemCode: item.itemCode, itemName: item.itemName, unit: item.unit, isShift: !!c.s, color: c.c || null })
|
||||
if (item) cols.push({ $type: 'detail', itemId: item.itemId, itemCode: item.itemCode, itemName: item.itemName, unit: item.unit, isShift: !!c.s, color: c.c || null, queryCondition: item.queryCondition, category: item.category })
|
||||
} else if (c.t === 'm') {
|
||||
const id = String(c.id)
|
||||
let def = (this._allMetricDefs || []).find(m => String(m.metricId) === id)
|
||||
@@ -554,7 +651,7 @@ export default {
|
||||
cols.forEach(sc => {
|
||||
if (sc.t === 'd') {
|
||||
const sid = String(sc.id)
|
||||
if (!usedIds.has(sid)) { const item = this.allItems.find(i=>String(i.itemId)===sid); if (item) { this.allCols.push({ $type:'detail', itemId:item.itemId, itemCode:item.itemCode, itemName:item.itemName, unit:item.unit, isShift:!!sc.s, color:sc.c||null }); usedIds.add(sid) } }
|
||||
if (!usedIds.has(sid)) { const item = this.allItems.find(i=>String(i.itemId)===sid); if (item) { this.allCols.push({ $type:'detail', itemId:item.itemId, itemCode:item.itemCode, itemName:item.itemName, unit:item.unit, isShift:!!sc.s, color:sc.c||null, queryCondition:item.queryCondition, category:item.category }); usedIds.add(sid) } }
|
||||
}
|
||||
else if (sc.t === 'm') {
|
||||
const sid = String(sc.id)
|
||||
@@ -566,7 +663,41 @@ export default {
|
||||
this.$modal.msgSuccess('配置已复用')
|
||||
},
|
||||
|
||||
/* auto fetch */
|
||||
async fetchAutoData(col, row, shift) {
|
||||
if (!col.queryCondition || this.autoLoading[col.itemId]) return
|
||||
const handler = queryHandlers[col.category] || queryHandlers['default']
|
||||
if (!handler) { this.$modal.msgWarning(`类别 "${col.category}" 未注册查询处理器`); return }
|
||||
this.$set(this.autoLoading, col.itemId, true)
|
||||
try {
|
||||
const val = await handler(col.queryCondition, row, col, this.activeReport, shift)
|
||||
if (val != null) {
|
||||
const round3 = n => Math.round(n * 1000) / 1000
|
||||
if (Array.isArray(val)) {
|
||||
this.$set(row, 'q' + col.itemId + '_1', round3(val[0]))
|
||||
this.$set(row, 'q' + col.itemId + '_2', round3(val[1]))
|
||||
} else {
|
||||
const key = 'q' + col.itemId + (shift ? '_' + shift : '')
|
||||
this.$set(row, key, round3(val))
|
||||
}
|
||||
this.recalcAll()
|
||||
}
|
||||
} catch (e) {
|
||||
this.$modal.msgError('自动获取数据失败')
|
||||
} finally {
|
||||
this.$set(this.autoLoading, col.itemId, false)
|
||||
}
|
||||
},
|
||||
|
||||
/* helpers */
|
||||
async loadLines() { const r = await listProductionLine({ pageSize: 999 }); this.lineOptions = r.rows || [] },
|
||||
lineName(row) {
|
||||
if (row.lineType) {
|
||||
const found = this.lineOptions.find(l => l.lineId == row.lineType);
|
||||
if (found) return found.lineName
|
||||
else return row.lineType || '-'
|
||||
}
|
||||
},
|
||||
async loadItems() { if (!this.allItems.length) { const r = await listItem({ pageNum:1, pageSize:999 }); this.allItems = r.rows || [] } },
|
||||
async loadAllMetrics(rid) {
|
||||
const q = { pageNum:1, pageSize:99999 }; if (rid) q.reportId = rid
|
||||
|
||||
Reference in New Issue
Block a user