feat(wms/coil): 新增钢卷加工链追溯功能并优化成本信息展示

1. 新增钢卷加工链双向追溯API接口getCoilChain
2. 替换原有的递归 lineage 构建逻辑,使用新接口获取完整加工链数据
3. 补充完善工序类型枚举映射,新增修复工序和退火工序
4. 重构加工路径图构建逻辑,使用接口返回的depth字段计算层级
This commit is contained in:
2026-06-18 09:31:32 +08:00
parent 585017873c
commit 41716b3430
2 changed files with 63 additions and 57 deletions

View File

@@ -538,4 +538,12 @@ export function listForPeriodComparison(data) {
timeout: 600000,
data: data
})
}
// 钢卷加工链追溯查询(双向:向上到根节点,向下到所有后代)
export function getCoilChain(coilId) {
return request({
url: '/wms/materialCoil/chain/all/' + coilId,
method: 'get'
})
}

View File

@@ -60,7 +60,7 @@
<script>
import * as echarts from 'echarts'
import { listPendingAction } from '@/api/wms/pendingAction'
import { getMaterialCoil } from '@/api/wms/coil'
import { getCoilChain } from '@/api/wms/coil'
import { listProdReport } from '@/api/cost/prodReport'
import { listProdMetric } from '@/api/cost/prodMetric'
import { listProdMetricResult } from '@/api/cost/prodMetricResult'
@@ -74,16 +74,29 @@ const PROD_LINE_NAME = { 1: '镀锌产线', 2: '酸轧产线' }
const ACTION_TYPE_MAP = {
11: '酸连轧工序',
120: '酸轧分条',
201: '酸轧合卷',
202: '镀锌合卷',
203: '脱脂合卷',
204: '拉矫平整合卷',
205: '双机架合卷',
206: '镀铬合卷',
501: '镀锌工序',
502: '脱脂工序',
503: '拉矫平整工序',
504: '双机架工序',
506: '纵剪分条工序'
505: '镀铬工序',
506: '纵剪分条工序',
520: '酸轧修复工序',
521: '镀锌修复工序',
522: '脱脂修复工序',
523: '拉矫修复工序',
524: '双机架修复工序',
525: '镀铬修复工序',
600: '退火工序'
}
export default {
@@ -135,17 +148,23 @@ export default {
this.pathLoading = true
this.pathChecked = false
try {
const lineage = await this.buildCoilLineage(coilId, new Set(), 0)
if (lineage.length <= 1) {
const res = await getCoilChain(coilId)
const chainData = res.data || {}
const traceList = chainData.traceList || []
if (traceList.length <= 1) {
this.pathGraphData = { nodes: [], links: [] }
return
}
const coilMap = {}
lineage.forEach(c => { coilMap[String(c.coilId)] = c })
const allCoilIds = lineage.map(c => c.coilId).filter(Boolean)
const allCoilIds = []
traceList.forEach(node => {
const coil = node.coil
if (coil && coil.coilId) {
allCoilIds.push(coil.coilId)
}
})
// 用每个卷的 coilId 查询完整的 pendingAction批量并发
const actionResults = await Promise.allSettled(
allCoilIds.map(id => listPendingAction({
coilId: id,
@@ -171,7 +190,7 @@ export default {
console.log('pendingAction 样例:', sampleAction)
}
this.pathGraphData = this.buildGraphData(lineage, coilId, Object.values(actionMap), coilMap)
this.pathGraphData = this.buildGraphData(traceList, coilId, Object.values(actionMap))
await this.loadCostData()
} catch (e) {
console.error('获取加工路径失败:', e)
@@ -184,33 +203,11 @@ export default {
})
}
},
async buildCoilLineage(coilId, visited, depth) {
if (!coilId || visited.has(String(coilId)) || depth > 20) return []
visited.add(String(coilId))
const res = await getMaterialCoil(coilId)
const coil = res.data || {}
if (!coil.coilId) return []
const parents = []
if (coil.parentCoilId) {
const parentIds = String(coil.parentCoilId).split(',').map(id => id.trim()).filter(Boolean)
for (const pid of parentIds) {
if (!visited.has(pid)) {
const pLineage = await this.buildCoilLineage(pid, visited, depth + 1)
parents.push(...pLineage)
}
}
}
return [...parents, coil]
},
buildGraphData(lineage, currentCoilId, actions, coilMap) {
buildGraphData(traceList, currentCoilId, actions) {
const nodes = []
const links = []
const nodeIdxMap = {}
// 建立 outputCoilIds → action 的索引,兼容多种后端字段名
const actionByOutput = {}
const actionByInput = {}
function getOutputIds(a) {
@@ -225,41 +222,41 @@ export default {
if (tid) actionByOutput[tid] = a
})
}
// 同时建立输入侧索引作为兜底
if (a.coilId) {
const key = String(a.coilId)
if (!actionByInput[key]) actionByInput[key] = a
}
})
// 计算层级
const levels = {}
lineage.forEach(coil => {
if (!coil.parentCoilId) {
levels[String(coil.coilId)] = 0
} else {
const parentIds = String(coil.parentCoilId).split(',').map(id => id.trim()).filter(Boolean)
const maxParentLevel = Math.max(...parentIds.map(pid => levels[pid] || 0))
levels[String(coil.coilId)] = maxParentLevel + 1
}
})
// 使用 traceList 自带的 depth归一化使根节点 level=0
const depths = traceList.map(n => n.depth != null ? n.depth : 0)
const minDepth = Math.min(...depths)
const levelGroups = {}
lineage.forEach(coil => {
const lv = levels[String(coil.coilId)]
traceList.forEach(node => {
const coil = node.coil
if (!coil || !coil.coilId) return
const lv = (node.depth != null ? node.depth : 0) - minDepth
if (!levelGroups[lv]) levelGroups[lv] = []
levelGroups[lv].push(coil)
})
// 构建节点
lineage.forEach(coil => {
const isRoot = !coil.parentCoilId
const levelIndexMap = {}
Object.keys(levelGroups).forEach(lv => {
levelGroups[lv].forEach((coil, idx) => {
levelIndexMap[String(coil.coilId)] = idx
})
})
traceList.forEach(node => {
const coil = node.coil
if (!coil || !coil.coilId) return
const isRoot = !node.parentCoilId
const isCurrent = String(coil.coilId) === String(currentCoilId)
const category = isCurrent ? 'current' : (isRoot ? 'root' : 'intermediate')
const lv = levels[String(coil.coilId)]
const lv = (node.depth != null ? node.depth : 0) - minDepth
const siblings = levelGroups[lv] || []
const sibIdx = siblings.findIndex(c => String(c.coilId) === String(coil.coilId))
const totalInLevel = siblings.length
nodeIdxMap[String(coil.coilId)] = nodes.length
@@ -270,7 +267,7 @@ export default {
id: String(coil.coilId),
category,
level: lv,
levelIndex: sibIdx,
levelIndex: levelIndexMap[String(coil.coilId)] || 0,
levelTotal: totalInLevel,
symbolSize: 65,
coilNo: coil.currentCoilNo,
@@ -295,7 +292,6 @@ export default {
})
})
// 确保节点 name 唯一ECharts 以 name 标识节点,重名会合并,导致 dataIndex 错误)
const nameCount = {}
nodes.forEach(n => {
nameCount[n.name] = (nameCount[n.name] || 0) + 1
@@ -308,12 +304,14 @@ export default {
}
})
// 构建边
lineage.forEach(coil => {
if (!coil.parentCoilId) return
traceList.forEach(node => {
const coil = node.coil
if (!coil || !coil.coilId) return
const childIdx = nodeIdxMap[String(coil.coilId)]
const parentIds = String(coil.parentCoilId).split(',').map(id => id.trim()).filter(Boolean)
const parentIdStr = node.parentCoilId || (coil.parentCoilId)
if (!parentIdStr) return
const parentIds = String(parentIdStr).split(',').map(id => id.trim()).filter(Boolean)
const action = actionByOutput[String(coil.coilId)] || {}
parentIds.forEach(pid => {