From 092690791db284f484f870c7c85b6ddee6e56092 Mon Sep 17 00:00:00 2001 From: liuyihe <2513762445@qq.com> Date: Thu, 15 Oct 2026 15:45:16 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../overseas-warehouse/api/cargoDiscards.ts | 6 - .../api/warehouseTransfers.test.ts | 8 +- .../api/warehouseTransfers.ts | 24 +++ .../WarehouseTransferReceiptPanel.test.ts | 106 +++++++++++++ .../WarehouseTransferReceiptPanel.vue | 143 ++++++++++++++++++ .../pages/CargoDiscardsView.vue | 4 +- .../pages/LastMileOrderDetailView.test.ts | 27 ++++ .../pages/LastMileOrderDetailView.vue | 40 ++++- .../pages/OutboundDetailView.vue | 2 +- .../pages/OutboundsView.vue | 4 +- 10 files changed, 348 insertions(+), 16 deletions(-) create mode 100644 src/modules/overseas-warehouse/components/WarehouseTransferReceiptPanel.test.ts create mode 100644 src/modules/overseas-warehouse/components/WarehouseTransferReceiptPanel.vue diff --git a/src/modules/overseas-warehouse/api/cargoDiscards.ts b/src/modules/overseas-warehouse/api/cargoDiscards.ts index 267c0cd0..b404cfd7 100644 --- a/src/modules/overseas-warehouse/api/cargoDiscards.ts +++ b/src/modules/overseas-warehouse/api/cargoDiscards.ts @@ -20,12 +20,6 @@ export async function reviewCargoDiscard(discardNo: string, decision: 'APPROVE' const response = await http.post, ApiEnvelope>(`/platform/v1/last-leg/cargo-discards/${encodeURIComponent(discardNo)}/review`, { decision, remark }, { params: customerId ? { customerId } : undefined }) return unwrapApiResponse(response) } -export async function completeCargoDiscard(discardNo: string, customerId?: string) { - const response = await http.post, ApiEnvelope>(`/platform/v1/last-leg/cargo-discards/${encodeURIComponent(discardNo)}/complete`, undefined, { params: customerId ? { customerId } : undefined }) - return unwrapApiResponse(response) -} - - export function downloadCargoDiscardPhoto(discardNo: string, fileId: string, customerId: string) { return http.get(`/platform/v1/last-leg/cargo-discards/${encodeURIComponent(discardNo)}/photos/files/${encodeURIComponent(fileId)}/download`, { params: { customerId }, responseType: 'blob' }) } diff --git a/src/modules/overseas-warehouse/api/warehouseTransfers.test.ts b/src/modules/overseas-warehouse/api/warehouseTransfers.test.ts index 0fa70473..4b35accd 100644 --- a/src/modules/overseas-warehouse/api/warehouseTransfers.test.ts +++ b/src/modules/overseas-warehouse/api/warehouseTransfers.test.ts @@ -1,11 +1,17 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { http } from '@/shared/api/http' -import { allocateWarehouseTransfer, getWarehouseTransfer, listWarehouseTransfers, reviewWarehouseTransfer, outboundWarehouseTransfer, receiveWarehouseTransfer, listTransferReceivingWarehouses } from './warehouseTransfers' +import { allocateWarehouseTransfer, getWarehouseTransferCartonLabel, getWarehouseTransfer, listWarehouseTransfers, reviewWarehouseTransfer, outboundWarehouseTransfer, receiveWarehouseTransfer, listTransferReceivingWarehouses } from './warehouseTransfers' vi.mock('@/shared/api/http', async (importOriginal) => ({ ...(await importOriginal()), http: { get: vi.fn(), post: vi.fn() } })) describe('warehouseTransfers API', () => { beforeEach(() => vi.clearAllMocks()) + it('loads server-rendered carton labels using the source transfer and customer scope', async () => { + const label = { transferNo: 'WT/1', cartons: [{ cartonMark: 'BOX-1' }], printableHtml: 'BOX-1' } + vi.mocked(http.get).mockResolvedValue({ code: '200', data: label }) + expect(await getWarehouseTransferCartonLabel('customer-1', 'WT/1')).toEqual(label) + expect(http.get).toHaveBeenCalledExactlyOnceWith('/platform/v1/last-leg/warehouse-transfers/WT%2F1/carton-label', { params: { customerId: 'customer-1' } }) + }) it('uses the resource-role receiving options contract, never warehouse master list', async () => { vi.mocked(http.get).mockResolvedValue({ code: '200', data: { records: [{ id: 'NY', customerId: 0, warehouseCode: 'NY', warehouseName: '纽约', warehouseType: 'OVERSEAS', status: 'ACTIVE' }], total: 1, page: 1, size: 200, pages: 1 } }) expect((await listTransferReceivingWarehouses()).records[0]?.customerId).toBe('0') diff --git a/src/modules/overseas-warehouse/api/warehouseTransfers.ts b/src/modules/overseas-warehouse/api/warehouseTransfers.ts index 09791948..3b62351f 100644 --- a/src/modules/overseas-warehouse/api/warehouseTransfers.ts +++ b/src/modules/overseas-warehouse/api/warehouseTransfers.ts @@ -35,11 +35,21 @@ export interface WarehouseTransferItemDto { id: string skuCode: string productNameZh: string + quantity?: number | string | null packageCount: number shippingMark?: string | null } export interface WarehouseTransferCartonDto { + transferItemId?: string | null + transferPalletId?: string | null + length?: number | string | null + width?: number | string | null + height?: number | string | null + weight?: number | string | null + quantity?: number | string | null + countedAt?: string | null + discrepancyReason?: string | null cartonMark?: string | null customerCartonMark?: string | null cartonIndex: number @@ -47,6 +57,19 @@ export interface WarehouseTransferCartonDto { status: string } +export interface WarehouseTransferCartonLabelDto { + transferNo: string + cartons: WarehouseTransferCartonDto[] + printableHtml: string +} + +export async function getWarehouseTransferCartonLabel(customerId: string, transferNo: string) { + const response = await http.get, ApiEnvelope>( + `/platform/v1/last-leg/warehouse-transfers/${encodeURIComponent(transferNo)}/carton-label`, { params: { customerId } }, + ) + return unwrapApiResponse(response) +} + export interface WarehouseTransferRelabelBindingDto { operationType: string customerCartonMark: string @@ -61,6 +84,7 @@ export interface WarehouseTransferRelabelBindingDto { export interface WarehouseTransferPalletDto { id: string sequenceNo: number + warehouseLocationCode?: string | null palletNo?: string | null cartonCount: number grossWeightKg: number diff --git a/src/modules/overseas-warehouse/components/WarehouseTransferReceiptPanel.test.ts b/src/modules/overseas-warehouse/components/WarehouseTransferReceiptPanel.test.ts new file mode 100644 index 00000000..ae8bcb9b --- /dev/null +++ b/src/modules/overseas-warehouse/components/WarehouseTransferReceiptPanel.test.ts @@ -0,0 +1,106 @@ +import ElementPlus from 'element-plus' +import { defineComponent, h } from 'vue' +import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import WarehouseTransferReceiptPanel from './WarehouseTransferReceiptPanel.vue' +import { getWarehouseTransfer, getWarehouseTransferCartonLabel, type WarehouseTransferDetailDto } from '../api/warehouseTransfers' + +vi.mock('../api/warehouseTransfers', () => ({ getWarehouseTransfer: vi.fn(), getWarehouseTransferCartonLabel: vi.fn() })) +enableAutoUnmount(afterEach) +const DataTableStub = defineComponent({ + props: ['data', 'columns'], + setup(props, { slots }) { return () => h('div', { class: 'test-table' }, [JSON.stringify(props.data), ...(props.data || []).flatMap((row: object) => [slots.boxAction?.({ row }), slots.quantityDifference?.({ row }), slots.boxDifference?.({ row })])]) }, +}) +const DialogStub = defineComponent({ props: ['modelValue'], setup(props, { slots }) { return () => props.modelValue ? h('section', { 'data-test': 'box-dialog' }, slots.default?.()) : null } }) +const render = (transferNo = 'WT-SOURCE') => mount(WarehouseTransferReceiptPanel, { props: { customerId: 'CUSTOMER', transferNo }, global: { plugins: [ElementPlus], stubs: { DataTable: DataTableStub, AppDialog: DialogStub } } }) +function fixture(): WarehouseTransferDetailDto { + return { + id: '1', transferNo: 'WT-SOURCE', status: 'WAIT_CUSTOMER_CONFIRMATION', needRelabel: false, packageCount: 4, + items: [{ id: '10', skuCode: 'SKU-A', productNameZh: '产品 A', quantity: '120', packageCount: 3 }, { id: '20', skuCode: 'SKU-B', productNameZh: '产品 B', quantity: 30, packageCount: 1 }], + cartons: [ + { cartonMark: 'BOX-1', cartonIndex: 1, totalCartons: 4, status: 'COUNTED', transferItemId: '10', transferPalletId: '100', quantity: '8', length: 100, width: 20, height: 50, weight: '2.5', countedAt: '2026-10-15T12:00:00' }, + { cartonMark: 'BOX-2', cartonIndex: 2, totalCartons: 4, status: 'COUNTED', transferItemId: '10', transferPalletId: '100', quantity: 12, length: 100, width: 20, height: 50, weight: 3.5 }, + { cartonMark: 'BOX-3', cartonIndex: 3, totalCartons: 4, status: 'COUNTED', transferItemId: '20', transferPalletId: '200', quantity: 7, length: 100, width: 20, height: 50, weight: 4 }, + { cartonMark: 'BOX-MISSING', cartonIndex: 4, totalCartons: 4, status: 'NOT_RECEIVED', transferItemId: '10', quantity: 999, weight: 999 }, + ], + pallets: [{ id: '100', sequenceNo: 1, palletNo: 'PALLET-A', cartonCount: 2, grossWeightKg: 6, volumeCbm: .2, status: 'COMPLETED' }, { id: '200', sequenceNo: 2, palletNo: 'PALLET-B', cartonCount: 1, grossWeightKg: 4, volumeCbm: .1, status: 'COMPLETED' }], relabelBindings: [], + } +} +beforeEach(() => { vi.mocked(getWarehouseTransfer).mockReset(); vi.mocked(getWarehouseTransferCartonLabel).mockReset() }) +afterEach(() => vi.restoreAllMocks()) +describe('转运预报与实际签收明细', () => { + it('keeps declarations separate and totals only counted boxes across pallets', async () => { + vi.mocked(getWarehouseTransfer).mockResolvedValue(fixture()) + const wrapper = render(); await flushPromises() + expect(getWarehouseTransfer).toHaveBeenCalledWith('CUSTOMER', 'WT-SOURCE') + const summary = wrapper.emitted('summary')!.at(-1)![0] + expect(summary).toMatchObject({ forecastBoxes: 4, boxes: 3, quantity: 27, weight: 10 }) + expect((summary as { volume: number }).volume).toBeCloseTo(.3) + const rows = wrapper.findAllComponents(DataTableStub)[0]!.props('data') + expect(rows[0]).toMatchObject({ quantity: '120', receivedQuantity: 20, receivedBoxes: 2, quantityDifference: -100, boxDifference: -1 }) + expect(rows[1]).toMatchObject({ receivedQuantity: 7, quantityDifference: -23, boxDifference: 0 }) + const tables = wrapper.findAll('.test-table') + expect(tables[0]!.text()).toContain('120') + expect(tables[1]!.text()).toContain('SKU-A × 20 件'); expect(tables[1]!.text()).toContain('SKU-B × 7 件') + expect(tables).toHaveLength(2) + await wrapper.findAll('button').find(button => button.text() === '查看箱号明细')!.trigger('click') + expect(wrapper.findAll('.test-table')[2]!.text()).not.toContain('BOX-MISSING') + await wrapper.findAll('button').find(button => button.text() === '箱内明细')!.trigger('click') + const box = wrapper.findAll('[data-test="box-dialog"]')[1]! + expect(box.text()).toContain('BOX-1'); expect(box.text()).toContain('产品 A'); expect(box.text()).toContain('"quantity":"8"') + expect(box.text()).not.toContain('120') + }) + it('shows zero receipts before signing without treating forecast cartons as actual receipts', async () => { + const value = fixture(); value.pallets = []; value.cartons.forEach(box => { box.status = 'EXPECTED' }) + vi.mocked(getWarehouseTransfer).mockResolvedValue(value) + const wrapper = render(); await flushPromises() + expect(wrapper.emitted('summary')!.at(-1)![0]).toMatchObject({ boxes: 0, quantity: 0 }) + expect(wrapper.findAll('.test-table')[0]!.text()).toContain('待签收') + expect(wrapper.findAll('.test-table')).toHaveLength(2) + expect(wrapper.findAll('button').find(button => button.text() === '查看箱号明细')!.attributes('disabled')).toBeDefined() + }) + it('shows signed positive and zero differences against each forecast row', async () => { + const value = fixture(); value.items[0]!.quantity = 10; value.items[0]!.packageCount = 2 + vi.mocked(getWarehouseTransfer).mockResolvedValue(value) + const wrapper = render(); await flushPromises() + const table = wrapper.findAll('.test-table')[0]! + expect(table.text()).toContain('+10') + expect(table.findAll('.receipt-difference').map(cell => cell.text())).toEqual(['+10', '-23']) + expect(wrapper.findAllComponents(DataTableStub)[0]!.props('data')[0]).toMatchObject({ quantityDifference: 10, boxDifference: 0 }) + }) + it('prints server label HTML with the selected customer and source reference', async () => { + vi.mocked(getWarehouseTransfer).mockResolvedValue(fixture()) + vi.mocked(getWarehouseTransferCartonLabel).mockResolvedValue({ transferNo: 'WT-SOURCE', cartons: fixture().cartons, printableHtml: 'SERVER-BOX-LABEL' }) + const popup = { opener: window, document: { open: vi.fn(), write: vi.fn(), close: vi.fn() }, focus: vi.fn(), print: vi.fn(), close: vi.fn() } + vi.spyOn(window, 'open').mockReturnValue(popup as unknown as Window) + const wrapper = render(); await flushPromises() + await wrapper.findAll('button').find(button => button.text() === '打印箱唛')!.trigger('click'); await flushPromises() + expect(getWarehouseTransferCartonLabel).toHaveBeenCalledExactlyOnceWith('CUSTOMER', 'WT-SOURCE') + expect(popup.document.write).toHaveBeenCalledWith('SERVER-BOX-LABEL') + expect(popup.print).toHaveBeenCalledOnce() + expect(popup.opener).toBeNull() + }) + it('closes the print window and leaves receipt details available when labels fail', async () => { + vi.mocked(getWarehouseTransfer).mockResolvedValue(fixture()) + vi.mocked(getWarehouseTransferCartonLabel).mockRejectedValue(new Error('标签暂不可用')) + const popup = { close: vi.fn(), print: vi.fn() } + vi.spyOn(window, 'open').mockReturnValue(popup as unknown as Window) + const wrapper = render(); await flushPromises() + await wrapper.findAll('button').find(button => button.text() === '打印箱唛')!.trigger('click'); await flushPromises() + expect(popup.close).toHaveBeenCalledOnce(); expect(popup.print).not.toHaveBeenCalled() + expect(wrapper.text()).toContain('箱唛加载失败,请重试') + expect(wrapper.text()).toContain('签收箱号明细(3 箱)') + }) + it('does not request another order when the source reference is missing', async () => { + const wrapper = render(''); await flushPromises() + expect(getWarehouseTransfer).not.toHaveBeenCalled() + expect(wrapper.text()).toContain('缺少客户或来源转运单号') + }) + it('discards a stale response after switching transfers', async () => { + let resolveOld!: (value: WarehouseTransferDetailDto) => void + vi.mocked(getWarehouseTransfer).mockImplementationOnce(() => new Promise(resolve => { resolveOld = resolve })).mockResolvedValueOnce({ ...fixture(), transferNo: 'WT-NEW', packageCount: 9 }) + const wrapper = render(); await wrapper.setProps({ transferNo: 'WT-NEW' }); await flushPromises() + resolveOld(fixture()); await flushPromises() + expect(wrapper.emitted('summary')!.at(-1)![0]).toMatchObject({ forecastBoxes: 9 }) + }) +}) diff --git a/src/modules/overseas-warehouse/components/WarehouseTransferReceiptPanel.vue b/src/modules/overseas-warehouse/components/WarehouseTransferReceiptPanel.vue new file mode 100644 index 00000000..8635fb06 --- /dev/null +++ b/src/modules/overseas-warehouse/components/WarehouseTransferReceiptPanel.vue @@ -0,0 +1,143 @@ + + + + + diff --git a/src/modules/overseas-warehouse/pages/CargoDiscardsView.vue b/src/modules/overseas-warehouse/pages/CargoDiscardsView.vue index f2bc133d..ace13fbf 100644 --- a/src/modules/overseas-warehouse/pages/CargoDiscardsView.vue +++ b/src/modules/overseas-warehouse/pages/CargoDiscardsView.vue @@ -14,9 +14,9 @@ import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam' import { overseasWarehousePermissions as permission } from '../permissions' import { useAccessPreviewStore } from '@/core/access/accessPreview' const access = useAccessPreviewStore(); const rows = ref([]); const customers = ref([]); const page = ref(1); const pageSize = ref(10); const total = ref(0); const loading = ref(false); const detailOpen = ref(false); const detail = ref(null); const filters = ref({ customerId: '', discardNo: '', inboundCode: '', status: '' }) -const fields: QueryField[] = [{ key: 'customerId', type: 'select', label: '客户', placeholder: '请选择客户', options: [] }, { key: 'discardNo', type: 'input', label: '废弃单号', placeholder: '废弃单号', width: 220 }, { key: 'inboundCode', type: 'input', label: '入仓码', placeholder: '请输入入仓码', width: 180 }, { key: 'status', type: 'select', label: '状态', placeholder: '全部状态', options: [{ label: '待审核', value: 'PENDING_REVIEW' }, { label: '已审核', value: 'APPROVED' }, { label: 'PDA处理中', value: 'PDA_COMPLETED' }, { label: '已完成', value: 'COMPLETED' }, { label: '已驳回', value: 'REJECTED' }, { label: '已取消', value: 'CANCELLED' }] }] +const fields: QueryField[] = [{ key: 'customerId', type: 'select', label: '客户', placeholder: '请选择客户', options: [] }, { key: 'discardNo', type: 'input', label: '废弃单号', placeholder: '废弃单号', width: 220 }, { key: 'inboundCode', type: 'input', label: '入仓码', placeholder: '请输入入仓码', width: 180 }, { key: 'status', type: 'select', label: '状态', placeholder: '全部状态', options: [{ label: '待审核', value: 'PENDING_REVIEW' }, { label: '已审核', value: 'APPROVED' }, { label: '待客户确认', value: 'PDA_COMPLETED' }, { label: '已完成', value: 'COMPLETED' }, { label: '已驳回', value: 'REJECTED' }, { label: '已取消', value: 'CANCELLED' }] }] const columns: DataTableColumn[] = [{ key: 'actions', label: '操作', width: 140, fixed: 'left', slotName: 'actions' }, { key: 'discardNo', label: '废弃单号', minWidth: 180 }, { key: 'inboundCodes', label: '入仓码', minWidth: 180 }, { key: 'overseasWarehouseName', label: '仓库', minWidth: 140 }, { key: 'totalPackages', label: '总箱数', width: 90 }, { key: 'totalFee', label: '废弃费用', width: 120, formatter: (row: CargoDiscardSummary) => `${row.currency || 'USD'} ${row.totalFee ?? 0}` }, { key: 'createdBy', label: '申请人', width: 110 }, { key: 'createdAt', label: '创建时间', minWidth: 170 }, { key: 'status', label: '状态', width: 150, slotName: 'status' }] -const statusLabels: Record = { PENDING_REVIEW: '待审核', APPROVED: '已审核,待 PDA', PDA_COMPLETED: 'PDA 已完成,待仓库确认', COMPLETED: '已完成', REJECTED: '已驳回', CANCELLED: '已取消' } +const statusLabels: Record = { PENDING_REVIEW: '待审核', APPROVED: '已审核,待 PDA', PDA_COMPLETED: 'PDA 已完成,待客户确认', COMPLETED: '已完成', REJECTED: '已驳回', CANCELLED: '已取消' } const photoUrls = reactive>({}); const photoLoading = reactive>({}); const photoErrors = reactive>({}) const photoPreviewUrls = computed(() => (detail.value?.photos || []).map(photo => photoUrls[photo.fileId]).filter(Boolean)) let photoGeneration = 0 diff --git a/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.test.ts b/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.test.ts index d8a44f82..0b9c8225 100644 --- a/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.test.ts +++ b/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.test.ts @@ -3,9 +3,11 @@ import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { getLastMileOrder } from '../api/lastMileOrders' import LastMileOrderDetailView from './LastMileOrderDetailView.vue' +import { getPlatformWarehouse } from '@/modules/warehouses' import { getWarehouseTransfer } from '../api/warehouseTransfers' const push = vi.hoisted(() => vi.fn()) +vi.mock('@/modules/warehouses', () => ({ getPlatformWarehouse: vi.fn() })) vi.mock('vue-router', () => ({ useRoute: () => ({ params: { orderId: 'internal-order-id' } }), useRouter: () => ({ push }) })) vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => false }) })) vi.mock('../api/lastMileOrders', () => ({ getLastMileOrder: vi.fn(), createLastMileOrderFee: vi.fn() })) @@ -63,9 +65,34 @@ describe('LastMileOrderDetailView domestic detail alignment', () => { expect(wrapper.get('[aria-label="本票转运与换标履约"]').text()).toContain('不得提前出库') }) + it('fills the receiving warehouse from its ID when the order name is absent', async () => { + vi.mocked(getLastMileOrder).mockResolvedValue({ order: { id: 'tail', customerId: 'C1', orderNo: 'WT-TAIL', orderType: 'WAREHOUSE_TRANSFER', overseasWarehouseId: '193000000000200003' }, cargo: [], fees: [], statusHistory: [], operationHistory: [] }) + vi.mocked(getPlatformWarehouse).mockResolvedValue({ warehouse: { id: '193000000000200003', warehouseName: '洛杉矶收货仓', warehouseCode: 'US-LAX-01', tenantId: '0', internalCode: null, warehouseType: 'OVERSEAS', countryCode: 'US', timezone: 'America/Los_Angeles', contactName: null, contactPhone: null, contactEmail: null, status: 'ACTIVE', remark: null }, addresses: [], capabilities: [] }) + const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [ElementPlus] } }); await flushPromises() + expect(getPlatformWarehouse).toHaveBeenCalledWith('193000000000200003') + expect(wrapper.get('[aria-label="尾程订单概览"]').text()).toContain('海外仓洛杉矶收货仓') + expect(wrapper.get('[aria-label="基本信息"]').text()).toContain('海外仓洛杉矶收货仓') + }) + + it('loads transfer declarations and actual cartons on the cargo tab using the source transfer', async () => { + vi.mocked(getLastMileOrder).mockResolvedValue({ order: { id: 'tail', customerId: 'customer-1', orderNo: 'TAIL-NO', sourceOrderNo: 'WT-SOURCE', orderType: 'WAREHOUSE_TRANSFER' }, cargo: [], fees: [], statusHistory: [], operationHistory: [] }) + vi.mocked(getWarehouseTransfer).mockResolvedValue({ id: 'transfer', transferNo: 'WT-SOURCE', status: 'WAIT_PDA_RECEIVING', needRelabel: false, packageCount: 5, items: [], cartons: [], pallets: [], relabelBindings: [] }) + const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [ElementPlus] } }); await flushPromises() + expect(getWarehouseTransfer).not.toHaveBeenCalled() + await wrapper.get('#tab-cargo').trigger('click'); await flushPromises() + expect(getWarehouseTransfer).toHaveBeenCalledExactlyOnceWith('customer-1', 'WT-SOURCE') + const panel = wrapper.get('[aria-label="仓储转运预报与实际签收"]') + expect(wrapper.get('[aria-label="尾程订单概览"]').text()).toContain('预报箱数5 箱') + expect(wrapper.get('[aria-label="尾程订单概览"]').text()).toContain('实收箱数0 箱') + expect(panel.text()).toContain('预报与实际签收对比') + expect(panel.text()).toContain('签收箱号明细(0 箱)') + expect(panel.text()).toContain('查看箱号明细') + }) + beforeEach(() => { push.mockReset() vi.mocked(getWarehouseTransfer).mockReset() + vi.mocked(getPlatformWarehouse).mockReset() vi.mocked(getLastMileOrder).mockResolvedValue({ order: { id: 'internal-order-id', customerId: 'customer-1', orderNo: 'FL-WH-001', currentStatus: 'PENDING_ASSIGNMENT', customerName: '示例客户', overseasWarehouseName: '洛杉矶仓', packageCount: 0, palletCount: 2, grossWeightKg: 30, netWeightKg: 22, volumeCbm: 6.374, inboundCode: 'IN-001', containerNo: 'CONT-001', customerRemark: '完整保留客户备注', updatedAt: '2026-09-14T10:00:00' }, cargo: [{ id: 'cargo-1', skuCode: 'SKU-001', productNameZh: '测试货物' }], fees: [], statusHistory: [], operationHistory: [], diff --git a/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.vue b/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.vue index 7e3ecc76..05c23194 100644 --- a/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.vue +++ b/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.vue @@ -1,10 +1,11 @@