diff --git a/src/modules/overseas-warehouse/API_INTEGRATION_MATRIX.md b/src/modules/overseas-warehouse/API_INTEGRATION_MATRIX.md index be23d528..b977165e 100644 --- a/src/modules/overseas-warehouse/API_INTEGRATION_MATRIX.md +++ b/src/modules/overseas-warehouse/API_INTEGRATION_MATRIX.md @@ -4,9 +4,9 @@ | 页面/能力 | Method + Path | Params / Body(当前 adapter) | 权限与承载 | |---|---|---|---| -| 尾程列表/详情 | `GET /platform/v1/last-leg/orders`;`GET /orders/{id}` | list query:`page,size,orderNo,orderType,warehouse,inboundCode,sourceOrderNo,containerNo,currentStatus,transferWarehouseCode,abnormal,detained,createdFrom,createdTo`;平台跨客户,不传 `customerId` | `platform:overseas-warehouse:last-mile-order:list`;真实分页、空态、错误及 request/trace 元数据;详情只读展示离仓关联事实 | +| 尾程列表/详情 | `GET /platform/v1/last-leg/orders`;`GET /orders/{id}` | list query:`page,size,orderNo,orderType,warehouse,inboundCode,sourceOrderNo,containerNo,currentStatus,transferWarehouseCode,abnormal,detained,createdFrom,createdTo`;平台跨客户,不传 `customerId`;货主只消费响应 `customerName`,空值显示 `—` | `platform:overseas-warehouse:last-mile-order:list`;真实分页、空态、错误及 request/trace 元数据;详情只读展示离仓关联事实 | | 尾程转目的仓 | `PUT /platform/v1/last-leg/orders/destination` | body:`orderIds:string[],warehouseId,warehouseCode,warehouseName,remark?` | `...:last-mile-order:edit`;失败保留弹框,不提示成功 | -| 尾程转入仓计划 | `POST /platform/v1/last-leg/inbound-plans/convert` | body:`orderId:string`,不传客户 | `...:inbound-order:create`;不接补托/PDA | +| 尾程转入仓计划 | `POST /platform/v1/last-leg/inbound-plans/convert` | body:`orderId:string,customerId:string`,客户作用域精确取自所选尾程行;缺失时前端失败关闭 | `...:inbound-order:create`;A0404/A0409 保留 code/requestId/traceId;不接补托/PDA | | 入库列表 | `GET /platform/v1/last-leg/inbound-plans` | query:`page,size,planNo,inboundCode,status,warehouse,fbaWarehouseCode,appointmentStatus,dispatchStatus,createdFrom,createdTo`;平台跨客户 | `...:inbound-order:list`;`lineId/planId/orderId/customerId` 均为字符串 | | 入库详情 | `GET /platform/v1/last-leg/inbound-plans/{planNo}` | query:显式 `customerId`;path 编码 | 同 list;缺客户范围时页面明确报错;`sourceOrderId` 字符串化 | | FBA 预约新增 | `POST /platform/v1/last-leg/inbound-plans/fba-appointments/batch` | body:`customerId,lineIds:string[],appointmentNo,appointmentAt,expectedLoadingAt,appointmentStatus,appointmentRemark?` | `...:inbound-order:fba-appointment`;只允许同客户、同 FBA 仓勾选 | diff --git a/src/modules/overseas-warehouse/api/lastMileOrders.test.ts b/src/modules/overseas-warehouse/api/lastMileOrders.test.ts index 018f9bb0..19b38bbc 100644 --- a/src/modules/overseas-warehouse/api/lastMileOrders.test.ts +++ b/src/modules/overseas-warehouse/api/lastMileOrders.test.ts @@ -8,8 +8,9 @@ describe('last-mile order overseas API', () => { beforeEach(() => vi.mocked(overseasHttp.get).mockReset()) it('loads all platform orders without a customer scope', async () => { - vi.mocked(overseasHttp.get).mockResolvedValue({ code: '200', message: 'success', data: { records: [], page: '2', size: '20', total: '0', pages: '0' } }) - await expect(listLastMileOrders({ page: 2, size: 20, orderType: 'FIRST_LEG', currentStatus: 'PENDING_ASSIGNMENT', warehouse: 'LAX' })).resolves.toEqual({ records: [], page: 2, size: 20, total: 0, pages: 0 }) + const record = { id: '190000000000000099', customerId: '190000000000000001', customerName: '华远贸易', orderNo: 'LMO-260827-0001' } + vi.mocked(overseasHttp.get).mockResolvedValue({ code: '200', message: 'success', data: { records: [record], page: '2', size: '20', total: '1', pages: '1' } }) + await expect(listLastMileOrders({ page: 2, size: 20, orderType: 'FIRST_LEG', currentStatus: 'PENDING_ASSIGNMENT', warehouse: 'LAX' })).resolves.toEqual({ records: [record], page: 2, size: 20, total: 1, pages: 1 }) expect(overseasHttp.get).toHaveBeenCalledWith('/platform/v1/last-leg/orders', { params: { page: 2, size: 20, orderType: 'FIRST_LEG', currentStatus: 'PENDING_ASSIGNMENT', warehouse: 'LAX' }, }) @@ -47,10 +48,11 @@ describe('last-mile order overseas API', () => { expect(overseasHttp.put).toHaveBeenCalledWith('/platform/v1/last-leg/orders/destination', expect.objectContaining({ orderIds: ['190000000000000099'] })) }) - it('uses the inbound-plan conversion endpoint without customerId', async () => { + it('uses the inbound-plan conversion endpoint with the exact selected customer scope', async () => { vi.mocked(overseasHttp.post).mockResolvedValue({ code: '200', message: 'success', data: {} }) - await convertLastMileOrderToInboundPlan({ orderId: '190000000000000099' }) - expect(overseasHttp.post).toHaveBeenCalledWith('/platform/v1/last-leg/inbound-plans/convert', { orderId: '190000000000000099' }) - expect(JSON.stringify(vi.mocked(overseasHttp.post).mock.calls[0]?.[1])).not.toContain('customerId') + await convertLastMileOrderToInboundPlan({ orderId: '190000000000000099', customerId: '190000000000000001' }) + expect(overseasHttp.post).toHaveBeenCalledWith('/platform/v1/last-leg/inbound-plans/convert', { + orderId: '190000000000000099', customerId: '190000000000000001', + }) }) }) diff --git a/src/modules/overseas-warehouse/api/lastMileOrders.ts b/src/modules/overseas-warehouse/api/lastMileOrders.ts index 18dad487..329a53fb 100644 --- a/src/modules/overseas-warehouse/api/lastMileOrders.ts +++ b/src/modules/overseas-warehouse/api/lastMileOrders.ts @@ -150,6 +150,7 @@ export interface TransferDestinationPayload { export interface ConvertInboundPlanPayload { orderId: string + customerId: string } export interface LastMileOrderDetailDto { diff --git a/src/modules/overseas-warehouse/components/OrderReferenceText.test.ts b/src/modules/overseas-warehouse/components/OrderReferenceText.test.ts new file mode 100644 index 00000000..81855ab4 --- /dev/null +++ b/src/modules/overseas-warehouse/components/OrderReferenceText.test.ts @@ -0,0 +1,26 @@ +import { mount } from '@vue/test-utils' +import { describe, expect, it } from 'vitest' +import OrderReferenceText from './OrderReferenceText.vue' + +describe('OrderReferenceText', () => { + it.each([ + ['desktop-1920', '170px'], + ['narrow', '88px'], + ])('keeps the complete server order number in %s layouts', (_viewport, width) => { + const orderNo = 'LEGACY-LAST-MILE-ORDER-202608270000000001' + const wrapper = mount({ + components: { OrderReferenceText }, + template: `
`, + }) + + const reference = wrapper.get('.order-reference-text') + expect(reference.text()).toBe(orderNo) + expect(reference.attributes('title')).toBe(orderNo) + }) + + it('renders the new short number in full and does not guess from another identifier', () => { + const wrapper = mount(OrderReferenceText, { props: { value: 'LMO-260827-0001' } }) + expect(wrapper.text()).toBe('LMO-260827-0001') + expect(wrapper.attributes('title')).toBe('LMO-260827-0001') + }) +}) diff --git a/src/modules/overseas-warehouse/components/OrderReferenceText.vue b/src/modules/overseas-warehouse/components/OrderReferenceText.vue new file mode 100644 index 00000000..3f4576dc --- /dev/null +++ b/src/modules/overseas-warehouse/components/OrderReferenceText.vue @@ -0,0 +1,30 @@ + + + + + diff --git a/src/modules/overseas-warehouse/components/OverseasDetailShell.vue b/src/modules/overseas-warehouse/components/OverseasDetailShell.vue index 9924aaec..5d7d73b2 100644 --- a/src/modules/overseas-warehouse/components/OverseasDetailShell.vue +++ b/src/modules/overseas-warehouse/components/OverseasDetailShell.vue @@ -22,7 +22,7 @@ defineEmits<{ back: [] }>() 返回

{{ title }}

-

{{ referenceLabel }} {{ referenceValue }}

+

{{ referenceLabel }} {{ referenceValue }}

{{ statusText }} @@ -37,8 +37,8 @@ defineEmits<{ back: [] }>() .overseas-detail-head { display: flex; min-height: 44px; align-items: center; gap: 12px; } .overseas-detail-title { min-width: 0; flex: 1; } .overseas-detail-title h1 { margin: 0; color: var(--cc-text); font-size: 20px; line-height: 1.35; } -.overseas-detail-title p { margin: 3px 0 0; color: var(--cc-muted); font-size: 12px; line-height: 1.5; } -.overseas-detail-title strong { color: var(--cc-text); overflow-wrap: anywhere; } +.overseas-detail-title p { display: flex; min-width: 0; align-items: baseline; gap: 4px; margin: 3px 0 0; color: var(--cc-muted); font-size: 12px; line-height: 1.5; } +.overseas-detail-title strong { min-width: 0; max-width: 100%; color: var(--cc-text); } .overseas-detail-status { display: inline-flex; min-height: 26px; align-items: center; padding: 3px 10px; border: 1px solid #f3d19e; border-radius: 999px; background: #fdf6ec; color: #b88230; font-size: 12px; font-weight: 600; white-space: nowrap; } .overseas-detail-status.is-live { border-color: var(--el-color-success-light-7); background: var(--el-color-success-light-9); color: var(--el-color-success-dark-2); } diff --git a/src/modules/overseas-warehouse/components/SmartDispatchDialog.vue b/src/modules/overseas-warehouse/components/SmartDispatchDialog.vue index e1a414e0..30af8143 100644 --- a/src/modules/overseas-warehouse/components/SmartDispatchDialog.vue +++ b/src/modules/overseas-warehouse/components/SmartDispatchDialog.vue @@ -10,6 +10,7 @@ import LoadErrorState from '@/shared/components/LoadErrorState.vue' import OperationErrorToast from '@/shared/components/OperationErrorToast.vue' import { createDispatchDraftContainer, deleteDispatchDraftContainer, listDispatchCandidates, listDispatchDrafts, lockDispatchDraftPallet, preArrangeDispatch, releaseDispatchDraft, unlockDispatchDraftPallet, type DispatchPalletCandidateDto } from '../api/dispatches' import type { InboundOrderListItem } from '../types' +import OrderReferenceText from './OrderReferenceText.vue' type VehicleSpec = { value: string; label: string; capacity: number; maxWeightLb: number } interface LoadingContainer { id: string; capacity: number; maxWeightLb: number; vehicleLabel: string; palletIds: string[] } @@ -211,7 +212,7 @@ watch([warehouseTab, () => query.orderType], () => {

出库单列表 ({{ orderGroups.length }}单)

- + @@ -225,7 +226,7 @@ watch([warehouseTab, () => query.orderType], () => {
-

托盘明细 {{ activePallets.length ? `(${activePallets.length}托)` : '' }}

订单号:{{ text(activePallets[0]?.orderNo) }}
+

托盘明细 {{ activePallets.length ? `(${activePallets.length}托)` : '' }}

订单号:
@@ -246,7 +247,7 @@ watch([warehouseTab, () => query.orderType], () => {
{{ container.vehicleLabel }}托数 {{ container.palletIds.length }}/{{ container.capacity }} · 重量 {{ number(containerWeightLb(container), 0) }}LB/{{ container.maxWeightLb }}LB 超限10%
-
{{ text(palletSnapshot(id)?.palletNo || id) }}{{ text(palletSnapshot(id)?.orderNo) }}移除
+
{{ text(palletSnapshot(id)?.palletNo || id) }}移除
拖入托盘,移入后立即锁定
@@ -276,6 +277,7 @@ watch([warehouseTab, () => query.orderType], () => { .dispatch-workbench { display: grid; grid-template-columns: minmax(550px, 1.5fr) minmax(330px, .85fr) minmax(390px, 1fr); gap: 16px; margin-top: 16px; } .order-pane, .detail-pane, .container-pane { min-width: 0; } .order-pane header, .detail-pane header, .container-pane header { display: flex; min-height: 74px; align-items: center; justify-content: space-between; gap: 10px; } +.detail-order-reference { display: flex; min-width: 0; max-width: 240px; align-items: baseline; gap: 3px; } h3, p { margin: 0; } h3 { font-size: 16px; } h3 span, header > span, p { color: var(--cc-muted); font-size: 12px; font-weight: 400; } .order-type-tabs { display: flex; gap: 6px; margin-top: 10px; overflow-x: auto; } .order-type-tabs button { flex: 0 0 auto; padding: 5px 12px; border-radius: 16px; font-size: 12px; } @@ -297,7 +299,7 @@ h3, p { margin: 0; } h3 { font-size: 16px; } h3 span, header > span, p { color: .container-head span { color: var(--cc-muted); font-size: 12px; }.container-head b { color: var(--el-color-primary); } .container-body { min-height: 76px; padding: 8px; }.drop-hint { display: grid; min-height: 60px; place-items: center; color: var(--cc-muted); font-size: 12px; } .loaded-pallet { display: flex; min-height: 52px; align-items: center; justify-content: space-between; gap: 8px; border-bottom: 1px solid var(--cc-border); } -.loaded-pallet > span { display: flex; min-width: 0; flex-direction: column; color: var(--cc-muted); font-size: 12px; }.loaded-pallet strong { color: var(--cc-text); font-size: 13px; } +.loaded-pallet > span { display: flex; min-width: 0; flex: 1; flex-direction: column; color: var(--cc-muted); font-size: 12px; }.loaded-pallet strong { color: var(--cc-text); font-size: 13px; } .dialog-footer { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: 16px -20px -20px; padding: 14px 20px; border-top: 1px solid var(--cc-border); background: #fafbfc; } .dialog-footer > span { color: var(--cc-muted); font-size: 12px; } .vehicle-options { display: grid; gap: 14px; } diff --git a/src/modules/overseas-warehouse/lastMilePresentation.test.ts b/src/modules/overseas-warehouse/lastMilePresentation.test.ts index 04fa6365..2e8bffbf 100644 --- a/src/modules/overseas-warehouse/lastMilePresentation.test.ts +++ b/src/modules/overseas-warehouse/lastMilePresentation.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { toLastMileOrderListItem } from './lastMilePresentation' +import { formatOverseasDateTime, toLastMileOrderListItem } from './lastMilePresentation' describe('last-mile order presentation adapter', () => { it('maps backend codes, associations and long ids without inventing data', () => { @@ -14,6 +14,28 @@ describe('last-mile order presentation adapter', () => { expect(row.rejectedAt).toBe('—') }) + it('shows the backend customerName and never guesses the owner from customerId', () => { + expect(toLastMileOrderListItem({ + id: '1', customerId: '190000000000000001', customerName: '华远贸易', orderNo: 'LMO-260827-0001', + }).owner).toBe('华远贸易') + expect(toLastMileOrderListItem({ + id: '2', customerId: '190000000000000002', customerName: null, orderNo: 'LMO-260827-0002', + }).owner).toBe('—') + }) + + it('preserves real metrics and distinguishes zero from unknown fields and times', () => { + const estimatedArrivalAt = '2026-08-27T12:30:00Z' + const row = toLastMileOrderListItem({ + id: '3', customerId: '190000000000000003', orderNo: 'LMO-260827-0003', + palletCount: 3, packageCount: 0, volumeCbm: 12.75, productQuantity: null, volumeWeightKg: 88.6, + estimatedArrivalAt, actualArrivalAt: null, estimatedWarehouseAt: null, + }) + expect(row).toMatchObject({ + pallets: 3, cartons: 0, volume: 12.75, quantity: '—', volumeWeight: 88.6, + portEta: formatOverseasDateTime(estimatedArrivalAt), portAta: '—', warehouseEta: '—', + }) + }) + it.each([ ['RELABEL_APPLICATION', '换标下单'], ['WAREHOUSE_TRANSFER', '仓储转运'], diff --git a/src/modules/overseas-warehouse/lastMilePresentation.ts b/src/modules/overseas-warehouse/lastMilePresentation.ts index f2f9a6a1..2588b24a 100644 --- a/src/modules/overseas-warehouse/lastMilePresentation.ts +++ b/src/modules/overseas-warehouse/lastMilePresentation.ts @@ -27,8 +27,8 @@ function text(value: unknown) { return normalized || '—' } -function count(value: number | null | undefined) { - return Number.isFinite(value) ? Number(value) : 0 +function metric(value: number | null | undefined): number | '—' { + return value != null && Number.isFinite(value) ? Number(value) : '—' } function yesNo(value: boolean | null | undefined) { @@ -69,11 +69,11 @@ export function toLastMileOrderListItem(dto: LastMileOrderSummaryDto): LastMileO containerStatus: text(dto.containerStatus), abnormal: yesNo(dto.abnormal), held: yesNo(dto.detained), - pallets: count(dto.palletCount), - cartons: count(dto.packageCount), - volume: count(dto.volumeCbm), - quantity: count(dto.productQuantity), - volumeWeight: count(dto.volumeWeightKg), + pallets: metric(dto.palletCount), + cartons: metric(dto.packageCount), + volume: metric(dto.volumeCbm), + quantity: metric(dto.productQuantity), + volumeWeight: metric(dto.volumeWeightKg), portEta: formatOverseasDateTime(dto.estimatedArrivalAt), portAta: formatOverseasDateTime(dto.actualArrivalAt), warehouseEta: formatOverseasDateTime(dto.estimatedWarehouseAt), diff --git a/src/modules/overseas-warehouse/pages/CustomerVisitsView.vue b/src/modules/overseas-warehouse/pages/CustomerVisitsView.vue index 6858ce23..ccac3c1a 100644 --- a/src/modules/overseas-warehouse/pages/CustomerVisitsView.vue +++ b/src/modules/overseas-warehouse/pages/CustomerVisitsView.vue @@ -23,6 +23,7 @@ import { type CustomerVisitRow, } from '../api/customerVisits' import { overseasWarehousePermissions as permission } from '../permissions' +import OrderReferenceText from '../components/OrderReferenceText.vue' const access = useAccessPreviewStore() const rows = ref([]) @@ -188,7 +189,7 @@ onMounted(load)
@@ -216,6 +217,7 @@ onMounted(load) .visit-order-summary div { display: grid; gap: 5px; } .visit-order-summary span { color: var(--cc-text-secondary); } .visit-order-summary strong { color: var(--cc-text-primary); } .inbound-options { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; width: 100%; } .inbound-option { display: grid; gap: 6px; padding: 12px; border: 1px solid var(--cc-border-color); border-radius: 8px; cursor: pointer; color: var(--cc-text-secondary); } +.visit-order-reference { display: flex; min-width: 0; align-items: baseline; gap: 3px; } .inbound-option.selected { border-color: var(--el-color-primary); background: var(--el-color-primary-light-9); } .file-picker { display: inline-flex; align-items: center; gap: 6px; padding: 8px 12px; color: var(--el-color-primary); border: 1px solid var(--el-color-primary); border-radius: 6px; cursor: pointer; } .file-picker input { display: none; } .file-picker.disabled { opacity: .6; cursor: not-allowed; } .file-tip { margin-left: 12px; color: var(--cc-text-secondary); } diff --git a/src/modules/overseas-warehouse/pages/ExceptionOrdersView.vue b/src/modules/overseas-warehouse/pages/ExceptionOrdersView.vue index 2506fcf6..284a59fb 100644 --- a/src/modules/overseas-warehouse/pages/ExceptionOrdersView.vue +++ b/src/modules/overseas-warehouse/pages/ExceptionOrdersView.vue @@ -15,6 +15,7 @@ import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http' import { formatOverseasDateTime } from '../lastMilePresentation' import { overseasWarehousePermissions as permission } from '../permissions' import { createExceptionOrder, listExceptionOrders, resolveExceptionRelation, type ExceptionOrderRow, type ExceptionRelation } from '../api/exceptionOrders' +import OrderReferenceText from '../components/OrderReferenceText.vue' const rows = ref([]); const page = ref(1); const pageSize = ref(10); const total = ref(0); const loading = ref(false); const error = ref(null) const access = useAccessPreviewStore() @@ -73,7 +74,7 @@ onMounted(load)
- +
@@ -87,13 +88,13 @@ onMounted(load)
- + diff --git a/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.vue b/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.vue index 93c16e88..eca3d512 100644 --- a/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.vue +++ b/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.vue @@ -14,6 +14,7 @@ import { createLastMileOrderFee, getLastMileOrder, type LastMileOrderDetailDto } import { formatOverseasDateTime, lastMileOrderTypeLabel, lastMileStatusLabel } from '../lastMilePresentation' import { overseasWarehousePermissions as permission } from '../permissions' import OverseasDetailShell from '../components/OverseasDetailShell.vue' +import OrderReferenceText from '../components/OrderReferenceText.vue' interface DetailField { label: string; value: string | number } interface CargoRow { id: string; sku: string; productName: string; pallets: number; cartons: number; quantity: number; weight: number; volume: number } @@ -150,6 +151,7 @@ onMounted(load) + + +
+
diff --git a/src/modules/overseas-warehouse/pages/realApiMount.test.ts b/src/modules/overseas-warehouse/pages/realApiMount.test.ts index 2fffa4c0..e194e034 100644 --- a/src/modules/overseas-warehouse/pages/realApiMount.test.ts +++ b/src/modules/overseas-warehouse/pages/realApiMount.test.ts @@ -1,17 +1,21 @@ import ElementPlus from 'element-plus' +import { createPinia } from 'pinia' import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils' import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { listPlatformTenants } from '@/modules/iam' import { getInboundPlan } from '../api/inboundPlans' import { listInventoryLedger } from '../api/inventory' +import { getLastMileOrder } from '../api/lastMileOrders' import InboundOrderDetailView from './InboundOrderDetailView.vue' import InventoryTransactionsView from './InventoryTransactionsView.vue' +import LastMileOrderDetailView from './LastMileOrderDetailView.vue' const routing = vi.hoisted(() => ({ push: vi.fn(), route: { params: {} as Record, query: {} as Record } })) vi.mock('vue-router', () => ({ useRoute: () => routing.route, useRouter: () => ({ push: routing.push }) })) vi.mock('@/modules/iam', () => ({ listPlatformTenants: vi.fn() })) vi.mock('../api/inboundPlans', () => ({ getInboundPlan: vi.fn() })) vi.mock('../api/inventory', () => ({ listInventoryLedger: vi.fn() })) +vi.mock('../api/lastMileOrders', () => ({ getLastMileOrder: vi.fn(), createLastMileOrderFee: vi.fn() })) class ResizeObserverStub implements ResizeObserver { observe() {} unobserve() {} disconnect() {} } function memoryStorage(): Storage { const values = new Map(); return { get length() { return values.size }, clear: () => values.clear(), getItem: (key) => values.get(key) ?? null, key: (index) => [...values.keys()][index] ?? null, removeItem: (key) => values.delete(key), setItem: (key, value) => values.set(key, value) } } enableAutoUnmount(afterEach); afterAll(() => vi.unstubAllGlobals()) @@ -22,7 +26,9 @@ describe('overseas warehouse mounted real API pages', () => { vi.mocked(listPlatformTenants).mockReset().mockResolvedValue({ records: [{ id: '10001', tenantName: '客户甲', status: 'ACTIVE' } as never], page: 1, size: 200, total: 1, pages: 1 }) vi.mocked(getInboundPlan).mockReset().mockResolvedValue({ id: '9007199254740993', planNo: 'IP-1', customerId: '10001', sourceOrderId: '9007199254740995', status: 'PENDING_ASSIGNMENT', lineCount: 1, palletCount: 1, totalVolumeCbm: 2, lines: [{ id: '1', orderId: '9007199254740995', inboundCode: 'IN-1', fbaWarehouseCode: 'ONT8', destinationWarehouseName: '洛杉矶仓', volumeCbm: 2, requiredPackageCount: 10, plannedPalletCount: 1 }], pallets: [], allocations: [] }) vi.mocked(listInventoryLedger).mockReset().mockResolvedValue({ records: [{ id: '9007199254740997', customerId: '10001', destinationWarehouseCode: 'LAX', inboundOrderNo: 'IP-1', businessType: 'STOCK_IN', beforePackages: 0, deltaPackages: 10, afterPackages: 10, occurredAt: '2026-08-20T10:00:00' }], page: 1, size: 20, total: 1, pages: 1 }) + vi.mocked(getLastMileOrder).mockReset().mockResolvedValue({ order: { id: '9007199254740999', customerId: '10001', customerName: '华远贸易', orderNo: 'LMO-260827-0001', currentStatus: 'PENDING_ASSIGNMENT' }, cargo: [], statusHistory: [], fees: [], operationHistory: [] }) }) it('mounts inbound detail with a string-safe source order id', async () => { routing.route.params = { orderId: 'IP-1' }; routing.route.query = { customerId: '10001' }; const wrapper = mount(InboundOrderDetailView, { global: { plugins: [ElementPlus] } }); await flushPromises(); expect(getInboundPlan).toHaveBeenCalledWith('IP-1', '10001'); expect(wrapper.text()).toContain('9007199254740995'); expect(wrapper.text()).toContain('洛杉矶仓') }) it('mounts inventory ledger as read-only facts', async () => { const wrapper = mount(InventoryTransactionsView, { global: { plugins: [ElementPlus] } }); await flushPromises(); expect(listInventoryLedger).toHaveBeenCalledWith(expect.objectContaining({ page: 1, size: 10 })); expect(listInventoryLedger).not.toHaveBeenCalledWith(expect.objectContaining({ customerId: expect.anything() })); expect(wrapper.text()).toContain('IP-1'); expect(wrapper.text()).toContain('10'); expect(wrapper.text()).toContain('不提供出库单跳转') }) + it('renders the backend owner and complete short order number in last-mile detail', async () => { routing.route.params = { orderId: '9007199254740999' }; const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [createPinia(), ElementPlus] } }); await flushPromises(); expect(getLastMileOrder).toHaveBeenCalledWith('9007199254740999'); expect(wrapper.text()).toContain('华远贸易'); expect(wrapper.text()).toContain('LMO-260827-0001'); expect(wrapper.get('.order-reference-text').attributes('title')).toBe('LMO-260827-0001') }) }) diff --git a/src/modules/overseas-warehouse/types.ts b/src/modules/overseas-warehouse/types.ts index 8cd05959..295ce1d0 100644 --- a/src/modules/overseas-warehouse/types.ts +++ b/src/modules/overseas-warehouse/types.ts @@ -19,11 +19,11 @@ export interface LastMileOrderListItem { containerStatus: string abnormal: string held: string - pallets: number - cartons: number - volume: number - quantity: number - volumeWeight: number + pallets: number | '—' + cartons: number | '—' + volume: number | '—' + quantity: number | '—' + volumeWeight: number | '—' portEta: string portAta: string warehouseEta: string