From da92b75ae19a6f248c896560605b29460039124e Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Sun, 30 Aug 2026 17:35:52 +0800 Subject: [PATCH] feat(client): show inbound business relationships --- .../api/inboundCodeAssociations.test.mjs | 19 +++ .../first-leg/api/inboundCodeAssociations.ts | 22 ++++ .../components/CustomsDocumentsDialog.vue | 4 +- .../InboundCodeAssociationDialog.test.mjs | 32 +++++ .../InboundCodeAssociationDialog.vue | 115 ++++++++++++++++++ .../views/FirstLegOrdersView.test.mjs | 17 ++- .../first-leg/views/FirstLegOrdersView.vue | 55 ++++++--- .../first-leg/views/QuickOrderView.test.mjs | 5 +- .../first-leg/views/QuickOrderView.vue | 8 +- .../views/businessIdentifiers.test.mjs | 27 ++++ .../views/WarehouseServiceView.vue | 14 +-- 11 files changed, 288 insertions(+), 30 deletions(-) create mode 100644 src/modules/first-leg/api/inboundCodeAssociations.test.mjs create mode 100644 src/modules/first-leg/api/inboundCodeAssociations.ts create mode 100644 src/modules/first-leg/components/InboundCodeAssociationDialog.test.mjs create mode 100644 src/modules/first-leg/components/InboundCodeAssociationDialog.vue create mode 100644 src/modules/first-leg/views/businessIdentifiers.test.mjs diff --git a/src/modules/first-leg/api/inboundCodeAssociations.test.mjs b/src/modules/first-leg/api/inboundCodeAssociations.test.mjs new file mode 100644 index 0000000..f2b4173 --- /dev/null +++ b/src/modules/first-leg/api/inboundCodeAssociations.test.mjs @@ -0,0 +1,19 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const source = readFileSync(new URL('./inboundCodeAssociations.ts', import.meta.url), 'utf8') + +test('queries the customer-scoped inbound-code association without browser-selected customer scope', () => { + assert.match(source, /method: 'GET'/) + assert.match(source, /`\/merchant\/v1\/orders\/first-leg\/inbound-code-associations\/\$\{encodeURIComponent\(inboundCode\.trim\(\)\)\}`/) + assert.doesNotMatch(source, /params:[\s\S]*customerId/) +}) + +test('keeps internal association identifiers as strings without making them display keys', () => { + for (const field of ['orderId', 'customerId']) assert.match(source, new RegExp(`${field}: string`)) + assert.match(source, /destinationWarehouseId: string \| null/) + for (const field of ['inboundCode', 'orderNo', 'customerName', 'destinationWarehouseCode', 'destinationWarehouseName']) { + assert.match(source, new RegExp(`${field}: string`)) + } +}) diff --git a/src/modules/first-leg/api/inboundCodeAssociations.ts b/src/modules/first-leg/api/inboundCodeAssociations.ts new file mode 100644 index 0000000..48b9392 --- /dev/null +++ b/src/modules/first-leg/api/inboundCodeAssociations.ts @@ -0,0 +1,22 @@ +import { apiRequest } from '@/api/http' + +export interface MerchantInboundCodeAssociation { + inboundCode: string + orderNo: string + orderStatus: string + orderId: string + customerId: string + customerName: string + destinationType: string + destinationCountryCode: string | null + destinationWarehouseId: string | null + destinationWarehouseCode: string | null + destinationWarehouseName: string | null +} + +export function getMerchantInboundCodeAssociation(inboundCode: string) { + return apiRequest({ + method: 'GET', + url: `/merchant/v1/orders/first-leg/inbound-code-associations/${encodeURIComponent(inboundCode.trim())}`, + }) +} diff --git a/src/modules/first-leg/components/CustomsDocumentsDialog.vue b/src/modules/first-leg/components/CustomsDocumentsDialog.vue index fe048e4..0a975dd 100644 --- a/src/modules/first-leg/components/CustomsDocumentsDialog.vue +++ b/src/modules/first-leg/components/CustomsDocumentsDialog.vue @@ -240,7 +240,7 @@ function formatSize(bytes: number) {
柜次 - {{ detail.containerNo || detail.cabinetNo || detail.planNo || detail.loadingPlanId }} + {{ detail.containerNo || detail.cabinetNo || detail.planNo || '—' }}
资料覆盖 @@ -281,7 +281,7 @@ function formatSize(bytes: number) {
{{ item.source.fileName }} 版本 V{{ item.source.version }} · {{ formatSize(item.source.sizeBytes) }} · {{ formatDateTime(item.source.uploadedAt) }} - 关联入仓码:{{ item.inboundCodes.join('、') || '-' }} + 关联入仓码:{{ item.inboundCodes.join('、') || '—' }}
diff --git a/src/modules/first-leg/components/InboundCodeAssociationDialog.test.mjs b/src/modules/first-leg/components/InboundCodeAssociationDialog.test.mjs new file mode 100644 index 0000000..18f0218 --- /dev/null +++ b/src/modules/first-leg/components/InboundCodeAssociationDialog.test.mjs @@ -0,0 +1,32 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const source = readFileSync(new URL('./InboundCodeAssociationDialog.vue', import.meta.url), 'utf8') + +test('loads only the customer-scoped server association and fails closed', () => { + assert.match(source, /getMerchantInboundCodeAssociation\(inboundCode\)/) + assert.match(source, /association\.value = null/) + assert.match(source, /关联摘要加载失败/) + assert.match(source, /@click="loadAssociation"/) + assert.doesNotMatch(source, /orderId\)|customerId\)|destinationWarehouseId\)/) +}) + +test('renders business identifiers and names without internal long identifiers', () => { + for (const label of ['入仓码', '订单号', '订单状态', '客户名称', '目的国家', '目的仓']) { + assert.match(source, new RegExp(label)) + } + assert.match(source, /association\.inboundCode/) + assert.match(source, /association\.orderNo/) + assert.match(source, /association\.customerName/) + assert.match(source, /destinationWarehouseCode/) + assert.match(source, /destinationWarehouseName/) + assert.doesNotMatch(source, /association\.(?:orderId|customerId|destinationWarehouseId)/) + assert.match(source, /\|\| '—'/) +}) + +test('uses a bounded centered summary dialog suitable for the desktop detail flow', () => { + assert.match(source, /]*width="620px"/) + assert.match(source, /grid-template-columns: repeat\(2, minmax\(0, 1fr\)\)/) + assert.match(source, /min-height: 190px/) +}) diff --git a/src/modules/first-leg/components/InboundCodeAssociationDialog.vue b/src/modules/first-leg/components/InboundCodeAssociationDialog.vue new file mode 100644 index 0000000..12fe916 --- /dev/null +++ b/src/modules/first-leg/components/InboundCodeAssociationDialog.vue @@ -0,0 +1,115 @@ + + + + + diff --git a/src/modules/first-leg/views/FirstLegOrdersView.test.mjs b/src/modules/first-leg/views/FirstLegOrdersView.test.mjs index b6760bf..e54cd6d 100644 --- a/src/modules/first-leg/views/FirstLegOrdersView.test.mjs +++ b/src/modules/first-leg/views/FirstLegOrdersView.test.mjs @@ -223,9 +223,10 @@ test('uses only fields present on C02 rows before loading full detail', () => { test('shows and searches the inbound code after a draft is submitted', () => { assert.match(source, /label: '入仓码 \/ SKU'/) assert.match(source, /order\.businessSummary\.inboundCode/) - assert.match(source, / - +