diff --git a/AGENTS.md b/AGENTS.md index f4b5cbc..5ae07ce 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -88,8 +88,8 @@ - 已发车详情按需挂载 `src/components/DispatchHandoverStatus.vue`,经 `src/api/trip-handover.js` 读当前派送资源/最近交接;进入/刷新各一次GET,列表不逐行请求。只读;后台确认交接,已完成派送留完成时资源。回归 `tests/trip-handover.test.mjs`。 -- 安装:`npm install`;检查:`npm test`。 -- 专项用 `node --test`:转运测量/打托/照片/标签变更跑 `tests/warehouse-transfer-api.test.mjs`;退货签收/合箱/工作台变更先跑 `tests/return-receipt-api.test.mjs tests/return-packing-api.test.mjs`;两类均加 `tests/project-contract.test.mjs tests/workbench-availability.test.mjs`。静态/离线不替代真实API、真机扫码/绑定/打印。 +- 验证按 `../../docs/AI工程执行与协作约束.md`;文案看差异,小逻辑定向测试,不默认全量/构建/真机验收。`npm test` 为全量入口。 +- 专项用 `node --test`:转运测量/打托/照片/标签行为变更选 `tests/warehouse-transfer-api.test.mjs`;退货签收/合箱行为变更选对应 `tests/return-receipt-api.test.mjs` / `tests/return-packing-api.test.mjs`;项目结构/工作台入口变化才加 `tests/project-contract.test.mjs` / `tests/workbench-availability.test.mjs`。离线不替代真实API、真机扫码/绑定/打印。 - H5编译链 `vue-i18n` 固定 `9.1.9`,禁范围版本/单独升级;依赖变更同步 `package.json`、`package-lock.json`、现有 `pnpm-lock.yaml`,至少跑H5编译。 - 产品为海外PDA App;`npm run build:h5` 仅浏览器调试/模板门禁,App资源用 `npm run build:app`。APK/IPA须在HBuilderX/正式原生打包环境以独立AppID/签名验证,资源编译非APK真机验收。 - 改 `src/manifest.json` 图标映射或 `static/app-icons/` 跑 `node --test tests/app-icons.test.mjs`;禁缺声明尺寸、同一错尺寸占位、复制国内AppID/签名。 diff --git a/src/api/dispatch-loading-documents.js b/src/api/dispatch-loading-documents.js new file mode 100644 index 0000000..265f3ec --- /dev/null +++ b/src/api/dispatch-loading-documents.js @@ -0,0 +1,26 @@ +import { overseasRequest, overseasUpload, overseasDownload } from './overseas.js' + +export const loadingPhotoSlots = ['LOADING_SCENE', 'LOADING_LIST', 'PRE_DEPARTURE_CHECK', 'DRIVER_LICENSE', 'RECEIPT'] +const path = no => { + if (!no || typeof no !== 'string') throw new Error('请先打开排车任务') + return `/pda/v1/last-leg/dispatches/${encodeURIComponent(no)}/loading-documents` +} +function slotPath(no, slot) { + if (!loadingPhotoSlots.includes(slot)) throw new Error('装车照片框无效') + return `${path(no)}/${slot}` +} +export function normalizeLoadingDocuments(value, no) { + if (value?.dispatchNo !== no || !/^\d+$/.test(String(value.recordVersion ?? '')) || !Array.isArray(value.photos) + || !['AFTER_LOADING', 'DELIVERING', 'DELIVERED'].includes(value.stage)) throw new Error('装车资料与当前排车事实不一致') + const slots = new Set() + for (const photo of value.photos) { + if (!loadingPhotoSlots.includes(photo.slot) || !photo.fileId || slots.has(photo.slot)) throw new Error('装车照片引用不完整或重复') + slots.add(photo.slot) + } + return { ...value, editable: value.editable === true, complete: value.complete === true } +} +export async function getLoadingDocuments(no) { return normalizeLoadingDocuments(await overseasRequest('GET', path(no)), no) } +export function uploadLoadingPhoto(no, slot, filePath, key) { return overseasUpload(`${slotPath(no, slot)}/uploads?idempotencyKey=${encodeURIComponent(key)}`, filePath) } +export function recoverLoadingPhoto(no, key) { return overseasRequest('GET', `${path(no)}/uploads/${encodeURIComponent(key)}`) } +export async function bindLoadingPhoto(no, slot, body) { return normalizeLoadingDocuments(await overseasRequest('PUT', slotPath(no, slot), body), no) } +export function readLoadingPhoto(no, photo) { return overseasDownload(`${slotPath(no, photo.slot)}/files/${encodeURIComponent(photo.fileId)}/content`, { expectedContentTypePrefix: 'image/' }) } diff --git a/src/components/DispatchLoadingDocuments.vue b/src/components/DispatchLoadingDocuments.vue new file mode 100644 index 0000000..9989690 --- /dev/null +++ b/src/components/DispatchLoadingDocuments.vue @@ -0,0 +1,101 @@ + + + diff --git a/src/pages/dispatch/dispatch.vue b/src/pages/dispatch/dispatch.vue index 0116afd..316e1b5 100644 --- a/src/pages/dispatch/dispatch.vue +++ b/src/pages/dispatch/dispatch.vue @@ -9,7 +9,7 @@ 正在加载排车事实… {{ error.message }}错误码:{{ error.code }}requestId:{{ error.requestId }}traceId:{{ error.traceId }}网络中断,原请求和作业草稿已保留。 -