diff --git a/src/modules/cross-border/PROTOTYPE_MATRIX.md b/src/modules/cross-border/PROTOTYPE_MATRIX.md index e151aa8..2d2319b 100644 --- a/src/modules/cross-border/PROTOTYPE_MATRIX.md +++ b/src/modules/cross-border/PROTOTYPE_MATRIX.md @@ -8,4 +8,14 @@ | 美国本土卡 | 列表 | 现有 `BusinessOrdersView.vue` 的独立列表与左侧操作列 | 对应列展示车型和取送时间 | 待人工截图确认 | | 美国本土卡 | 详情 | 现有独立详情路由及键值网格 | 展示保存的两端联系信息、车型和取送时间;加宽长字段标签 | 待人工截图确认 | -不改动散板字段和承载方式。无新增审核、删除或附件操作。 +本节记录本土卡车调整;散板最新范围见下节。无新增审核、删除或附件操作。 + + +## 本土散板简化需求合同 + +当前字段规则见 `docs/海外仓业务/README.md`“本土散板简化下单”;依据用户提供的 Admin 简化原型及本轮同步消费者确认,不改变客户认证范围。 + +| 页面 | 操作 | 承载与范围 | 验收 | +|---|---|---|---| +| 美本土散板 | 新增 | 保持现有独立路由和表单容器;同步商品、预计托数、四项联系信息及必填的当地取送时间;删除运输方式、柜型和下单安排项 | 自动测试与人工视觉分开;待人工截图确认 | +| 美本土散板 | 列表、详情 | 保持现有独立路由;展示同一保存值和真实承运安排,不访问运力池 | 待人工截图确认 | diff --git a/src/modules/cross-border/api/businessOrders.ts b/src/modules/cross-border/api/businessOrders.ts index 3961d21..b54b2de 100644 --- a/src/modules/cross-border/api/businessOrders.ts +++ b/src/modules/cross-border/api/businessOrders.ts @@ -17,7 +17,6 @@ export interface BusinessOrderQuery { status?: string routeKeyword?: string transportType?: string - transportMode?: string vehicleType?: 'PALLET_15' | 'PALLET_30' pickupFrom?: string pickupTo?: string @@ -31,20 +30,17 @@ export interface TransportSummary { fleetArrangement: import('../fleetArrangement').FleetArrangement | null orderNo: string transportType?: string - transportMode?: string palletRequired?: string | null origin: string destination: string - containerType?: string | null palletCount?: number packageCount: number totalWeightKg: number totalVolumeCbm: number budgetAmount: number | null currency: string | null - contactName?: string | null - contactInfo?: string | null - requestedDate?: string | null + productName?: string | null + estimatedPalletCount?: number | null vehicleType?: 'PALLET_15' | 'PALLET_30' | null originContactName?: string | null originContactPhone?: string | null @@ -66,22 +62,23 @@ export interface TransportDetail { } export interface TransportCreate { - transportType: string - transportMode?: string - palletRequired?: string + productName?: string + estimatedPalletCount: number origin: string originAddress?: string destination: string destinationAddress?: string - containerType?: string packageCount: number totalWeightKg: number totalVolumeCbm: number budgetAmount?: number currency?: string - contactName?: string - contactInfo?: string - requestedDate?: string + originContactName: string + originContactPhone: string + destinationContactName: string + destinationContactPhone: string + pickupAt: string + deliveryAt: string remark?: string idempotencyKey: string } diff --git a/src/modules/cross-border/businessOrders.test.mjs b/src/modules/cross-border/businessOrders.test.mjs index b8584cb..0e5215b 100644 --- a/src/modules/cross-border/businessOrders.test.mjs +++ b/src/modules/cross-border/businessOrders.test.mjs @@ -142,7 +142,8 @@ test('truck requires both contacts vehicle and minute times without inheriting l const loose = transportHarness('looseboard') assert.equal(loose.rules.value.contactName, undefined) assert.equal(loose.rules.value.contactInfo, undefined) - assert.equal(loose.rules.value.transportMode[0].required, true) + assert.equal(loose.rules.value.transportMode, undefined) + for (const key of ['originContactName', 'originContactPhone', 'destinationContactName', 'destinationContactPhone', 'pickupAt', 'deliveryAt']) assert.equal(loose.rules.value[key][0].required, true) truck.form.pickupAt = '2026-10-20T09:30:00' let failure truck.rules.value.deliveryAt[1].validator({}, '2026-10-20T09:29:00', error => { failure = error }) @@ -177,3 +178,30 @@ test('truck details render saved minute times and unknown pool demand without ti assert.equal(fields.has('柜型'), false) assert.equal(fields.has('托盘数'), false) }) + + +test('looseboard saves split contacts and both local minute times without obsolete fields', async () => { + const current = transportHarness('looseboard', true) + Object.assign(current.form, { originContactName: 'A', originContactPhone: '1', destinationContactName: 'B', destinationContactPhone: '2', productName: '家居用品', estimatedPalletCount: 3, packageCount: 8, origin: 'LA', destination: 'ONT', pickupAt: '2026-10-20T09:00:00', deliveryAt: '2026-10-20T15:30:00' }) + await current.submit(); await current.submit() + const payload = current.calls[1].payload + assert.equal(payload.estimatedPalletCount, 3) + assert.equal(payload.packageCount, 8) + assert.equal(payload.productName, '家居用品') + assert.equal(payload.pickupAt, '2026-10-20T09:00:00') + assert.equal(payload.originContactName, 'A') + assert.equal(payload.destinationContactPhone, '2') + assert.equal(payload.deliveryAt, '2026-10-20T15:30:00') + assert.equal(payload.idempotencyKey, current.calls[0].payload.idempotencyKey) + for (const field of ['palletRequired', 'transportType', 'transportMode', 'containerType', 'contactName', 'contactInfo', 'requestedDate']) assert.equal(Object.hasOwn(payload, field), false) +}) + +test('looseboard requires both times and rejects backwards schedule', () => { + const current = transportHarness('looseboard') + assert.equal(current.rules.value.pickupAt[0].required, true) + assert.equal(current.rules.value.deliveryAt[0].required, true) + let failure + current.form.pickupAt = '2026-10-20T10:30:00' + current.rules.value.deliveryAt[1].validator({}, '2026-10-20T10:29:00', error => { failure = error }) + assert.ok(failure) +}) diff --git a/src/modules/cross-border/components/BusinessOrdersView.vue b/src/modules/cross-border/components/BusinessOrdersView.vue index 152987f..ccae096 100644 --- a/src/modules/cross-border/components/BusinessOrdersView.vue +++ b/src/modules/cross-border/components/BusinessOrdersView.vue @@ -1,13 +1,12 @@