Files
klp-oa/klp-ui/src/views/wms/coil/info/index.vue
砂糖 b9fb4b4611 refactor(wms/coil): 重构钢卷详情页面,拆分组件并优化页面结构
1.  将原钢卷详情页面拆分为多个独立组件,按功能模块划分
2.  调整路由指向,将页面入口指向新的页面文件
3.  新增状态工具类,封装通用格式化和样式
2026-06-11 14:02:49 +08:00

452 lines
17 KiB
Vue

<template>
<div class="coil-info-page" v-loading="loading">
<div class="content-container">
<BasicInfoSection :coilInfo="coilInfo" />
<CostInfoSection :coilInfo="coilInfo" :traceResult="traceResult"
:hoardingDays="hoardingDays" :hoardingCost="hoardingCost" />
<LifecycleTrace :traceResult="traceResult" :coilInfo="coilInfo" />
<ContractInfo title="生产合同信息" :info="salesInfo" emptyText="未找到相关生产合同信息" />
<ContractInfo title="发货合同信息" :info="deliveryOrderInfo" emptyText="未找到相关发货合同信息" />
<SalesObjectionTable :list="salesObjectionInfo" />
<TransferRecords :warehouseList="warehouseTranferList" :transferList="tranferList" />
<div class="section abnormal-section">
<div class="section-header">
<span class="section-icon">&#9888;&#65039;</span>
<span class="section-title">异常信息</span>
</div>
<div class="section-body">
<abnormal-table ref="abnormalTable" :list="abmornalList" :editable="false" :show-coil="false"
:coil-info="coilInfo" />
</div>
</div>
<div class="section abnormal-section">
<div class="section-header">
<span class="section-icon">&#128218;</span>
<span class="section-title">异常历史</span>
</div>
<div class="section-body">
<abnormal-table ref="historyAbnormalTable" :list="allAbmornalList" :editable="false" :show-coil="true" />
</div>
</div>
<InspectionInfo :taskList="inspectionTaskList" :loading="inspectionLoading" />
<ProductionCharts v-if="isColdHardCoil"
:segData="segData" :gaugeRows="gaugeRows" :shapeRows="shapeRows"
:perfSegCount="perfSegCount" :segLoading="segLoading" :realtimeLoading="realtimeLoading" />
</div>
</div>
</template>
<script>
import { getMaterialCoil, listMaterialCoil, getMaterialCoilTrace, getDeliveryOrderInfo } from '@/api/wms/coil'
import { getCoilWarehouseOperationLogByCoilId } from '@/api/wms/coilWarehouseOperationLog'
import { listSalesObjection } from "@/api/crm/salesObjection"
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 AbnormalTable from '@/views/wms/coil/components/AbnormalTable.vue'
import { formatTime } from './statusUtils'
import BasicInfoSection from './components/BasicInfoSection.vue'
import CostInfoSection from './components/CostInfoSection.vue'
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 ProductionCharts from './components/ProductionCharts.vue'
export default {
name: 'CoilInfo',
components: {
AbnormalTable,
BasicInfoSection,
CostInfoSection,
LifecycleTrace,
ContractInfo,
SalesObjectionTable,
TransferRecords,
InspectionInfo,
ProductionCharts
},
data() {
return {
loading: false,
coilInfoLoading: false,
traceLoading: false,
traceResult: null,
coilDetails: {},
loadingCoilDetails: false,
coilInfo: {},
coilList: [],
coilId: '',
warehouseTranferList: [],
abmornalList: [],
allAbmornalList: [],
transferOrderItemList: [],
coilQualityRejudgeList: [],
tranferList: [],
perfLoading: false,
perfSeries: null,
perfSegCount: 0,
segLoading: false,
realtimeLoading: false,
segData: null,
gaugeRows: null,
shapeRows: null,
deliveryOrderInfo: {},
salesObjectionInfo: [],
inspectionTaskList: [],
inspectionLoading: false,
standardSteps: [],
}
},
computed: {
salesInfo() {
return this.coilInfo.orderList?.[0] || {}
},
isColdHardCoil() {
return this.coilInfo.itemName && this.coilInfo.itemName.includes('冷硬卷')
},
hasPerfData() {
return this.perfSeries && this.perfSegCount > 0
},
hoardingDays() {
const inboundTime = this.getInboundTime()
if (!inboundTime) return 0
const inboundDate = new Date(inboundTime)
const endDate = this.coilInfo.status == 1
? new Date(this.coilInfo.exportTime)
: new Date()
const diffTime = endDate.getTime() - inboundDate.getTime()
return Math.max(0, Math.floor(diffTime / (1000 * 60 * 60 * 24)))
},
hoardingCost() {
const netWeight = parseFloat(this.coilInfo.netWeight) || 0
return (this.hoardingDays * netWeight).toFixed(2)
}
},
async created() {
this.coilId = this.$route.params.coilId
this.loading = true
await this.getCoilInfo()
await this.getTraceInfo()
await this.getHistoryAbnormalList()
await this.getAbnormalList()
await this.getWarehouseTransferList()
await this.getTransferOrderItemList()
await this.getCoilQualityRejudgeList()
await this.fetchDeliveryOrderInfo()
this.mergeTransferList()
await this.getSalesObjectionList()
await this.getInspectionTasks()
if (this.isColdHardCoil) {
await this.loadProductionData()
}
this.loading = false
},
methods: {
async getCoilInfo() {
const res = await getMaterialCoil(this.coilId)
this.coilInfo = res.data || {}
},
async fetchDeliveryOrderInfo() {
const res = await getDeliveryOrderInfo(this.coilId)
this.deliveryOrderInfo = res.data || {}
},
async getSalesObjectionList() {
const res = await listSalesObjection({ coilIds: this.coilId })
this.salesObjectionInfo = res.rows || []
},
async getTraceInfo() {
const res = await getMaterialCoilTrace({
coilId: this.coilId,
currentCoilNo: this.coilInfo.currentCoilNo
})
this.traceResult = res.data
this.standardSteps = this.traceResult.steps?.map(step => this.formatStep(step)) || []
this.fetchCoilDetails()
},
async getAbnormalList() {
const res = await listCoilAbnormal({ coilId: this.coilId })
this.abmornalList = res.rows || []
},
async getHistoryAbnormalList() {
const coilIds = this.traceResult.steps?.map(step => step.old_coil_id).filter(Boolean) || []
if (coilIds.length === 0) return
const res = await listCoilAbnormal({ coilIds: coilIds.join(',') })
this.allAbmornalList = res.rows || []
},
async getWarehouseTransferList() {
const res = await getCoilWarehouseOperationLogByCoilId({ coilId: this.coilId })
this.warehouseTranferList = res.data || []
},
async getTransferOrderItemList() {
const res = await listTransferOrderItem({ coilId: this.coilId, pageNum: 1, pageSize: 100 })
this.transferOrderItemList = res.rows || []
},
async getCoilQualityRejudgeList() {
const res = await listCoilQualityRejudge({ coilId: this.coilId, pageNum: 1, pageSize: 100 })
this.coilQualityRejudgeList = res.rows || []
},
mergeTransferList() {
const list = []
this.transferOrderItemList.forEach(item => {
list.push({
type: '批量调拨',
before: '逻辑库:' + (item.warehouseNameBefore || '-'),
after: '逻辑库:' + (item.warehouseNameAfter || '-'),
createTime: item.createTime || '-',
remark: item.remark || '-',
...item
})
})
this.coilQualityRejudgeList.forEach(item => {
list.push({
...item,
type: '技术部改判',
before: '质量状态:' + (item.beforeQuality || '-'),
after: '质量状态:' + (item.afterQuality || '-'),
createTime: item.createTime || '-',
remark: item.rejudgeReason,
})
})
list.sort((a, b) => {
const timeA = new Date(a.createTime || 0).getTime()
const timeB = new Date(b.createTime || 0).getTime()
return timeB - timeA
})
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
}
},
getInboundTime() {
if (!this.traceResult || !this.traceResult.steps) {
return this.coilInfo.createTime || null
}
const createStep = this.traceResult.steps.find(step =>
step.action === '新增' || step.action === '创建'
)
if (createStep) {
return createStep.create_time || createStep.update_time || createStep.time
}
return this.coilInfo.createTime || null
},
formatTime(timeStamp) {
return formatTime(timeStamp)
},
formatStep(originalStep) {
const standardStep = {
action: '', time: '-', operation: '-',
oldCoilIds: [], newCoilIds: [],
changedFields: '', oldCoilInfoList: [], newCoilInfoList: [],
deletedCoilInfo: null, restoredCoilInfo: null,
original: originalStep
}
if (originalStep.action === '新增') {
standardStep.action = '创建'
} else if (originalStep.action === '更新') {
if (originalStep.operation === '信息更新') standardStep.action = '更新'
else if (originalStep.operation === '分卷') standardStep.action = '分卷'
else if (originalStep.operation === '合卷') standardStep.action = '合卷'
else standardStep.action = '更新'
} else if (originalStep.action === '回滚') {
standardStep.action = '回滚'
} else {
standardStep.action = originalStep.action || '-'
}
if (originalStep.update_time || originalStep.create_time) {
standardStep.time = this.formatTime(originalStep.update_time || originalStep.create_time)
} else if (originalStep.rollback_time) {
standardStep.time = this.formatTime(originalStep.rollback_time)
}
standardStep.operation = originalStep.operator_nickname || originalStep.operator || originalStep.create_by || '-'
switch (standardStep.action) {
case '创建': standardStep.oldCoilIds = []; break
case '更新': standardStep.oldCoilIds = originalStep.old_coil_id ? [originalStep.old_coil_id.trim()] : []; break
case '退火': standardStep.oldCoilIds = originalStep.old_coil_id ? [originalStep.old_coil_id.trim()] : []; break
case '分卷': standardStep.oldCoilIds = originalStep.old_coil_id ? [originalStep.old_coil_id.trim()] : []; break
case '合卷':
standardStep.oldCoilIds = originalStep.parent_coil_ids
? originalStep.parent_coil_ids.split(',').map(id => id.trim()).filter(Boolean) : []
break
case '回滚':
standardStep.oldCoilIds = originalStep.deleted_coil_id ? [originalStep.deleted_coil_id.trim()] : []; break
default: standardStep.oldCoilIds = []
}
switch (standardStep.action) {
case '创建': standardStep.newCoilIds = originalStep.current_coil_id ? [originalStep.current_coil_id.trim()] : []; break
case '更新': standardStep.newCoilIds = originalStep.new_coil_id ? [originalStep.new_coil_id.trim()] : []; break
case '退火': standardStep.newCoilIds = originalStep.new_coil_id ? [originalStep.new_coil_id.trim()] : []; break
case '分卷':
standardStep.newCoilIds = originalStep.child_coil_ids
? originalStep.child_coil_ids.split(',').map(id => id.trim()).filter(Boolean) : []
break
case '合卷': standardStep.newCoilIds = originalStep.new_coil_id ? [originalStep.new_coil_id.trim()] : []; break
case '回滚':
standardStep.newCoilIds = originalStep.restored_coil_id ? [originalStep.restored_coil_id.trim()] : []; break
default: standardStep.newCoilIds = []
}
if (standardStep.action === '更新') {
standardStep.changedFields = originalStep.changed_fields || ''
}
if (standardStep.action === '创建') {
listMaterialCoil({ currentCoilNo: originalStep.current_coil_no, enterCoilNo: originalStep.current_coil_no }).then(res => {
standardStep.newCoilInfoList = res.rows || []
})
}
standardStep.newCoilInfoList = this.mapCoilInfoList(standardStep.newCoilIds)
if (standardStep.action === '回滚') {
standardStep.deletedCoilInfo = standardStep.oldCoilInfoList[0] || null
standardStep.restoredCoilInfo = standardStep.newCoilInfoList[0] || null
}
return standardStep
},
mapCoilInfoList(coilIds) {
if (!coilIds || !coilIds.length) return []
return coilIds.map(coilId => {
const coil = this.coilDetails[coilId] || {}
const safeCoil = coil || {}
return {
...safeCoil,
enterCoilNo: safeCoil.enterCoilNo || '-',
currentCoilNo: safeCoil.currentCoilNo || '-',
materialType: safeCoil.materialType || '-',
itemName: safeCoil.itemName || '-',
specification: safeCoil.specification || '-',
material: safeCoil.material || '-',
netWeight: safeCoil.netWeight || 0,
warehouseName: safeCoil.warehouseName || '-',
actualWarehouseName: safeCoil.actualWarehouseName || '-',
manufacturer: safeCoil.manufacturer || '-',
zincLayer: safeCoil.zincLayer || '-',
qualityStatus: safeCoil.qualityStatus || '-',
createTime: safeCoil.createTime || '-',
status: safeCoil.status || 0,
exportByName: safeCoil.exportByName || '-',
exportTime: safeCoil.exportTime || '-',
}
}).filter(coil => coil)
},
collectCoilIds() {
if (!this.standardSteps.length) return []
const coilIds = new Set()
this.standardSteps.forEach(step => {
step.oldCoilIds.forEach(id => id && coilIds.add(id))
step.newCoilIds.forEach(id => id && coilIds.add(id))
})
return [...coilIds]
},
async fetchCoilDetails() {
const coilIds = this.collectCoilIds().filter(id => id)
if (coilIds.length === 0) { this.coilDetails = {}; return }
if (this.loadingCoilDetails) return
this.loadingCoilDetails = true
try {
const res = await listMaterialCoil({ coilIds: coilIds.join(',') })
if (res && res.code === 200 && res.rows) {
const coilIdMap = {}
res.rows.forEach(coil => { if (coil.coilId) coilIdMap[coil.coilId] = coil })
this.coilDetails = coilIdMap
this.standardSteps.forEach(step => {
step.oldCoilInfoList = this.mapCoilInfoList(step.oldCoilIds)
step.newCoilInfoList = this.mapCoilInfoList(step.newCoilIds)
if (step.action === '回滚') {
step.deletedCoilInfo = step.oldCoilInfoList[0] || null
step.restoredCoilInfo = step.newCoilInfoList[0] || null
}
})
} else {
this.$message.warning('获取钢卷详情失败')
}
} catch (error) {
console.error('获取钢卷详情接口异常:', error)
this.$message.error('获取钢卷详情异常,请刷新重试')
} finally {
this.loadingCoilDetails = false
}
},
async loadProductionData() {
const hotCoilId = this.coilInfo.enterCoilNo
if (!hotCoilId) return
this.segLoading = true
this.realtimeLoading = true
try {
const detail = await getTimingPlanDetailByHotcoilId(hotCoilId)
const encoilId = detail?.data?.firstRow?.coilid || ''
const [segRes, realtimeRes] = await Promise.all([
encoilId ? getTimingSegByEncoilId(encoilId) : Promise.resolve({ data: null }),
encoilId ? getTimingRealtimeData(encoilId) : Promise.resolve({ data: null })
])
const series = segRes?.data?.series || null
const rows = segRes?.data?.rows || []
this.perfSegCount = rows.length
this.perfSeries = series
this.segData = series
const g = realtimeRes?.data?.gauge?.result
const s = realtimeRes?.data?.shape?.result
this.gaugeRows = Array.isArray(g) ? g : null
this.shapeRows = Array.isArray(s) ? s : null
} catch (error) {
console.error('获取生产数据异常:', error)
this.perfSeries = null
this.perfSegCount = 0
this.segData = null
this.gaugeRows = null
this.shapeRows = null
} finally {
this.segLoading = false
this.realtimeLoading = false
}
},
},
}
</script>
<style lang="scss">
@import './coil-info.scss';
</style>
<style scoped>
.coil-info-page {
background: linear-gradient(180deg, #f0f4f8 0%, #e2e8f0 50%, #f7fafc 100%);
min-height: 100vh;
padding: 8px 12px;
}
.content-container {
display: flex;
flex-direction: column;
gap: 12px;
}
</style>