diff --git a/AGENTS.md b/AGENTS.md
index 4450ed4..31693af 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -90,6 +90,8 @@
## 质量门禁
+- 已发车排车详情按需挂载`src/components/DispatchHandoverStatus.vue`,通过`src/api/trip-handover.js`读取当前派送资源和最近交接;每次进入/刷新一次GET,列表不逐行请求。仅只读、不提交客户或仓ID;后台确认交接,已完成派送保留完成时资源。回归`tests/trip-handover.test.mjs`。
+
- 安装:`npm install`;检查:`npm test`。
- 仓储转运测量、打托、照片或标签入口变更运行 `node --test tests/warehouse-transfer-api.test.mjs tests/project-contract.test.mjs tests/workbench-availability.test.mjs`;静态合同不替代真实扫码、绑定及打印验收。
- 退货签收、合箱或工作台入口变更先运行 `node --test tests/return-receipt-api.test.mjs tests/return-packing-api.test.mjs tests/project-contract.test.mjs tests/workbench-availability.test.mjs`;离线检查不替代真实 API 联调与真机扫码验收。
diff --git a/src/api/trip-handover.js b/src/api/trip-handover.js
new file mode 100644
index 0000000..3e0a3b3
--- /dev/null
+++ b/src/api/trip-handover.js
@@ -0,0 +1,26 @@
+import { overseasRequest } from './overseas.js'
+
+const text = value => typeof value === 'string' ? value : ''
+const transport = value => ({ vehiclePlate: text(value?.vehiclePlate), driverName: text(value?.driverName), driverPhone: text(value?.driverPhone) })
+export function normalizeHandoverStatus(value) {
+ if (!value?.dispatchNo || !value?.tripNo || !Array.isArray(value.deliveries)) throw new Error('交接状态缺少排车或派送依据')
+ return {
+ dispatchNo: text(value.dispatchNo), tripNo: text(value.tripNo), tripStatus: text(value.tripStatus),
+ tripRecordVersion: value.tripRecordVersion == null ? '' : String(value.tripRecordVersion),
+ originalDeparture: transport(value.originalDeparture),
+ deliveries: value.deliveries.map(row => {
+ const packages = Number(row.packages)
+ if (!row.deliveryNo || row.packages == null || String(row.packages).trim() === '' || !Number.isSafeInteger(packages) || packages <= 0) throw new Error('交接状态中的派送箱数无效')
+ return { deliveryNo: text(row.deliveryNo), status: text(row.status), packages, terminalAt: text(row.terminalAt), transport: transport(row.transport) }
+ }),
+ latestHandover: value.latestHandover ? { handoverNo: text(value.latestHandover.handoverNo), handedOverAt: text(value.latestHandover.handedOverAt),
+ location: text(value.latestHandover.location), previous: transport(value.latestHandover.previous), replacement: transport(value.latestHandover.replacement) } : null
+ }
+}
+export async function getDispatchHandoverStatus(dispatchNo) {
+ const number = String(dispatchNo || '').trim()
+ if (!number) throw new Error('请先打开排车任务')
+ const result = normalizeHandoverStatus(await overseasRequest('GET', `/pda/v1/last-leg/dispatches/${encodeURIComponent(number)}/handover-status`))
+ if (result.dispatchNo !== number) throw new Error('返回的交接记录不属于当前排车任务')
+ return result
+}
diff --git a/src/components/DispatchHandoverStatus.vue b/src/components/DispatchHandoverStatus.vue
new file mode 100644
index 0000000..7990a9d
--- /dev/null
+++ b/src/components/DispatchHandoverStatus.vue
@@ -0,0 +1,61 @@
+
+
+ 发车与交接记录
+ 正在读取当前运输资源…
+
+ {{ error.message }}requestId:{{ error.requestId }}traceId:{{ error.traceId }}
+
+
+
+ 车次 {{ status.tripNo }}
+ 原始发车:{{ status.originalDeparture.vehiclePlate || '—' }} · {{ status.originalDeparture.driverName || '—' }}
+
+ 最近交接 {{ status.latestHandover.handoverNo || '—' }}
+ {{ time(status.latestHandover.handedOverAt) }} · {{ status.latestHandover.location || '—' }}
+ {{ status.latestHandover.previous.vehiclePlate || '—' }} → {{ status.latestHandover.replacement.vehiclePlate || '—' }}
+ {{ status.latestHandover.previous.driverName || '—' }} → {{ status.latestHandover.replacement.driverName || '—' }}
+
+ 本排车暂无在途交接记录。
+
+ {{ delivery.deliveryNo }} · {{ delivery.packages }} 箱 · {{ label(delivery) }}
+ {{ delivery.terminalAt ? '完成时承运' : '当前承运' }}:{{ delivery.transport.vehiclePlate || '—' }} · {{ delivery.transport.driverName || '—' }}
+ 联系电话 {{ delivery.transport.driverPhone }}
+
+ 交接由后台确认。本页只读,不重复发车。
+
+
+
+
+
diff --git a/src/pages/dispatch/dispatch.vue b/src/pages/dispatch/dispatch.vue
index 31dbb60..ba9b125 100644
--- a/src/pages/dispatch/dispatch.vue
+++ b/src/pages/dispatch/dispatch.vue
@@ -30,7 +30,7 @@
@@ -38,9 +38,10 @@
+
{{ current.dispatchNo || '—' }}{{ statusLabel(current.status) }}
- 任务号 {{ current.taskNo || '—' }} · 车牌 {{ current.vehiclePlate || '—' }}
+ 任务号 {{ current.taskNo || '—' }} · {{ departed ? '原发车车牌' : '车牌' }} {{ current.vehiclePlate || '—' }}
{{ current.pickBlockReason || '请先通过调度审核再拣货' }}
{{ plannedPackages }}计划箱数{{ pickedPackages }}已拣箱数{{ remainingPackages }}待拣箱数
@@ -88,6 +89,7 @@