From 6a135c3982666cc826ec970f0802ffc7bb835c77 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Sat, 3 Oct 2026 15:00:10 +0800 Subject: [PATCH] fix(fleet): distinguish signed returned and remaining trip cargo --- .../overseas-master-data/PROTOTYPE_MATRIX.md | 2 +- .../overseas-master-data/api/trips.test.ts | 5 ++-- src/modules/overseas-master-data/api/trips.ts | 5 ++-- .../pages/TripDetailView.vue | 8 +++++-- .../pages/TripsBehavior.test.ts | 23 ++++++++++++++++++- src/modules/overseas-master-data/tripTypes.ts | 2 +- 6 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/modules/overseas-master-data/PROTOTYPE_MATRIX.md b/src/modules/overseas-master-data/PROTOTYPE_MATRIX.md index f6232f7c..5dec8836 100644 --- a/src/modules/overseas-master-data/PROTOTYPE_MATRIX.md +++ b/src/modules/overseas-master-data/PROTOTYPE_MATRIX.md @@ -81,6 +81,6 @@ | 外接询价、详情与承运占位 | 用户确认运营后台登记外接业务;首版不扩展客户在线撮合 | 车次详情外接页签、1080px居中三列表单,详情只读、确认/取消居中确认;操作列随计划/执行阶段使用320/170px,保留原行操作 | 已接登记/编辑/确认/取消、独立权限及已确认预订余位;已接真实来货登记、实收复核和排车关联。2026-10-03截图`fleet-external-booking-1920.png`及DOM复现旧440px操作列使1640px详情容器内表格宽1720px;本次仅收窄闲置列宽,部署后的1920×1080复验仍待完成 | | 计划容量与剩余运力 | 本轮外接剩余运力方案,原型无完整占位工作台 | 车次详情独立页签,复用托盘核对表,显示预计值和每段剩余托位/kg/m³ | 源码已接计划容量;已扣除确认承运的外接占位;不把预订当实物装载 | | 实际装载确认 | 新业务规则,无原型对应操作 | 详情页内实物托盘表及确认区 | 已按用户确认接入首版;真实页面验收待执行 | -| 货物结案、结束车辆/司机任务 | 新业务规则,无原型对应操作 | 详情页展示服务端检查结果,提交前居中确认 | 已接独立命令和确认;结案与资源释放独立 | +| 货物结案、结束车辆/司机任务 | 新业务规则,无原型对应操作 | 详情页展示服务端检查结果,提交前居中确认 | 已接独立命令和确认;结案与资源释放独立。原实发、实签、本趟实退及未结箱数分别展示,混合结果显示“余货已签收”;一次结案查询提供数量,不逐票补请求,1920×1080真实视觉待验 | | 外接来货与实收复核、关联发运 | 首版外接货自送起运仓、按实际货量确认费用的已确认方案 | 车次外接行操作打开1080px居中弹框;现有PDA转运实收承载现场作业 | 已接真实来源、实测差异与运费复核、版本/hash和客户排车关联;未将本地验证当作环境验收 | diff --git a/src/modules/overseas-master-data/api/trips.test.ts b/src/modules/overseas-master-data/api/trips.test.ts index f3cd8b2b..3db084f6 100644 --- a/src/modules/overseas-master-data/api/trips.test.ts +++ b/src/modules/overseas-master-data/api/trips.test.ts @@ -20,7 +20,7 @@ const load = { palletSpaces: '1', weightKg: '100.125', volumeCbm: '1.005' } const confirmed = { manifestHash: 'a'.repeat(64), cargo: [cargo], pallets: [{ ...load, warehousePalletId: longId, deliveryIndex: '1' }], segments: [{ fromIndex: '0', toIndex: '1', occupied: load, remaining: { palletSpaces: '29', weightKg: '17899.875', volumeCbm: '88.995' } }], operatorId: longId, confirmedAt: trip.updatedAt } const loading = { tripNo: trip.tripNo, recordVersion: '3', cargo: [cargo], manifestHash: confirmed.manifestHash, confirmedLoad: confirmed, blockReason: null } -const outcome = { dispatchNo: 'DP-1', deliveryNo: 'DL-1', customerId: longId, destination: 'ONT8', status: 'DELIVERED', packages: '12', terminalAt: trip.updatedAt } +const outcome = { dispatchNo: 'DP-1', deliveryNo: 'DL-1', customerId: longId, destination: 'ONT8', status: 'DELIVERED', packages: '12', signedPackages: '8', returnedPackages: '4', remainingPackages: '0', terminalAt: trip.updatedAt } const envelope = (data: T) => ({ code: '200', message: '成功', data }) describe('fleet trip API contract', () => { @@ -93,12 +93,13 @@ describe('fleet trip API contract', () => { it('loads completion and immutable history only when explicitly requested', async () => { vi.mocked(http.get).mockResolvedValueOnce(envelope({ tripNo: trip.tripNo, recordVersion: '5', canComplete: false, blockReason: '另一票未签收', outcomes: [outcome] })) - expect(await getTripCompletion(trip.tripNo)).toMatchObject({ recordVersion: 5, canComplete: false, blockReason: '另一票未签收', outcomes: [{ packages: 12 }] }) + expect(await getTripCompletion(trip.tripNo)).toMatchObject({ recordVersion: 5, canComplete: false, blockReason: '另一票未签收', outcomes: [{ packages: 12, signedPackages: 8, returnedPackages: 4, remainingPackages: 0 }] }) vi.mocked(http.get).mockResolvedValueOnce(envelope({ page: '2', size: '10', total: '12', pages: '2', records: [{ recordVersion: '5', action: 'COMPLETE', reason: null, operatorId: longId, occurredAt: trip.updatedAt, snapshot: { trip, externalBookings: [], capacityPlan: null, loadingReview: reviewState, dispatches: detail.dispatches, confirmedLoad: confirmed, completion: { idempotencyKey: 'key', requestHash: 'hash', completedAt: trip.updatedAt, recordedAt: trip.updatedAt, operatorId: longId, outcomes: [outcome] } } }] })) const history = await listTripHistory(trip.tripNo, 2, 10) expect(http.get).toHaveBeenLastCalledWith(`${base}/TR%2FTEST/history`, { params: { page: 2, size: 10 } }) expect(history.records[0]?.snapshot.completion?.outcomes[0]?.packages).toBe(12) + expect(history.records[0]?.snapshot.completion?.outcomes[0]).toMatchObject({ signedPackages: 8, returnedPackages: 4, remainingPackages: 0 }) expect(history.records[0]?.snapshot.trip.recordVersion).toBe(0) }) diff --git a/src/modules/overseas-master-data/api/trips.ts b/src/modules/overseas-master-data/api/trips.ts index 6178ad09..157f7c45 100644 --- a/src/modules/overseas-master-data/api/trips.ts +++ b/src/modules/overseas-master-data/api/trips.ts @@ -26,7 +26,7 @@ type WireConfirmed = Omit & } type WireLoading = Omit & { recordVersion: Numeric; cargo: WireCargo[]; confirmedLoad: WireConfirmed | null } type WirePlanning = Omit & { recordVersion: Numeric; cargo: WireCargo[]; capacityPlan: WireConfirmed | null; availableSegments: WireConfirmed['segments'] } -type WireOutcome = Omit & { packages: Numeric } +type WireOutcome = Omit & { packages: Numeric; signedPackages: Numeric; returnedPackages: Numeric; remainingPackages: Numeric } type WireInspection = Omit & { recordVersion: Numeric; outcomes: WireOutcome[] } type WireHistory = Omit & { recordVersion: Numeric @@ -52,7 +52,8 @@ const review = (value: WireReview): TripReviewState => ({ ...value, recordVersio const binding = (value: WireBinding): TripBinding => ({ ...value, packageCount: integer(value.packageCount), review: review(value.review) }) const detail = (value: WireDetail): TripDetail => ({ ...value, trip: trip(value.trip), dispatches: value.dispatches.map(binding), loadingReview: review(value.loadingReview) }) const cargo = (value: WireCargo): TripCargo => ({ ...value, packages: integer(value.packages) }) -const outcome = (value: WireOutcome): TripOutcome => ({ ...value, packages: integer(value.packages) }) +const outcome = (value: WireOutcome): TripOutcome => ({ ...value, packages: integer(value.packages), + signedPackages: integer(value.signedPackages), returnedPackages: integer(value.returnedPackages), remainingPackages: integer(value.remainingPackages) }) const load = (value: WireLoad): TripLoad => ({ palletSpaces: integer(value.palletSpaces), weightKg: number(value.weightKg), volumeCbm: number(value.volumeCbm) }) function confirmed(value: WireConfirmed | null): TripConfirmedLoad | null { return value == null ? null : { ...value, cargo: value.cargo.map(cargo), diff --git a/src/modules/overseas-master-data/pages/TripDetailView.vue b/src/modules/overseas-master-data/pages/TripDetailView.vue index 3adf8058..1bb8c1e9 100644 --- a/src/modules/overseas-master-data/pages/TripDetailView.vue +++ b/src/modules/overseas-master-data/pages/TripDetailView.vue @@ -42,7 +42,11 @@ const bindingsColumns: DataTableColumn[] = [{ key: 'actions', label: '操作', w { key: 'boundAt', label: '关联时间', slotName: 'boundAt', minWidth: 180 }] const outcomeColumns: DataTableColumn[] = [{ key: 'deliveryNo', prop: 'deliveryNo', label: '派送单号', minWidth: 220 }, { key: 'destination', prop: 'destination', label: '目的地', minWidth: 180 }, { key: 'status', label: '状态', slotName: 'status', width: 115 }, - { key: 'packages', prop: 'packages', label: '箱数', width: 110 }, { key: 'terminalAt', label: '完成时间', slotName: 'terminalAt', minWidth: 180 }] + { key: 'packages', prop: 'packages', label: '原实发箱数', width: 110 }, + { key: 'signedPackages', prop: 'signedPackages', label: '实签箱数', width: 100 }, + { key: 'returnedPackages', prop: 'returnedPackages', label: '本趟实退箱数', width: 120 }, + { key: 'remainingPackages', prop: 'remainingPackages', label: '未结箱数', width: 100 }, + { key: 'terminalAt', label: '完成时间', slotName: 'terminalAt', minWidth: 180 }] const outcomeLabels: Record = { DELIVERED: '已签收', RETURNED: '已退回', RETURN_PENDING: '待退回', DELIVERING: '派送中', WAIT_DELIVERY: '待派送', CANCELLED: '已取消' } const historyColumns: DataTableColumn[] = [{ key: 'occurredAt', label: '操作时间', slotName: 'time', minWidth: 175 }, { key: 'action', label: '操作', slotName: 'action', minWidth: 145 }, { key: 'reason', prop: 'reason', label: '说明', minWidth: 230 }, @@ -199,7 +203,7 @@ onBeforeUnmount(() => { generation++; inspectionSequence++; historySequence++ })

理货审核:{{ tripReviewLabels[detail.loadingReview.status] }} · {{ detail.loadingReview.reason }} · {{ time(detail.loadingReview.reviewedAt) }}

理货审核通过理货打回