feat(cost+wms): 新增生产指标标签功能并优化钢卷成本展示

1. 为生产指标实体、BO、VO新增tags标签字段并完善MyBatis映射
2. 在生产指标查询中添加标签模糊筛选条件
3. 新增生产指标计算结果API接口
4. 优化成本综合页面:支持标签字段的增改查,新增指标结果批量保存逻辑
5. 移除废弃的costDataService文件,重构钢卷详情页成本展示模块,新增加工路径可视化和吨钢成本计算展示
6. 注释并禁用了原有的检验任务相关代码逻辑
This commit is contained in:
2026-06-17 15:09:08 +08:00
parent 3719416cbf
commit 768b18c22a
11 changed files with 748 additions and 471 deletions

View File

@@ -53,6 +53,10 @@ public class CostProdMetric extends BaseEntity {
* 备注
*/
private String remark;
/**
* 标签
*/
private String tags;
/**
* 删除标识 0=正常 2=删除
*/

View File

@@ -58,5 +58,9 @@ public class CostProdMetricBo extends BaseEntity {
*/
private String remark;
/**
* 标签
*/
private String tags;
}

View File

@@ -9,6 +9,7 @@ import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
/**
* 生产指标计算结果业务对象 cost_prod_metric_result
@@ -39,6 +40,8 @@ public class CostProdMetricResultBo extends BaseEntity {
/**
* 计算日期
*/
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date metricDate;
/**

View File

@@ -69,5 +69,10 @@ public class CostProdMetricVo {
@ExcelProperty(value = "备注")
private String remark;
/**
* 标签
*/
@ExcelProperty(value = "标签")
private String tags;
}

View File

@@ -67,6 +67,7 @@ public class CostProdMetricServiceImpl implements ICostProdMetricService {
lqw.eq(StringUtils.isNotBlank(bo.getMetricFormula()), CostProdMetric::getMetricFormula, bo.getMetricFormula());
lqw.eq(bo.getMetricValue() != null, CostProdMetric::getMetricValue, bo.getMetricValue());
lqw.eq(bo.getUsePrice() != null, CostProdMetric::getUsePrice, bo.getUsePrice());
lqw.like(StringUtils.isNotBlank(bo.getTags()), CostProdMetric::getTags, bo.getTags());
return lqw;
}

View File

@@ -13,6 +13,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="metricValue" column="metric_value"/>
<result property="usePrice" column="use_price"/>
<result property="remark" column="remark"/>
<result property="tags" column="tags"/>
<result property="delFlag" column="del_flag"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>

View File

@@ -0,0 +1,53 @@
import request from '@/utils/request'
// 查询生产指标计算结果列表
export function listProdMetricResult(query) {
return request({
url: '/cost/prodMetricResult/list',
method: 'get',
params: query
})
}
// 查询生产指标计算结果详细
export function getProdMetricResult(resultId) {
return request({
url: '/cost/prodMetricResult/' + resultId,
method: 'get'
})
}
// 新增生产指标计算结果
export function addProdMetricResult(data) {
return request({
url: '/cost/prodMetricResult',
method: 'post',
data: data
})
}
// 修改生产指标计算结果
export function updateProdMetricResult(data) {
return request({
url: '/cost/prodMetricResult',
method: 'put',
data: data
})
}
// 删除生产指标计算结果
export function delProdMetricResult(resultId) {
return request({
url: '/cost/prodMetricResult/' + resultId,
method: 'delete'
})
}
// 批量保存计算结果(先删后插)
export function batchSaveProdMetricResult(data) {
return request({
url: '/cost/prodMetricResult/batch',
method: 'post',
data: data
})
}

View File

@@ -180,6 +180,7 @@
<el-table :data="mgrList" border stripe size="mini">
<el-table-column label="名称" prop="metricName" width="150" />
<el-table-column label="公式" prop="metricFormula" min-width="200" />
<el-table-column label="标签" prop="tags" width="100" />
<el-table-column label="单位" prop="remark" width="70" />
<el-table-column label="操作" width="100">
<template slot-scope="s">
@@ -206,6 +207,7 @@
</template>
</div>
<el-form-item label="单位"><el-input v-model="defForm.unit" placeholder="如 %" /></el-form-item>
<el-form-item label="标签"><el-input v-model="defForm.tags" placeholder="请输入标签" /></el-form-item>
<el-form-item label="使用单价"><el-switch v-model="defForm.usePrice" :active-value="1" :inactive-value="0" /></el-form-item>
<el-form-item label="单价"><el-input v-model="defForm.metricValue" type="number" placeholder="请输入单价" /></el-form-item>
</el-form>
@@ -324,6 +326,7 @@
import { listProdReport, getProdReport, addProdReport, updateProdReport, delProdReport, copyProdReport } from "@/api/cost/prodReport"
import { listProdDetail, batchSaveProdDetail } from "@/api/cost/prodDetail"
import { listProdMetric, addProdMetric, updateProdMetric, delProdMetric, getProdMetric } from "@/api/cost/prodMetric"
import { listProdMetricResult, batchSaveProdMetricResult } from "@/api/cost/prodMetricResult"
import { listItem } from "@/api/cost/item"
import { listLightPendingAction } from "@/api/wms/pendingAction"
import { getCoilStatisticsList } from "@/api/wms/coil"
@@ -608,7 +611,7 @@ export default {
},
doPickMetric() {
this.selMp.forEach(m => {
this.allCols.push({ $type: 'metric', metricId: m.metricId, metricName: m.metricName, metricFormula: m.metricFormula, unit: m.remark||'', isShift: false, color: null, metricValue: m.metricValue, usePrice: m.usePrice })
this.allCols.push({ $type: 'metric', metricId: m.metricId, metricName: m.metricName, metricFormula: m.metricFormula, unit: m.remark||'', tags: m.tags || '', isShift: false, color: null, metricValue: m.metricValue, usePrice: m.usePrice })
})
this.metricPickOpen = false
},
@@ -618,15 +621,15 @@ export default {
this.mgrList = this._allMetricDefs || []
this.mgrOpen = true
},
addMetricDef() { this.defForm = { metricId: null, metricName: '', metricFormula: '', unit: '', usePrice: 0, metricValue: '' }; this.defTitle = '新增指标'; this.defOpen = true },
editMetricDef(row) { this.defForm = { metricId: row.metricId, metricName: row.metricName, metricFormula: row.metricFormula, unit: row.remark||'', usePrice: row.usePrice || 0, metricValue: row.metricValue || '' }; this.defTitle = '编辑指标'; this.defOpen = true },
addMetricDef() { this.defForm = { metricId: null, metricName: '', metricFormula: '', unit: '', tags: '', usePrice: 0, metricValue: '' }; this.defTitle = '新增指标'; this.defOpen = true },
editMetricDef(row) { this.defForm = { metricId: row.metricId, metricName: row.metricName, metricFormula: row.metricFormula, unit: row.remark||'', tags: row.tags || '', usePrice: row.usePrice || 0, metricValue: row.metricValue || '' }; this.defTitle = '编辑指标'; this.defOpen = true },
async submitMetricDef() {
const f = this.defForm
if (!f.metricName) { this.$modal.msgWarning('请输入指标名称'); return }
if (f.metricId) {
await updateProdMetric({ metricId: f.metricId, metricName: f.metricName, metricFormula: f.metricFormula, remark: f.unit, usePrice: f.usePrice, metricValue: f.metricValue || 0 })
await updateProdMetric({ metricId: f.metricId, metricName: f.metricName, metricFormula: f.metricFormula, remark: f.unit, tags: f.tags || '', usePrice: f.usePrice, metricValue: f.metricValue || 0 })
} else {
await addProdMetric({ reportId: this.activeReport.reportId, metricCode: f.metricName, metricName: f.metricName, metricFormula: f.metricFormula, metricValue: f.metricValue || 0, remark: f.unit || '', usePrice: f.usePrice })
await addProdMetric({ reportId: this.activeReport.reportId, metricCode: f.metricName, metricName: f.metricName, metricFormula: f.metricFormula, metricValue: f.metricValue || 0, remark: f.unit || '', tags: f.tags || '', usePrice: f.usePrice })
}
this.defOpen = false; this.$modal.msgSuccess('保存成功')
await this.openMetricMgr()
@@ -676,7 +679,7 @@ export default {
// ensure metric definitions exist in DB for all metric columns before saving config
for (const mc of metricCols) {
if (!mc.metricId) {
const r = await addProdMetric({ reportId: rid, metricCode: mc.metricName, metricName: mc.metricName, metricFormula: mc.metricFormula || '', metricValue: 0, remark: mc.unit || '' })
const r = await addProdMetric({ reportId: rid, metricCode: mc.metricName, metricName: mc.metricName, metricFormula: mc.metricFormula || '', metricValue: 0, remark: mc.unit || '', tags: mc.tags || '' })
mc.metricId = r.data && r.data.metricId || r.metricId
}
}
@@ -721,7 +724,7 @@ export default {
if (!def && c.id) {
try { const r = await getProdMetric(c.id); if (r.data) { def = r.data; this._allMetricDefs.push(def) } } catch(e) {}
}
if (def) cols.push({ $type: 'metric', metricId: def.metricId, metricName: def.metricName, metricFormula: def.metricFormula, unit: def.remark||'', isShift: !!c.s, color: c.c || null, metricValue: def.metricValue, usePrice: def.usePrice })
if (def) cols.push({ $type: 'metric', metricId: def.metricId, metricName: def.metricName, metricFormula: def.metricFormula, unit: def.remark||'', tags: def.tags || '', isShift: !!c.s, color: c.c || null, metricValue: def.metricValue, usePrice: def.usePrice })
}
}
this.allCols = cols
@@ -782,6 +785,7 @@ export default {
async saveGrid() {
const rid = this.activeReport.reportId; if (!rid) return; this.saving = true
try {
this.recalcAll()
const exist = await listProdDetail({ reportId: rid, pageNum: 1, pageSize: 9999 })
const ids = (exist.rows||[]).map(d => d.detailId)
const detailCols = this.allCols.filter(c => c.$type === 'detail'); const detailList = []
@@ -793,6 +797,31 @@ export default {
})
})
await batchSaveProdDetail({ detailIds: ids, prodDetailList: detailList })
const metricCols = this.allCols.filter(c => c.$type === 'metric' && c.metricId)
if (metricCols.length) {
const existMR = await listProdMetricResult({ reportId: rid, pageNum: 1, pageSize: 99999 })
const resultIds = (existMR.rows || []).map(r => r.resultId)
const metricResultList = []
this.gridRows.forEach(row => {
if (!row.detailDate) return
metricCols.forEach(m => {
const push = (teamGroup, value) => {
if (value != null && value !== '') {
metricResultList.push({ reportId: rid, metricId: m.metricId, metricDate: row.detailDate, teamGroup, calcValue: value, tags: m.tags || '' })
}
}
if (m.isShift) {
push('1', row['mv' + m.mIdx + '_1'])
push('2', row['mv' + m.mIdx + '_2'])
} else {
push('0', row['mv' + m.mIdx])
}
})
})
await batchSaveProdMetricResult({ resultIds, prodMetricResultList: metricResultList })
}
this.$modal.msgSuccess("保存成功"); await this.loadGrid()
} catch(e) { this.$modal.msgError("保存失败") } finally { this.saving = false }
},
@@ -815,7 +844,7 @@ export default {
else if (sc.t === 'm') {
const sid = String(sc.id)
let def = this._allMetricDefs.find(m=>String(m.metricId)===sid)
if (def && !usedMids.has(sid)) { usedMids.add(sid); this.allCols.push({ $type:'metric', metricId:String(def.metricId), metricName:def.metricName, metricFormula:def.metricFormula, unit:def.remark||'', isShift:!!sc.s, color:sc.c||null, metricValue: def.metricValue, usePrice: def.usePrice }) }
if (def && !usedMids.has(sid)) { usedMids.add(sid); this.allCols.push({ $type:'metric', metricId:String(def.metricId), metricName:def.metricName, metricFormula:def.metricFormula, unit:def.remark||'', tags: def.tags || '', isShift:!!sc.s, color:sc.c||null, metricValue: def.metricValue, usePrice: def.usePrice }) }
}
})
this.copyCfgOpen = false; let mi = 0; this.allCols.forEach(c => { if (c.$type === 'metric') c.mIdx = mi++ })

View File

@@ -1,344 +0,0 @@
import { listProdReport } from '@/api/cost/prodReport'
import { listProdDetail } from '@/api/cost/prodDetail'
import { listProdMetric } from '@/api/cost/prodMetric'
import { listItem } from '@/api/cost/item'
import { listPrice } from '@/api/cost/price'
/**
* 安全计算公式值
* @param {string} f - 公式表达式
* @returns {number|null}
*/
function evalFormula(f) {
const s = f.replace(/[^0-9+\-*/.()\s]/g, '')
if (!s) return null
try {
const r = new Function('return (' + s + ')')()
return isFinite(r) ? Math.round(r * 10000) / 10000 : null
} catch (e) {
return null
}
}
/**
* 将明细数据按日期分组,并计算指标公式,生成完整行数据
*
* @param {Object} report - 生产月报
* @param {Array} details - 生产成本明细列表
* @param {Array} metrics - 生产指标列表
* @param {Array} items - 成本项目配置列表
* @returns {Array<Object>} data - 完整行数据数组
*/
function buildComputedRows(report, details, metrics, items) {
const itemCodeMap = {} // itemId -> itemCode
items.forEach(item => {
itemCodeMap[item.itemId] = item.itemCode
})
const esc = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
// 1. 按 detailDate 分组明细
const dateMap = {}
details.forEach(d => {
if (!d.detailDate) return
if (!dateMap[d.detailDate]) {
dateMap[d.detailDate] = { detailDate: d.detailDate }
}
const code = itemCodeMap[d.itemId]
const sfx = d.shift && d.shift !== '0' ? '_' + d.shift : ''
if (code) {
dateMap[d.detailDate]['q_' + code + sfx] = parseFloat(d.quantity) || 0
}
})
const rows = Object.values(dateMap).sort((a, b) => a.detailDate.localeCompare(b.detailDate))
// 2. 补齐每个 itemCode 的总量(跨班次汇总)
items.forEach(item => {
const code = item.itemCode
if (!code) return
rows.forEach(row => {
const v1 = row['q_' + code + '_1'] || 0
const v2 = row['q_' + code + '_2'] || 0
if (v1 || v2) {
row['q_' + code] = v1 + v2
}
})
})
// 3. 逐行计算指标公式
const rp = report || {}
rows.forEach(row => {
metrics.forEach((m, mi) => {
if (!m.metricFormula) return
let f = m.metricFormula
// 替换其他指标的引用(按定义顺序,前面的指标可被后面的引用)
for (let pmi = 0; pmi < mi; pmi++) {
const pm = metrics[pmi]
if (!pm.metricName) continue
const pn = esc(pm.metricName)
const pv = row['mv_' + pm.metricName]
if (pv != null) {
f = f.replace(new RegExp('@\\{' + pn + '\\}', 'g'), pv)
}
}
// 替换明细项引用 @{itemCode}
items.forEach(item => {
if (!item.itemCode) return
const code = esc(item.itemCode)
const totalVal = parseFloat(row['q_' + item.itemCode]) || 0
const val1 = parseFloat(row['q_' + item.itemCode + '_1']) || 0
const val2 = parseFloat(row['q_' + item.itemCode + '_2']) || 0
f = f.replace(new RegExp('@\\{' + code + '\\}\\.甲班', 'g'), val1)
f = f.replace(new RegExp('@\\{' + code + '\\}\\.乙班', 'g'), val2)
f = f.replace(new RegExp('@\\{' + code + '\\}', 'g'), totalVal)
})
// 替换系统变量
f = f.replace(/input_weight/g, rp.inputWeight || 0)
.replace(/output_weight/g, rp.outputWeight || 0)
.replace(/price/g, (m.usePrice === 1) ? (m.metricValue || 0) : 0)
const result = evalFormula(f)
if (result != null) {
row['mv_' + m.metricName] = result
}
})
})
return rows
}
/**
* 获取指定日期和产线类型的全部成本数据
*
* @param {string} date - 日期,格式 yyyy-MM-dd如 "2024-01-15"
* @param {string} lineType - 产线类型,"1"=镀锌,"2"=酸轧
* @returns {Promise<{
* report: Object|null, // 生产月报
* metrics: Array, // 生产指标明细列表(公式列)
* details: Array, // 生产成本明细列表(原始数据)
* items: Array, // 成本项目配置列表(原始数据列定义)
* data: Array, // 完整行数据:按日期分组,包含原始值(q_xxx)和计算值(mv_xxx)
* prices: Array, // 成本单价历史列表
* detailMap: Object, // 按 itemId 分组的明细
* itemMap: Object, // 按 itemId 索引的项目配置
* metricMap: Object // 按 metricId 索引的指标
* }>}
*/
export async function getCostDataByDate(date, lineType) {
// 1. 查找指定日期和产线类型的生产月报
// 假设每个日期只有一条月报,所以取第一个, 需要将日期转为第一天的日期
const firstDay = new Date(date)
firstDay.setDate(1)
console.log(firstDay)
// 使用北京时间,转换为东八区时间
const year = firstDay.getFullYear()
const month = (firstDay.getMonth() + 1).toString().padStart(2, '0')
const firstDayStr = year + '-' + month + '-01 00:00:00'
const reportRes = await listProdReport({
reportDate: firstDayStr,
lineType: lineType,
pageNum: 1,
pageSize: 1
})
console.log(reportRes)
const reports = reportRes.rows || []
const report = reports.length > 0 ? reports[0] : null
// 2. 获取所有成本项目配置(用于映射 itemId
const itemRes = await listItem({ pageNum: 1, pageSize: 9999 })
const items = itemRes.rows || []
const itemMap = {}
items.forEach(item => {
itemMap[item.itemId] = item
})
// 3. 获取该日期下生效的单价历史
const priceRes = await listPrice({
effectiveDate: date,
pageNum: 1,
pageSize: 9999
})
const prices = priceRes.rows || []
// 如果没有找到月报,返回基础数据
if (!report) {
return {
report: null,
metrics: [],
details: [],
items,
data: [],
prices,
detailMap: {},
itemMap,
metricMap: {}
}
}
const reportId = report.reportId
// 4. 获取该月报的所有生产成本明细
const detailRes = await listProdDetail({
reportId,
pageNum: 1,
pageSize: 9999
})
const details = detailRes.rows || []
// 5. 构建按 itemId 分组的明细
const detailMap = {}
details.forEach(detail => {
const key = detail.itemId
if (!detailMap[key]) {
detailMap[key] = []
}
detailMap[key].push(detail)
})
// 6. 获取该月报的所有生产指标
const metricRes = await listProdMetric({
reportId,
pageNum: 1,
pageSize: 9999
})
const metrics = metricRes.rows || []
const metricMap = {}
metrics.forEach(metric => {
metricMap[metric.metricId] = metric
})
// 7. 构建完整行数据(原始值 + 公式计算值)
const data = buildComputedRows(report, details, metrics, items)
return {
report,
metrics,
details,
items,
data,
prices,
detailMap,
itemMap,
metricMap
}
}
/**
* 获取指定日期范围、产线类型的成本数据,支持多天批量查询
*
* @param {string} startDate - 开始日期 yyyy-MM-dd
* @param {string} endDate - 结束日期 yyyy-MM-dd
* @param {string} lineType - 产线类型
* @returns {Promise<Array<{ date: string, report, metrics, details, items, data, prices }>>}
*/
export async function getCostDataByDateRange(startDate, endDate, lineType) {
// 查询日期范围内的所有月报
const reportRes = await listProdReport({
lineType,
pageNum: 1,
pageSize: 9999
})
const allReports = (reportRes.rows || []).filter(r => {
return r.reportDate >= startDate && r.reportDate <= endDate
})
// 获取所有项目配置和单价(共享数据,只查一次)
const itemRes = await listItem({ pageNum: 1, pageSize: 9999 })
const items = itemRes.rows || []
const itemMap = {}
items.forEach(item => {
itemMap[item.itemId] = item
})
const priceRes = await listPrice({
pageNum: 1,
pageSize: 9999
})
const prices = priceRes.rows || []
// 逐日报表获取完整数据
const results = []
for (const report of allReports) {
const reportId = report.reportId
const [detailRes, metricRes] = await Promise.all([
listProdDetail({ reportId, pageNum: 1, pageSize: 9999 }),
listProdMetric({ reportId, pageNum: 1, pageSize: 9999 })
])
const details = detailRes.rows || []
const metrics = metricRes.rows || []
const detailMap = {}
details.forEach(detail => {
const key = detail.itemId
if (!detailMap[key]) detailMap[key] = []
detailMap[key].push(detail)
})
const metricMap = {}
metrics.forEach(metric => {
metricMap[metric.metricId] = metric
})
const data = buildComputedRows(report, details, metrics, items)
results.push({
date: report.reportDate,
report,
metrics,
details,
items,
data,
prices,
detailMap,
itemMap,
metricMap
})
}
return results
}
/**
* 根据 itemId 获取指定日期下该成本项目的明细数据
*
* @param {string} date - 日期 yyyy-MM-dd
* @param {string} lineType - 产线类型
* @param {string|number} itemId - 成本项目ID
* @returns {Promise<{ quantity: number|null, unitPrice: number|null, amount: number|null }>}
*/
export async function getCostItemData(date, lineType, itemId) {
const { details, items } = await getCostDataByDate(date, lineType)
const itemDetails = details.filter(d => String(d.itemId) === String(itemId))
const item = items.find(i => String(i.itemId) === String(itemId))
let totalQuantity = 0
itemDetails.forEach(d => {
totalQuantity += parseFloat(d.quantity) || 0
})
return {
itemId,
itemName: item ? item.itemName : '',
itemCode: item ? item.itemCode : '',
category: item ? item.category : '',
unit: item ? item.unit : '',
quantity: totalQuantity || null,
unitPrice: itemDetails.length > 0 ? itemDetails[0].unitPrice : null,
amount: itemDetails.length > 0 ? itemDetails[0].amount : null,
details: itemDetails
}
}
export default {
getCostDataByDate,
getCostDataByDateRange,
getCostItemData
}

View File

@@ -16,69 +16,74 @@
<el-descriptions-item label="钢卷净重">
<span>{{ coilInfo.netWeight || 0 }} t</span>
</el-descriptions-item>
<el-descriptions-item v-if="costReport" label="产线类型">
<el-tag size="mini">{{ lineTypeName }}</el-tag>
</el-descriptions-item>
<el-descriptions-item v-if="costReport" label="投入量">
<span>{{ costReport.inputWeight || 0 }} t</span>
</el-descriptions-item>
<el-descriptions-item v-if="costReport" label="产出量">
<span>{{ costReport.outputWeight || 0 }} t</span>
</el-descriptions-item>
<el-descriptions-item v-if="costReport" label="报表日期">
<span>{{ costReport.reportDate }}</span>
</el-descriptions-item>
<el-descriptions-item v-if="costReport" label="报表名称">
<span>{{ costReport.reportTitle }}</span>
</el-descriptions-item>
<el-descriptions-item v-if="totalCost != null" label="当班总成本">
<span class="cost-value">{{ totalCost }}</span>
<span class="cost-unit"> </span>
</el-descriptions-item>
</el-descriptions>
<!-- 成本明细表格 -->
<div v-if="costRows && costRows.length" class="cost-table-wrap">
<div class="cost-table-title">当日生产成本明细</div>
<el-table :data="costRows" border stripe size="mini" style="width:100%">
<el-table-column label="日期" prop="detailDate" width="100" align="center" />
<template v-for="item in costItems">
<el-table-column v-if="item.itemCode" :key="'i' + item.itemId"
:label="item.itemName + (item.unit ? '(' + item.unit + ')' : '')" width="110" align="center">
<div v-if="!pathChecked || pathGraphData.nodes.length > 1" class="cost-chart-wrap">
<div class="cost-table-title">加工路径</div>
<div v-if="processedCostTable.length" class="ton-cost-table-wrap">
<el-table
:data="processedCostTable"
border
size="small"
:header-cell-style="{ background: '#f5f7fa', color: '#303133', fontWeight: 600 }"
>
<el-table-column prop="actionName" label="工序" min-width="120" />
<el-table-column prop="lineName" label="产线" width="100" />
<el-table-column prop="coilNo" label="钢卷号" min-width="140" show-overflow-tooltip />
<el-table-column prop="costPerTon" label="吨钢成本(元/吨)" width="130" align="right">
<template slot-scope="s">
{{ formatVal(s.row['q_' + item.itemCode]) }}
<span :class="s.row.costPerTon ? 'cost-ton-val' : 'cost-ton-zero'">{{ s.row.costPerTon || '-' }}</span>
</template>
</el-table-column>
</template>
<template v-for="m in costMetrics">
<el-table-column v-if="m.metricName" :key="'m' + m.metricId" :label="m.metricName" width="100"
align="center">
<el-table-column prop="netWeight" label="净重(t)" width="100" align="right">
<template slot-scope="s">{{ s.row.netWeight }}</template>
</el-table-column>
<el-table-column prop="totalCost" label="总成本(元)" width="120" align="right">
<template slot-scope="s">
{{ formatVal(s.row['mv_' + m.metricName]) }}
<span class="cost-total-val">{{ s.row.costPerTon ? s.row.totalCost : '-' }}</span>
</template>
</el-table-column>
</template>
</el-table>
</el-table>
</div>
<div ref="pathChart" v-loading="pathLoading || costLoading" class="path-chart" />
</div>
<div v-else-if="!pathLoading && pathChecked" class="cost-chart-wrap">
<el-empty description="暂无加工路径数据" :image-size="60" />
</div>
</div>
</div>
</template>
<script>
import costDataService from '@/views/cost/costDataService'
import * as echarts from 'echarts'
import { listPendingAction } from '@/api/wms/pendingAction'
import { getMaterialCoil } from '@/api/wms/coil'
import { listProdReport } from '@/api/cost/prodReport'
import { listProdMetric } from '@/api/cost/prodMetric'
import { listProdMetricResult } from '@/api/cost/prodMetricResult'
/**
* 仓库ID -> 成本产线类型映射
* 酸连轧成品库 -> 2镀锌成品库 -> 1
*/
const WAREHOUSE_LINE_TYPE_MAP = {
'1988150099140866050': '2',
'1988150323162836993': '1'
const ACTION_TO_PROD_LINE = {
11: 2, 120: 2, 201: 2, 520: 2,
501: 1, 202: 1, 521: 1
}
const PROD_LINE_NAME = { 1: '镀锌产线', 2: '酸轧产线' }
const LINE_TYPE_NAME_MAP = {
'2': '酸',
'1': '镀锌'
const ACTION_TYPE_MAP = {
11: '酸连轧工序',
120: '酸轧分条',
201: '酸轧合卷',
202: '镀锌合卷',
203: '脱脂合卷',
204: '拉矫平整合卷',
205: '双机架合卷',
206: '镀铬合卷',
501: '镀锌工序',
502: '脱脂工序',
504: '双机架工序',
506: '纵剪分条工序'
}
export default {
@@ -91,71 +96,564 @@ export default {
},
data() {
return {
costData: null,
loadingCost: false
}
},
computed: {
lineType() {
return WAREHOUSE_LINE_TYPE_MAP[this.coilInfo.warehouseId] || null
},
lineTypeName() {
return this.lineType ? (LINE_TYPE_NAME_MAP[this.lineType] || this.lineType) : ''
},
costReport() {
return this.costData ? this.costData.report : null
},
costMetrics() {
return this.costData ? this.costData.metrics : []
},
costItems() {
if (!this.costData) return []
return this.costData.items.filter(item => this.costData.detailMap[item.itemId])
},
costRows() {
return this.costData ? this.costData.data : []
},
totalCost() {
if (!this.costRows || !this.costRows.length) return null
for (const row of this.costRows) {
for (const key of Object.keys(row)) {
if (key.startsWith('mv_') && row[key] != null) {
return row[key]
}
}
}
return null
pathLoading: false,
costLoading: false,
pathChecked: false,
chart: null,
pathGraphData: {
nodes: [],
links: []
},
processedCostTable: []
}
},
watch: {
'coilInfo.warehouseId': {
immediate: true,
handler() {
this.fetchCostData()
}
'coilInfo.coilId': {
handler(val) {
if (val) {
this.loadProcessingPath()
}
},
immediate: true
}
},
beforeDestroy() {
if (this.chart) {
this.chart.dispose()
this.chart = null
}
window.removeEventListener('resize', this.handleResize)
},
methods: {
formatVal(val) {
return val != null ? val : '-'
},
async fetchCostData() {
console.log(this.coilInfo.warehouseId)
console.log(this.lineType)
if (!this.lineType) return
const createTime = this.coilInfo.createTime
if (!createTime) return
const date = createTime.slice(0, 10)
this.loadingCost = true
async loadProcessingPath() {
const coilId = this.coilInfo.coilId
if (!coilId) return
this.pathLoading = true
this.pathChecked = false
try {
console.log(date, this.lineType, '获取成本数据')
this.costData = await costDataService.getCostDataByDate(date, this.lineType)
console.log(this.costData, '成本数据')
const lineage = await this.buildCoilLineage(coilId, new Set(), 0)
if (lineage.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)
// 用每个卷的 coilId 查询完整的 pendingAction批量并发
const actionResults = await Promise.allSettled(
allCoilIds.map(id => listPendingAction({
coilId: id,
actionStatus: 2,
pageSize: 99999
}))
)
const actionMap = {}
actionResults.forEach(r => {
if (r.status === 'fulfilled' && r.value?.rows) {
r.value.rows.forEach(a => {
if (a.actionId) {
actionMap[a.actionId] = a
}
})
}
})
if (Object.keys(actionMap).length) {
const sampleAction = Object.values(actionMap)[0]
console.log('pendingAction 字段列表:', Object.keys(sampleAction))
console.log('pendingAction 样例:', sampleAction)
}
this.pathGraphData = this.buildGraphData(lineage, coilId, Object.values(actionMap), coilMap)
await this.loadCostData()
} catch (e) {
console.error('获取成本数据失败:', e)
console.error('获取加工路径失败', e)
this.pathGraphData = { nodes: [], links: [] }
} finally {
this.loadingCost = false
this.pathLoading = false
this.pathChecked = true
this.$nextTick(() => {
this.initChart()
})
}
},
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) {
const nodes = []
const links = []
const nodeIdxMap = {}
// 建立 outputCoilIds → action 的索引,兼容多种后端字段名
const actionByOutput = {}
const actionByInput = {}
function getOutputIds(a) {
return a.processedCoilIds || a.newCoilIds || a.outputCoilIds ||
a.completedCoilIds || a.coilIds || ''
}
actions.forEach(a => {
const outIds = getOutputIds(a)
if (outIds) {
String(outIds).split(',').forEach(id => {
const tid = id.trim()
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
}
})
const levelGroups = {}
lineage.forEach(coil => {
const lv = levels[String(coil.coilId)]
if (!levelGroups[lv]) levelGroups[lv] = []
levelGroups[lv].push(coil)
})
// 构建节点
lineage.forEach(coil => {
const isRoot = !coil.parentCoilId
const isCurrent = String(coil.coilId) === String(currentCoilId)
const category = isCurrent ? 'current' : (isRoot ? 'root' : 'intermediate')
const lv = levels[String(coil.coilId)]
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
const nodeName = String(coil.currentCoilNo || coil.coilId || '')
nodes.push({
name: nodeName,
id: String(coil.coilId),
category,
level: lv,
levelIndex: sibIdx,
levelTotal: totalInLevel,
symbolSize: 65,
coilNo: coil.currentCoilNo,
coilInfo: {
enterCoilNo: coil.enterCoilNo,
currentCoilNo: coil.currentCoilNo,
supplierCoilNo: coil.supplierCoilNo,
itemName: coil.itemName,
specification: coil.specification,
material: coil.material,
netWeight: coil.netWeight,
grossWeight: coil.grossWeight,
warehouseName: coil.warehouseName,
actualWarehouseName: coil.actualWarehouseName,
manufacturer: coil.manufacturer,
zincLayer: coil.zincLayer,
qualityStatus: coil.qualityStatus,
surfaceTreatmentDesc: coil.surfaceTreatmentDesc,
createTime: coil.createTime
},
isCurrent
})
})
// 确保节点 name 唯一ECharts 以 name 标识节点,重名会合并,导致 dataIndex 错误)
const nameCount = {}
nodes.forEach(n => {
nameCount[n.name] = (nameCount[n.name] || 0) + 1
})
const nameUsed = {}
nodes.forEach(n => {
if (nameCount[n.name] > 1) {
nameUsed[n.name] = (nameUsed[n.name] || 0) + 1
n.name = `${n.name}(${nameUsed[n.name]})`
}
})
// 构建边
lineage.forEach(coil => {
if (!coil.parentCoilId) return
const childIdx = nodeIdxMap[String(coil.coilId)]
const parentIds = String(coil.parentCoilId).split(',').map(id => id.trim()).filter(Boolean)
const action = actionByOutput[String(coil.coilId)] || {}
parentIds.forEach(pid => {
const parentIdx = nodeIdxMap[pid]
if (parentIdx != null && childIdx != null) {
const edgeAction = Object.keys(action).length ? action
: (actionByInput[pid] || {})
const actionTypeVal = edgeAction.actionType != null ? edgeAction.actionType : null
const actionLabel = ACTION_TYPE_MAP[actionTypeVal] ||
(actionTypeVal != null ? `工序${actionTypeVal}` : '')
links.push({
source: parentIdx,
target: childIdx,
label: {
show: !!actionLabel,
formatter: actionLabel,
fontSize: 11,
color: '#606266'
},
actionInfo: {
actionId: edgeAction.actionId,
actionType: edgeAction.actionType,
actionTypeName: actionLabel,
processTime: edgeAction.processTime,
completeTime: edgeAction.completeTime,
createTime: edgeAction.createTime,
createBy: edgeAction.createBy,
priority: edgeAction.priority,
remark: edgeAction.remark
}
})
}
})
})
return { nodes, links }
},
async loadCostData() {
const { nodes, links } = this.pathGraphData
if (!links.length) return
this.costLoading = true
this.processedCostTable = []
const buildFirstDay = (dateStr) => {
if (!dateStr) return null
const d = new Date(dateStr)
if (isNaN(d.getTime())) return null
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-01`
}
const buildDate = (dateStr) => {
if (!dateStr) return null
const d = new Date(dateStr)
if (isNaN(d.getTime())) return null
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
}
const cache = {}
const costQueries = links.map(async(link, idx) => {
const actionType = link.actionInfo?.actionType
if (actionType == null) return
const prodLineId = ACTION_TO_PROD_LINE[actionType]
if (!prodLineId) return
const targetNode = nodes[link.target]
const createTime = targetNode?.coilInfo?.createTime
if (!createTime) return
const firstDay = buildFirstDay(createTime)
const prodDate = buildDate(createTime)
if (!firstDay || !prodDate) return
const cacheKey = `${prodLineId}_${firstDay}`
try {
let rpMetric = cache[cacheKey]
if (rpMetric === undefined) {
const rpRes = await listProdReport({ lineType: prodLineId, reportDate: firstDay, pageNum: 1, pageSize: 10 })
const reports = rpRes?.rows || []
if (reports.length !== 1) { cache[cacheKey] = null; return }
const reportId = reports[0].reportId
const mRes = await listProdMetric({ reportId, pageNum: 1, pageSize: 9999, tags: '吨钢成本' })
const metrics = (mRes?.rows || []).filter(m => (m.tags || '').includes('吨钢成本'))
if (metrics.length !== 1) { cache[cacheKey] = null; return }
rpMetric = { reportId, metricId: metrics[0].metricId }
cache[cacheKey] = rpMetric
}
if (!rpMetric) return
const rRes = await listProdMetricResult({
reportId: rpMetric.reportId,
metricId: rpMetric.metricId,
metricDate: prodDate,
pageNum: 1, pageSize: 10
})
const results = rRes?.rows || []
const costPerTon = results.length > 0 ? parseFloat(results[0].calcValue) || 0 : 0
link.costPerTon = costPerTon
link.lineName = PROD_LINE_NAME[prodLineId] || ''
const netWeight = parseFloat(targetNode.coilInfo.netWeight) || 0
this.processedCostTable.push({
idx,
actionName: link.actionInfo?.actionTypeName || link.label?.formatter || '',
lineName: link.lineName,
coilNo: targetNode.coilNo || targetNode.name || '',
costPerTon,
netWeight,
totalCost: Math.round(costPerTon * netWeight * 100) / 100
})
} catch (e) {
console.error('查询吨钢成本失败(actionType=' + actionType + '):', e)
}
})
await Promise.allSettled(costQueries)
this.costLoading = false
},
initChart() {
if (this.chart) {
this.chart.dispose()
this.chart = null
}
const dom = this.$refs.pathChart
if (!dom) return
const { nodes, links } = this.pathGraphData
if (!nodes.length) return
const containerWidth = dom.clientWidth || 800
const maxLevel = Math.max(...nodes.map(n => n.level || 0), 0)
const totalLevels = maxLevel + 1
const levelGroups = {}
nodes.forEach(n => {
const lv = n.level || 0
if (!levelGroups[lv]) levelGroups[lv] = []
levelGroups[lv].push(n)
})
const maxNodesInLevel = Math.max(...Object.values(levelGroups).map(g => g.length), 1)
const paddingX = 100
const paddingY = 70
const LEVEL_GAP_X = totalLevels > 1
? Math.max(240, (containerWidth - paddingX * 2) / (totalLevels - 1))
: 240
const LEVEL_GAP_Y = 110
const START_X = paddingX
const START_Y = paddingY
nodes.forEach(n => {
const lv = n.level || 0
const sibIdx = n.levelIndex != null ? n.levelIndex : 0
const totalInLevel = n.levelTotal || 1
n.x = START_X + lv * LEVEL_GAP_X
n.y = START_Y + (sibIdx - (totalInLevel - 1) / 2) * LEVEL_GAP_Y
})
const chartHeight = Math.max(350, maxNodesInLevel * LEVEL_GAP_Y + paddingY * 2)
dom.style.width = containerWidth + 'px'
dom.style.height = chartHeight + 'px'
this.chart = echarts.init(dom)
const rootGradient = { type: 'radial', x: 0.5, y: 0.5, r: 0.5, colorStops: [{ offset: 0, color: '#93c5fd' }, { offset: 1, color: '#3b82f6' }] }
const intermediateGradient = { type: 'radial', x: 0.5, y: 0.5, r: 0.5, colorStops: [{ offset: 0, color: '#6ee7b7' }, { offset: 1, color: '#10b981' }] }
const currentGradient = { type: 'radial', x: 0.5, y: 0.5, r: 0.5, colorStops: [{ offset: 0, color: '#fde68a' }, { offset: 1, color: '#f59e0b' }] }
const gradientMap = {
root: rootGradient,
intermediate: intermediateGradient,
current: currentGradient
}
const option = {
tooltip: {
trigger: 'item',
confine: true,
backgroundColor: 'rgba(20,20,30,0.92)',
borderColor: '#3b82f6',
borderWidth: 1,
borderRadius: 8,
padding: [10, 14],
textStyle: { color: '#e5e7eb', fontSize: 12 },
formatter: (params) => {
if (params.dataType === 'edge') {
const ai = params.data.actionInfo || {}
const cp = params.data.costPerTon
const rows = [
`<div style="font-weight:bold;color:#fbbf24;margin-bottom:4px;font-size:13px">${ai.actionTypeName || '工序'}</div>`
]
if (cp) rows.push(`<span style="color:#34d399">吨钢成本</span>${cp} 元/吨`)
let hasInfo = false
if (ai.processTime) { rows.push(`处理时间:${ai.processTime}`); hasInfo = true }
if (ai.completeTime) { rows.push(`完成时间:${ai.completeTime}`); hasInfo = true }
if (ai.createTime) { rows.push(`创建时间:${ai.createTime}`); hasInfo = true }
if (ai.createBy) { rows.push(`创建人:${ai.createBy}`); hasInfo = true }
if (!cp && !hasInfo) return ''
return rows.join('<br/>')
}
const d = params.data
const ci = d.coilInfo || {}
const badge = d.isCurrent ? '<span style="display:inline-block;background:#f59e0b;color:#fff;padding:1px 6px;border-radius:4px;font-size:11px;margin-left:6px">当前卷</span>' : ''
const rows = [
`<div style="font-weight:bold;color:#60a5fa;margin-bottom:4px;font-size:13px">${ci.currentCoilNo || '-'}${badge}</div>`
]
if (ci.enterCoilNo) rows.push(`<span style="color:#9ca3af">入场钢卷号</span>${ci.enterCoilNo}`)
if (ci.supplierCoilNo) rows.push(`<span style="color:#9ca3af">厂家原料号</span>${ci.supplierCoilNo}`)
if (ci.itemName) rows.push(`<span style="color:#9ca3af">物料名</span>${ci.itemName}`)
if (ci.specification) rows.push(`<span style="color:#9ca3af">规格</span>${ci.specification}`)
if (ci.material) rows.push(`<span style="color:#9ca3af">材质</span>${ci.material}`)
if (ci.netWeight != null) rows.push(`<span style="color:#9ca3af">净重</span>${ci.netWeight} t`)
if (ci.manufacturer) rows.push(`<span style="color:#9ca3af">厂家</span>${ci.manufacturer}`)
if (ci.warehouseName) rows.push(`<span style="color:#9ca3af">逻辑库位</span>${ci.warehouseName}`)
if (ci.actualWarehouseName) rows.push(`<span style="color:#9ca3af">实际库区</span>${ci.actualWarehouseName}`)
if (ci.zincLayer) rows.push(`<span style="color:#9ca3af">镀层质量</span>${ci.zincLayer}`)
if (ci.qualityStatus) rows.push(`<span style="color:#9ca3af">质量状态</span>${ci.qualityStatus}`)
if (ci.surfaceTreatmentDesc) rows.push(`<span style="color:#9ca3af">表面处理</span>${ci.surfaceTreatmentDesc}`)
if (ci.createTime) rows.push(`<span style="color:#9ca3af">创建时间</span>${ci.createTime}`)
if (ci.createBy) rows.push(`<span style="color:#9ca3af">创建人</span>${ci.createBy}`)
return rows.join('<br/>')
}
},
animationDuration: 1000,
animationEasing: 'cubicOut',
animationEasingUpdate: 'quinticInOut',
series: [
{
type: 'graph',
layout: 'none',
roam: true,
draggable: false,
data: nodes.map(n => ({
name: n.name,
x: n.x,
y: n.y,
symbol: 'roundRect',
symbolSize: n.symbolSize,
category: undefined,
itemStyle: {
color: gradientMap[n.category] || '#909399',
borderColor: n.isCurrent ? '#f59e0b' : 'rgba(255,255,255,0.6)',
borderWidth: n.isCurrent ? 4 : 2,
shadowBlur: n.isCurrent ? 20 : 8,
shadowColor: n.isCurrent ? 'rgba(245,158,11,0.55)' : 'rgba(0,0,0,0.2)',
shadowOffsetY: 2
},
label: {
show: true,
formatter: (p) => {
const cn = p.data.coilNo || p.data.name
return cn && cn.length > 12 ? cn.slice(0, 11) + '...' : (cn || '-')
},
fontSize: 11,
fontWeight: n.isCurrent ? 'bold' : 'normal',
color: n.isCurrent ? '#f59e0b' : '#374151',
position: 'bottom',
distance: 8
},
emphasis: {
scale: 1.2,
itemStyle: {
shadowBlur: n.isCurrent ? 30 : 20,
shadowColor: n.isCurrent ? 'rgba(245,158,11,0.7)' : 'rgba(59,130,246,0.5)',
borderWidth: n.isCurrent ? 5 : 3
}
},
coilNo: n.coilNo,
coilInfo: n.coilInfo,
isCurrent: n.isCurrent
})),
links: links.map(l => ({
source: l.source,
target: l.target,
label: {
show: !!(l.label && l.label.formatter) || !!l.costPerTon,
formatter: (l.costPerTon != null)
? `${l.label && l.label.formatter ? l.label.formatter + ' ' : ''}${l.costPerTon}元/吨`
: (l.label && l.label.formatter ? l.label.formatter : ''),
fontSize: 11,
color: l.costPerTon != null ? '#059669' : '#606266'
},
lineStyle: {
color: l.costPerTon != null ? 'rgba(5,150,105,0.6)' : 'rgba(107,114,128,0.55)',
width: l.costPerTon != null ? 3 : 2.5,
curveness: 0.15,
cap: 'round',
opacity: 0.9
},
emphasis: {
lineStyle: {
color: '#3b82f6',
width: 4,
opacity: 1
},
label: {
fontSize: 12,
fontWeight: 'bold',
color: '#2563eb'
}
},
actionInfo: l.actionInfo,
costPerTon: l.costPerTon
})),
edgeSymbol: ['none', 'arrow'],
edgeSymbolSize: [0, 12],
edgeLabel: {
show: true,
fontSize: 11,
color: '#4b5563',
position: 'middle',
backgroundColor: 'rgba(255,255,255,0.85)',
borderRadius: 4,
padding: [2, 6],
distance: 6
},
categories: [
{ name: 'root', itemStyle: { color: '#3b82f6' }},
{ name: 'intermediate', itemStyle: { color: '#10b981' }},
{ name: 'current', itemStyle: { color: '#f59e0b' }}
]
}
]
}
this.chart.setOption(option)
this.handleResize = () => {
if (this.chart && !this.chart.isDisposed()) {
this.chart.resize()
}
}
window.addEventListener('resize', this.handleResize)
}
}
}
@@ -173,8 +671,8 @@ export default {
margin-left: 2px;
}
.cost-table-wrap {
margin-top: 10px;
.cost-chart-wrap {
margin-top: 12px;
}
.cost-table-title {
@@ -183,6 +681,29 @@ export default {
color: #303133;
padding: 6px 0;
border-bottom: 1px solid #ebeef5;
margin-bottom: 6px;
margin-bottom: 8px;
}
.path-chart {
width: 100%;
min-height: 300px;
}
.ton-cost-table-wrap {
margin-bottom: 12px;
}
.cost-ton-val {
font-weight: 600;
color: #059669;
}
.cost-ton-zero {
color: #c0c4cc;
}
.cost-total-val {
font-weight: 600;
color: #f56c6c;
}
</style>

View File

@@ -37,7 +37,7 @@
</div>
</div>
<InspectionInfo :taskList="inspectionTaskList" :loading="inspectionLoading" />
<!-- <InspectionInfo :taskList="inspectionTaskList" :loading="inspectionLoading" /> -->
<ProductionCharts v-if="isColdHardCoil"
:segData="segData" :gaugeRows="gaugeRows" :shapeRows="shapeRows"
@@ -54,7 +54,7 @@ import { listCoilAbnormal } from '@/api/wms/coilAbnormal'
import { listTransferOrderItem } from '@/api/wms/transferOrderItem'
import { listCoilQualityRejudge } from '@/api/wms/coilQualityRejudge'
import { getTimingSegByEncoilId, getTimingPlanDetailByHotcoilId, getTimingRealtimeData } from '@/api/l2/timing'
import { listInspectionTask } from "@/api/mes/qc/inspectionTask"
// import { listInspectionTask } from "@/api/mes/qc/inspectionTask"
import AbnormalTable from '@/views/wms/coil/components/AbnormalTable.vue'
import { formatTime } from './statusUtils'
@@ -64,7 +64,7 @@ import LifecycleTrace from './components/LifecycleTrace.vue'
import ContractInfo from './components/ContractInfo.vue'
import SalesObjectionTable from './components/SalesObjectionTable.vue'
import TransferRecords from './components/TransferRecords.vue'
import InspectionInfo from './components/InspectionInfo.vue'
// import InspectionInfo from './components/InspectionInfo.vue'
import ProductionCharts from './components/ProductionCharts.vue'
export default {
@@ -77,7 +77,7 @@ export default {
ContractInfo,
SalesObjectionTable,
TransferRecords,
InspectionInfo,
// InspectionInfo,
ProductionCharts
},
data() {
@@ -107,7 +107,7 @@ export default {
shapeRows: null,
deliveryOrderInfo: {},
salesObjectionInfo: [],
inspectionTaskList: [],
// inspectionTaskList: [],
inspectionLoading: false,
standardSteps: [],
}
@@ -150,7 +150,7 @@ export default {
await this.fetchDeliveryOrderInfo()
this.mergeTransferList()
await this.getSalesObjectionList()
await this.getInspectionTasks()
// await this.getInspectionTasks()
if (this.isColdHardCoil) {
await this.loadProductionData()
}
@@ -229,18 +229,18 @@ export default {
})
this.tranferList = list
},
async getInspectionTasks() {
this.inspectionLoading = true
try {
const res = await listInspectionTask({ enterCoilNos: this.coilInfo.enterCoilNo, pageNum: 1, pageSize: 100 })
this.inspectionTaskList = res.rows || []
} catch (e) {
console.error('获取检验任务异常:', e)
this.inspectionTaskList = []
} finally {
this.inspectionLoading = false
}
},
// async getInspectionTasks() {
// this.inspectionLoading = true
// try {
// const res = await listInspectionTask({ enterCoilNos: this.coilInfo.enterCoilNo, pageNum: 1, pageSize: 100 })
// this.inspectionTaskList = res.rows || []
// } catch (e) {
// console.error('获取检验任务异常:', e)
// this.inspectionTaskList = []
// } finally {
// // this.inspectionLoading = false
// }
// },
getInboundTime() {
if (!this.traceResult || !this.traceResult.steps) {
return this.coilInfo.createTime || null