| | |
| | | <script lang="ts" setup> |
| | | import type { MesWmBatchApi } from '#/api/mes/wm/batch'; |
| | | import type { MesTraceBatchApi } from '#/api/mes/trace/batch'; |
| | | |
| | | import { ref } from 'vue'; |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '..\..\..\..\..\packages\effects\common-ui\src'; |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { Tabs } from 'ant-design-vue'; |
| | | import { Descriptions, Spin, Tabs, Tag, message } from 'ant-design-vue'; |
| | | |
| | | import { useDescription } from '#/components/description'; |
| | | import { getBatchTraceDetail } from '#/api/mes/trace/batch'; |
| | | |
| | | import { useDetailSchema } from '../data'; |
| | | import TraceList from './trace-list.vue'; |
| | | import TraceDecision from './TraceDecision.vue'; |
| | | import TraceGraph from './TraceGraph.vue'; |
| | | import TraceStageOverview from './TraceStageOverview.vue'; |
| | | import TraceTimeline from './TraceTimeline.vue'; |
| | | |
| | | const detailData = ref<MesWmBatchApi.Batch>(); // 当前批次详情 |
| | | const subTabsName = ref<'backward' | 'forward'>('forward'); // 当前激活的追溯方向 |
| | | |
| | | const [Descriptions] = useDescription({ |
| | | bordered: true, |
| | | column: 3, |
| | | schema: useDetailSchema(), |
| | | const detailData = ref<MesTraceBatchApi.Detail>(); |
| | | const loading = ref(false); |
| | | const activeTab = ref('overview'); |
| | | const formatDate = (value?: string) => value ? new Date(value).toLocaleString() : '未记录'; |
| | | const riskLevel = computed(() => { |
| | | if (!detailData.value) return 'NORMAL'; |
| | | return detailData.value.decision?.riskLevel || (detailData.value.quality?.result === 2 ? 'HIGH' : 'NORMAL'); |
| | | }); |
| | | const riskLabel = computed(() => riskLevel.value === 'HIGH' ? '高风险' : riskLevel.value === 'NORMAL' ? '正常' : '需关注'); |
| | | const riskColor = computed(() => riskLevel.value === 'HIGH' ? 'red' : riskLevel.value === 'NORMAL' ? 'green' : 'orange'); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | onOpenChange(isOpen) { |
| | |
| | | detailData.value = undefined; |
| | | return; |
| | | } |
| | | detailData.value = modalApi.getData<MesWmBatchApi.Batch>(); |
| | | subTabsName.value = 'forward'; |
| | | const row = modalApi.getData<{ code?: string }>(); |
| | | if (row?.code) { |
| | | void loadDetail(row.code); |
| | | } |
| | | activeTab.value = 'overview'; |
| | | }, |
| | | }); |
| | | |
| | | async function loadDetail(code: string) { |
| | | loading.value = true; |
| | | try { |
| | | detailData.value = await getBatchTraceDetail(code); |
| | | } catch { |
| | | message.error('批次追溯详情加载失败'); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal |
| | | title="批次追溯" |
| | | class="w-4/5" |
| | | title="全链路批次追溯" |
| | | class="w-11/12" |
| | | :show-cancel-button="false" |
| | | :show-confirm-button="false" |
| | | > |
| | | <Descriptions class="mx-4 mt-2" :data="detailData" /> |
| | | <Tabs |
| | | v-if="detailData?.code" |
| | | v-model:active-key="subTabsName" |
| | | class="mx-4 mt-4" |
| | | type="card" |
| | | > |
| | | <Tabs.TabPane key="forward" tab="向前追溯"> |
| | | <TraceList :batch-code="detailData.code" direction="forward" /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="backward" tab="向后追溯"> |
| | | <TraceList :batch-code="detailData.code" direction="backward" /> |
| | | </Tabs.TabPane> |
| | | </Tabs> |
| | | <Spin :spinning="loading"> |
| | | <div v-if="detailData" class="px-2"> |
| | | <div class="mb-4 grid grid-cols-2 gap-3 lg:grid-cols-5"> |
| | | <div class="rounded border bg-blue-50 px-4 py-3"><div class="text-muted-foreground text-xs">袋码数量</div><div class="mt-1 text-lg font-semibold text-blue-700">{{ detailData.bagTotal ?? 0 }}</div></div> |
| | | <div class="rounded border bg-violet-50 px-4 py-3"><div class="text-muted-foreground text-xs">托盘数量</div><div class="mt-1 text-lg font-semibold text-violet-700">{{ detailData.palletCount ?? 0 }}</div></div> |
| | | <div class="rounded border bg-green-50 px-4 py-3"><div class="text-muted-foreground text-xs">入库数量</div><div class="mt-1 text-lg font-semibold text-green-700">{{ detailData.decision?.inboundQuantity ?? detailData.inbound?.quantity ?? 0 }}</div></div> |
| | | <div class="rounded border bg-orange-50 px-4 py-3"><div class="text-muted-foreground text-xs">销售去向</div><div class="mt-1 text-lg font-semibold text-orange-700">{{ detailData.decision?.salesTargetCount ?? detailData.salesTargets?.length ?? 0 }}</div></div> |
| | | <div class="col-span-2 rounded border px-4 py-3 lg:col-span-1" :class="riskLevel === 'HIGH' ? 'border-red-200 bg-red-50' : riskLevel === 'NORMAL' ? 'border-green-200 bg-green-50' : 'border-orange-200 bg-orange-50'"><div class="text-muted-foreground text-xs">综合风险</div><div class="mt-1"><Tag :color="riskColor">{{ riskLabel }}</Tag></div></div> |
| | | </div> |
| | | <Descriptions bordered :column="{ xs: 1, sm: 2, md: 3 }" size="small"> |
| | | <Descriptions.Item label="追溯入口">{{ detailData.requestCode || '未记录' }}</Descriptions.Item> |
| | | <Descriptions.Item label="入口类型"><Tag color="blue">{{ detailData.sourceType || 'BATCH' }}</Tag></Descriptions.Item> |
| | | <Descriptions.Item label="批次状态">{{ detailData.batchStatusName || '未知' }}</Descriptions.Item> |
| | | <Descriptions.Item label="品种">{{ detailData.itemName || detailData.itemCode || '未记录' }}</Descriptions.Item> |
| | | <Descriptions.Item label="批次号">{{ detailData.batchCode || '未记录' }}</Descriptions.Item> |
| | | <Descriptions.Item label="责任人">{{ detailData.ownerUserName || '未记录' }}</Descriptions.Item> |
| | | <Descriptions.Item label="生产线">{{ detailData.lineName || detailData.lineCode || '未记录' }}</Descriptions.Item> |
| | | <Descriptions.Item label="计划数量">{{ detailData.planQuantity ?? 0 }}</Descriptions.Item> |
| | | <Descriptions.Item label="生产日期">{{ formatDate(detailData.produceDate) }}</Descriptions.Item> |
| | | <Descriptions.Item label="入库时间">{{ formatDate(detailData.inboundTime || detailData.inbound?.eventTime) }}</Descriptions.Item> |
| | | </Descriptions> |
| | | |
| | | <Tabs v-model:active-key="activeTab" class="mt-4" type="card"> |
| | | <Tabs.TabPane key="overview" tab="阶段总览"> |
| | | <TraceStageOverview :detail="detailData" /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="timeline" tab="全链路时间线"> |
| | | <TraceTimeline :detail="detailData" /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="graph" tab="流动图谱"> |
| | | <TraceGraph :detail="detailData" /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="decision" tab="决策分析"> |
| | | <TraceDecision :detail="detailData" /> |
| | | </Tabs.TabPane> |
| | | </Tabs> |
| | | </div> |
| | | <div v-else-if="!loading" class="py-12 text-center text-muted-foreground">暂无追溯数据</div> |
| | | </Spin> |
| | | </Modal> |
| | | </template> |