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