feat(wms/coil): 新增钢卷加工链追溯功能并优化成本信息展示
1. 新增钢卷加工链双向追溯API接口getCoilChain 2. 替换原有的递归 lineage 构建逻辑,使用新接口获取完整加工链数据 3. 补充完善工序类型枚举映射,新增修复工序和退火工序 4. 重构加工路径图构建逻辑,使用接口返回的depth字段计算层级
This commit is contained in:
@@ -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'
|
||||
})
|
||||
}
|
||||
@@ -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 => {
|
||||
|
||||
Reference in New Issue
Block a user