From d910b247551ebe2038b051e83fec36949e144029 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Mon, 19 Oct 2026 17:40:25 +0800 Subject: [PATCH] feat(first-leg): reorder pickup choices and collect optional courier tracking --- src/modules/first-leg/PROTOTYPE_MATRIX.md | 11 +++++++++++ src/modules/first-leg/api/firstLeg.ts | 1 + .../first-leg/store/firstLegOrders.test.mjs | 15 +++++++++++++++ src/modules/first-leg/store/firstLegOrders.ts | 1 + src/modules/first-leg/types/firstLegOrder.ts | 1 + .../first-leg/views/FirstLegOrdersView.test.mjs | 14 ++++++++++++-- .../first-leg/views/FirstLegOrdersView.vue | 15 ++++++++++----- src/modules/first-leg/views/QuickOrderView.vue | 1 + 8 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 src/modules/first-leg/PROTOTYPE_MATRIX.md diff --git a/src/modules/first-leg/PROTOTYPE_MATRIX.md b/src/modules/first-leg/PROTOTYPE_MATRIX.md new file mode 100644 index 0000000..f992ea7 --- /dev/null +++ b/src/modules/first-leg/PROTOTYPE_MATRIX.md @@ -0,0 +1,11 @@ +# 头程下单交货方式调整 + +依据:当前对话 2026-10-19 用户确认“卡车自送排第一,我司提货排第二,快递自提排第三;快递自提增加非必填快递单号”。该确认优先于历史原型对应字段。 + +| 页面 | 操作 | 承载方式 | 布局 | 当前实现 | 结论 | +|---|---|---|---|---|---| +| 头程下单 | 新增 | 现有独立页面 | 保持现有国内交货区布局 | 快递自送、我司提货、卡车自送;无快递单号输入 | 按确认顺序排列;第三项改为快递自提,下方条件显示选填快递单号 | +| 头程订单 | 编辑草稿 | 现有居中弹框 | 复用新增表单 | 无快递单号输入 | 同步新增规则,回显已保存单号 | +| 头程订单 | 详情 | 现有独立页面 | 保持现有信息区 | 已有快递单号只读展示 | 复用当前展示;说明客户可在快递自提草稿填写单号 | + +自动检查与人工视觉验收分别记录;1920×1080、100% 缩放的页面效果待人工截图确认。 diff --git a/src/modules/first-leg/api/firstLeg.ts b/src/modules/first-leg/api/firstLeg.ts index fd7885e..b9e3554 100644 --- a/src/modules/first-leg/api/firstLeg.ts +++ b/src/modules/first-leg/api/firstLeg.ts @@ -139,6 +139,7 @@ export interface FirstLegOrderSaveRequest { privateDestinationAddressId?: number | string cargoes: FirstLegOrderCargoPayload[] pickupMethod: ApiPickupMethod + expressTrackingNo?: string receivingWarehouseId: number | string receivingWarehouseName: string receivingWarehouseAddress?: string | null diff --git a/src/modules/first-leg/store/firstLegOrders.test.mjs b/src/modules/first-leg/store/firstLegOrders.test.mjs index 1ee3866..486a982 100644 --- a/src/modules/first-leg/store/firstLegOrders.test.mjs +++ b/src/modules/first-leg/store/firstLegOrders.test.mjs @@ -8,6 +8,21 @@ const storeSource = readFileSync(new URL('./firstLegOrders.ts', import.meta.url) const apiSource = readFileSync(new URL('../api/firstLeg.ts', import.meta.url), 'utf8') const unifiedApiSource = readFileSync(new URL('../api/unifiedOrders.ts', import.meta.url), 'utf8') +test('saves optional express tracking, trims it, and omits it for other pickup methods', () => { + const functions = storeSource.slice(storeSource.indexOf('function apiTransport('), storeSource.indexOf('function prepareDraftForSave(')) + const javascript = ts.transpileModule(functions, { compilerOptions: { target: ts.ScriptTarget.ES2022 } }).outputText + const save = runInNewContext(`${javascript}; savePayload`) + const draft = { pickupMethod: 'SELF_EXPRESS', trackingNumber: ' SF123456789 ', destinationCountryCode: 'US', destinationType: 'OVERSEAS', items: [] } + const warehouse = { id: '1', name: '收货仓' } + assert.equal(save(draft, warehouse, '客户').expressTrackingNo, 'SF123456789') + for (const trackingNumber of ['', ' ']) { + assert.equal(save({ ...draft, trackingNumber }, warehouse, '客户').expressTrackingNo, undefined) + } + for (const pickupMethod of ['SELF_TRUCK', 'PICKUP']) { + assert.equal(save({ ...draft, pickupMethod }, warehouse, '客户').expressTrackingNo, undefined) + } +}) + test('detail fetches authoritative progress on direct entry and refresh, and clears stale progress on failure', async () => { const functionSource = storeSource.slice(storeSource.indexOf(' async function loadDetail('), storeSource.indexOf(' async function loadWarehouseDetail(')) const progressSource = readFileSync(new URL('../utils/orderProgress.ts', import.meta.url), 'utf8') diff --git a/src/modules/first-leg/store/firstLegOrders.ts b/src/modules/first-leg/store/firstLegOrders.ts index bbc120f..4be98fc 100644 --- a/src/modules/first-leg/store/firstLegOrders.ts +++ b/src/modules/first-leg/store/firstLegOrders.ts @@ -120,6 +120,7 @@ function savePayload(draft: FirstLegOrderDraft, warehouse: ClientWarehouse, cust productImageFileId: item.productImageFileId || undefined, })), pickupMethod: draft.pickupMethod, + expressTrackingNo: draft.pickupMethod === 'SELF_EXPRESS' ? draft.trackingNumber.trim() || undefined : undefined, receivingWarehouseId: warehouse.id, receivingWarehouseName: warehouse.name, channelCode: draft.channelCode, diff --git a/src/modules/first-leg/types/firstLegOrder.ts b/src/modules/first-leg/types/firstLegOrder.ts index 7a96184..3d2edb3 100644 --- a/src/modules/first-leg/types/firstLegOrder.ts +++ b/src/modules/first-leg/types/firstLegOrder.ts @@ -183,6 +183,7 @@ export type FirstLegOrderDraft = Omit { assert.match(source, /履约快递信息/) assert.match(source, /detailOrder\.expressCompanyName/) assert.match(source, /detailOrder\.trackingNumber/) - assert.match(source, /客户端仅只读展示/) + assert.match(source, /快递公司由平台维护/) assert.doesNotMatch(source, /v-model="form\.expressCompanyName"/) - assert.doesNotMatch(source, /v-model="form\.trackingNumber"/) +}) + +test('orders pickup choices and offers an optional tracking number only for express pickup', () => { + const group = source.match(//)[0] + assert.deepEqual([...group.matchAll(/value="([A-Z_]+)"/g)].map(match => match[1]), ['SELF_TRUCK', 'PICKUP', 'SELF_EXPRESS']) + assert.match(group, /value="SELF_EXPRESS">快递自提/) + const field = source.match(/]*label="快递单号"[\s\S]*?<\/el-form-item>/)[0] + assert.match(field, /v-model="form\.trackingNumber" maxlength="128"/) + assert.doesNotMatch(field, /\brequired\b|:rules=/) + assert.match(source, /trackingNumber: order\.pickupMethod === 'SELF_EXPRESS' \? order\.trackingNumber : ''/) + assert.match(source, /if \(form\.pickupMethod !== 'SELF_EXPRESS'\) form\.trackingNumber = ''/) }) test('shows the full customer-safe order detail without platform-only controls', () => { diff --git a/src/modules/first-leg/views/FirstLegOrdersView.vue b/src/modules/first-leg/views/FirstLegOrdersView.vue index 13f4e72..e4e2dbf 100644 --- a/src/modules/first-leg/views/FirstLegOrdersView.vue +++ b/src/modules/first-leg/views/FirstLegOrdersView.vue @@ -372,9 +372,9 @@ function formatExportDateTime(value: string | null | undefined) { } const pickupMethodLabels: Record = { - SELF_EXPRESS: '自行快递到仓', - PICKUP: '平台上门提货', - SELF_TRUCK: '自行送货到仓', + SELF_TRUCK: '卡车自送', + PICKUP: '我司提货', + SELF_EXPRESS: '快递自提', } const destinationTypeLabels: Record = { FBA: 'FBA 仓', @@ -424,6 +424,7 @@ function emptyDraft(): FirstLegOrderDraft { destinationWarehouseId: '', destinationWarehouseName: '', pickupMethod: 'SELF_EXPRESS', + trackingNumber: '', channelCode: '', channelName: '', pricingTaxMode: 'TAX_INCLUDED', @@ -920,6 +921,7 @@ function draftFromOrder(order: FirstLegOrder): FirstLegOrderDraft { destinationWarehouseId: order.destinationWarehouseId, destinationWarehouseName: order.destinationWarehouseName, pickupMethod: order.pickupMethod, + trackingNumber: order.pickupMethod === 'SELF_EXPRESS' ? order.trackingNumber : '', channelCode: order.channelCode, channelName: order.channelName, pricingTaxMode: order.pricingTaxMode, @@ -942,6 +944,7 @@ function copyDraftFromOrder(order: FirstLegOrder): FirstLegOrderDraft { return { ...draft, inboundCode: '', + trackingNumber: '', items: draft.items.map((item) => ({ ...item, id: `copied-cargo-${++cargoSequence}`, @@ -1134,6 +1137,7 @@ async function changeDestinationType() { } function changePickupMethod() { + if (form.pickupMethod !== 'SELF_EXPRESS') form.trackingNumber = '' if (form.pickupMethod === 'PICKUP') { form.shipperPartySnapshot ??= createPickupPartySnapshot() } else { @@ -2241,7 +2245,8 @@ async function searchOrders(model: QueryModel) {
普通头程一件代发测量完成后不办理国内库存入库,将直接推送 PDA 合托。 用于绑定领星入库单,提交后可在尾程订单、入库单和出库单中查询。 - 快递自送我司提货卡车自送 + 卡车自送我司提货快递自提 +