<template>
|
<div v-loading="loading"
|
class="forward-result">
|
<!-- 订单卡 -->
|
<div v-for="chain in data.chains"
|
:key="chain.productionOrderId"
|
class="order-card">
|
<div class="order-head">
|
<div class="order-title">
|
<el-link type="primary"
|
@click="$emit('goto-order', chain.npsNo)">{{ chain.npsNo }}</el-link>
|
<span class="product-name">{{ chain.finishedProduct || '-' }}</span>
|
</div>
|
<el-tag :type="statusType(chain.orderStatus)">{{ statusText(chain.orderStatus) }}</el-tag>
|
</div>
|
|
<div class="order-meta">
|
<span>计划数量:{{ chain.planQty ?? '-' }} {{ chain.unit || '' }}</span>
|
<span>完工数量:{{ chain.completeQty ?? '-' }} {{ chain.unit || '' }}</span>
|
<span>下单时间:{{ chain.orderCreateTime || '-' }}</span>
|
</div>
|
|
<div v-if="chain.batches && chain.batches.length"
|
class="hit-batches">
|
<span class="hit-label">命中入库批次:</span>
|
<el-tag v-for="batch in chain.batches"
|
:key="batch.batchNo"
|
type="success"
|
effect="plain">
|
{{ batch.batchNo }}({{ batch.qty }} {{ chain.unit || '' }},{{ batch.inTime }},{{ recordTypeLabel(batch.recordType) }})
|
</el-tag>
|
</div>
|
|
<el-timeline class="order-timeline">
|
<!-- 工序报工 -->
|
<el-timeline-item v-for="report in sortReports(chain.reports)"
|
:key="report.productMainId + '_' + report.workOrderNo"
|
:timestamp="report.reportTime"
|
type="primary"
|
placement="top">
|
<div class="node-title">工序报工:{{ report.operationName || '-' }}</div>
|
<el-descriptions :column="3"
|
border
|
size="small">
|
<el-descriptions-item label="工单编号">{{ report.workOrderNo || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="报工单号">{{ report.productNo || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="报工人">{{ report.userName || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="设备">{{ report.deviceName || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="产出数量">{{ report.outputQty ?? '-' }}</el-descriptions-item>
|
<el-descriptions-item label="报废数量">{{ report.scrapQty ?? '-' }}</el-descriptions-item>
|
</el-descriptions>
|
|
<el-collapse class="input-collapse"
|
:model-value="expandedInputs[reportKey(chain, report)] || []"
|
@update:model-value="val => (expandedInputs[reportKey(chain, report)] = val)">
|
<el-collapse-item name="inputs"
|
:title="`投入物料(${inputsFor(chain, report).length})`">
|
<el-table :data="inputsFor(chain, report)"
|
border
|
size="small">
|
<el-table-column label="物料名称"
|
prop="materialName"
|
align="center" />
|
<el-table-column label="物料规格"
|
prop="materialModel"
|
align="center" />
|
<el-table-column label="投入数量"
|
prop="inputQty"
|
align="center" />
|
<el-table-column label="单位"
|
prop="unit"
|
align="center" />
|
</el-table>
|
</el-collapse-item>
|
</el-collapse>
|
</el-timeline-item>
|
|
<!-- 领料批次(订单级) -->
|
<el-timeline-item v-if="chain.picks && chain.picks.length"
|
type="warning"
|
placement="top">
|
<div class="node-title">领料批次</div>
|
<el-table :data="chain.picks"
|
border
|
size="small">
|
<el-table-column label="原料批号"
|
align="center"
|
min-width="200">
|
<template #default="{ row }">
|
<el-link type="primary"
|
@click="$emit('pick-batch', row.batchNo)">{{ row.batchNo }}</el-link>
|
</template>
|
</el-table-column>
|
<el-table-column label="物料"
|
prop="materialName"
|
align="center" />
|
<el-table-column label="物料规格"
|
prop="materialModel"
|
align="center" />
|
<el-table-column label="领料数量"
|
align="center">
|
<template #default="{ row }">{{ row.quantity }} {{ row.unit || '' }}</template>
|
</el-table-column>
|
<el-table-column label="工序"
|
prop="operationName"
|
align="center" />
|
<el-table-column label="领料时间"
|
prop="pickTime"
|
align="center" />
|
<el-table-column label="供应商"
|
align="center">
|
<template #default="{ row }">{{ row.supplierName || '-' }}</template>
|
</el-table-column>
|
<el-table-column label="采购合同号"
|
align="center">
|
<template #default="{ row }">{{ row.contractNo || '-' }}</template>
|
</el-table-column>
|
<el-table-column label="原料检验结论"
|
align="center">
|
<template #default="{ row }">{{ row.checkResult || '-' }}</template>
|
</el-table-column>
|
</el-table>
|
</el-timeline-item>
|
|
<!-- 检验单 -->
|
<el-timeline-item v-if="chain.inspects && chain.inspects.length"
|
type="success"
|
placement="top">
|
<div class="node-title">检验单</div>
|
<el-table :data="chain.inspects"
|
border
|
size="small">
|
<el-table-column label="检验类型"
|
prop="inspectTypeName"
|
align="center" />
|
<el-table-column label="工序"
|
align="center">
|
<template #default="{ row }">{{ row.process || '-' }}</template>
|
</el-table-column>
|
<el-table-column label="检验员"
|
prop="checkName"
|
align="center" />
|
<el-table-column label="产品名称"
|
prop="productName"
|
align="center" />
|
<el-table-column label="规格型号"
|
prop="model"
|
align="center" />
|
<el-table-column label="数量"
|
prop="quantity"
|
align="center" />
|
<el-table-column label="合格数量"
|
prop="qualifiedQuantity"
|
align="center" />
|
<el-table-column label="不合格数量"
|
prop="unqualifiedQuantity"
|
align="center" />
|
<el-table-column label="检测结果"
|
align="center">
|
<template #default="{ row }">
|
<el-tag :type="row.checkResult === '合格' ? 'success' : 'danger'">
|
{{ row.checkResult || '待检测' }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="检测日期"
|
prop="checkTime"
|
align="center" />
|
<el-table-column label="操作"
|
align="center"
|
width="100">
|
<template #default="{ row }">
|
<el-link type="primary"
|
@click="openInspect(chain, row)">查看详情</el-link>
|
</template>
|
</el-table-column>
|
</el-table>
|
</el-timeline-item>
|
</el-timeline>
|
</div>
|
|
<!-- 出库流向(顶层,按批号聚合) -->
|
<div v-if="data.outbounds && data.outbounds.length"
|
class="order-card">
|
<div class="order-head">
|
<div class="order-title">
|
<span class="product-name">出库流向</span>
|
</div>
|
</div>
|
<el-table :data="data.outbounds"
|
border
|
size="small">
|
<el-table-column label="批号"
|
prop="batchNo"
|
align="center" />
|
<el-table-column label="产品名称"
|
prop="productName"
|
align="center" />
|
<el-table-column label="规格型号"
|
prop="productModel"
|
align="center" />
|
<el-table-column label="出库数量"
|
align="center">
|
<template #default="{ row }">{{ row.qty }} {{ row.unit || '' }}</template>
|
</el-table-column>
|
<el-table-column label="出库类型"
|
align="center">
|
<template #default="{ row }">{{ recordTypeLabel(row.recordType) }}</template>
|
</el-table-column>
|
<el-table-column label="出库时间"
|
prop="outTime"
|
align="center" />
|
<el-table-column label="备注"
|
align="center">
|
<template #default="{ row }">{{ row.remark || '-' }}</template>
|
</el-table-column>
|
</el-table>
|
</div>
|
|
<!-- 检验单详情 -->
|
<el-dialog v-model="inspectVisible"
|
title="检验单详情"
|
width="900px">
|
<div v-if="currentInspect"
|
class="inspect-detail">
|
<el-descriptions :column="3"
|
border
|
size="small">
|
<el-descriptions-item label="检验类型">{{ currentInspect.inspectTypeName || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="工序">{{ currentInspect.process || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="检验员">{{ currentInspect.checkName || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="产品名称">{{ currentInspect.productName || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="规格型号">{{ currentInspect.model || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="单位">{{ currentInspect.unit || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="数量">{{ currentInspect.quantity ?? '-' }}</el-descriptions-item>
|
<el-descriptions-item label="合格数量">{{ currentInspect.qualifiedQuantity ?? '-' }}</el-descriptions-item>
|
<el-descriptions-item label="不合格数量">{{ currentInspect.unqualifiedQuantity ?? '-' }}</el-descriptions-item>
|
<el-descriptions-item label="检测结果">
|
<el-tag :type="currentInspect.checkResult === '合格' ? 'success' : 'danger'">
|
{{ currentInspect.checkResult || '待检测' }}
|
</el-tag>
|
</el-descriptions-item>
|
<el-descriptions-item label="检测日期">{{ currentInspect.checkTime || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="不合格现象">{{ currentInspect.defectivePhenomena || '-' }}</el-descriptions-item>
|
</el-descriptions>
|
|
<h4 class="sub-title">不合格处理</h4>
|
<el-table :data="currentUnqualified"
|
border
|
size="small">
|
<el-table-column label="序号"
|
type="index"
|
width="60"
|
align="center" />
|
<el-table-column label="产品名称"
|
prop="productName"
|
align="center" />
|
<el-table-column label="规格型号"
|
prop="model"
|
align="center" />
|
<el-table-column label="数量"
|
prop="quantity"
|
align="center" />
|
<el-table-column label="不合格现象"
|
prop="defectivePhenomena"
|
align="center" />
|
<el-table-column label="处理结果"
|
prop="dealResult"
|
align="center" />
|
<el-table-column label="处理人"
|
prop="dealName"
|
align="center" />
|
<el-table-column label="处理日期"
|
prop="dealTime"
|
align="center" />
|
</el-table>
|
</div>
|
<template #footer>
|
<el-button @click="inspectVisible = false">关闭</el-button>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive } from "vue";
|
|
defineProps({
|
data: {
|
type: Object,
|
required: true,
|
},
|
loading: {
|
type: Boolean,
|
default: false,
|
},
|
});
|
|
defineEmits(["goto-order", "pick-batch"]);
|
|
const statusTypeMap = { 1: "primary", 2: "warning", 3: "success", 5: "danger" };
|
const statusTextMap = { 1: "待开始", 2: "进行中", 3: "已完成", 5: "已结束" };
|
const statusType = status => statusTypeMap[status] || "info";
|
const statusText = status => statusTextMap[status] || "已取消";
|
|
const inTypeMap = {
|
0: "自定义入库",
|
2: "生产报工入库",
|
6: "质检合格入库",
|
7: "采购入库",
|
10: "采购入库",
|
};
|
const outTypeMap = {
|
1: "自定义出库",
|
3: "生产报工出库",
|
8: "销售出库",
|
13: "销售发货出库",
|
};
|
const recordTypeLabel = type => {
|
const key = String(type ?? "");
|
return inTypeMap[key] || outTypeMap[key] || key || "-";
|
};
|
|
const sortReports = reports =>
|
[...(reports || [])].sort((a, b) =>
|
String(a.reportTime || "").localeCompare(String(b.reportTime || ""))
|
);
|
|
const inputsFor = (chain, report) =>
|
(chain.inputs || []).filter(
|
item => String(item.productMainId) === String(report.productMainId)
|
);
|
|
const reportKey = (chain, report) =>
|
`${chain.productionOrderId}_${report.productMainId}_${report.workOrderNo}`;
|
const expandedInputs = reactive({});
|
|
// 检验单详情
|
const inspectVisible = ref(false);
|
const currentInspect = ref(null);
|
const currentUnqualified = ref([]);
|
const openInspect = (chain, inspect) => {
|
currentInspect.value = inspect;
|
const own = inspect.unqualifiedList || [];
|
currentUnqualified.value = own.length
|
? own
|
: (chain.unqualifiedList || []).filter(
|
item => String(item.inspectId) === String(inspect.inspectId)
|
);
|
inspectVisible.value = true;
|
};
|
</script>
|
|
<style scoped lang="scss">
|
.forward-result {
|
min-height: 200px;
|
}
|
|
.order-card {
|
background-color: #ffffff;
|
border-radius: 10px;
|
padding: 20px;
|
margin-bottom: 20px;
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08);
|
}
|
|
.order-head {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding-bottom: 12px;
|
border-bottom: 1px solid #ebeef5;
|
}
|
|
.order-title {
|
display: flex;
|
align-items: center;
|
gap: 12px;
|
font-size: 16px;
|
}
|
|
.product-name {
|
font-weight: 600;
|
color: #1a1a1a;
|
}
|
|
.order-meta {
|
display: flex;
|
gap: 28px;
|
padding: 12px 0;
|
font-size: 13px;
|
color: #606266;
|
}
|
|
.hit-batches {
|
display: flex;
|
flex-wrap: wrap;
|
align-items: center;
|
gap: 8px;
|
padding: 8px 12px;
|
background-color: #f0f9eb;
|
border-radius: 6px;
|
|
.hit-label {
|
font-size: 13px;
|
color: #606266;
|
}
|
}
|
|
.order-timeline {
|
margin-top: 20px;
|
padding-left: 4px;
|
}
|
|
.node-title {
|
font-size: 14px;
|
font-weight: 600;
|
color: #303133;
|
margin-bottom: 12px;
|
}
|
|
.input-collapse {
|
margin-top: 12px;
|
}
|
|
.sub-title {
|
margin: 20px 0 12px;
|
font-size: 14px;
|
font-weight: 600;
|
color: #303133;
|
}
|
</style>
|