From c6978c0983bc16cd26d40a46a9b1776deb8415b1 Mon Sep 17 00:00:00 2001
From: ouyangxq <775153912@qq.com>
Date: Tue, 15 Sep 2026 20:51:09 +0800
Subject: [PATCH 1/2] =?UTF-8?q?=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/api/inbound.js | 4 ++--
src/api/overseas.js | 7 ++++++-
src/api/relabel.js | 2 +-
tests/overseas-request.test.mjs | 15 +++++++++++++++
4 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/src/api/inbound.js b/src/api/inbound.js
index 86c326a..1d22e16 100644
--- a/src/api/inbound.js
+++ b/src/api/inbound.js
@@ -17,8 +17,8 @@ export const getCurrentWarehouseProfile = () => {
export const getReceivingContainer = async (containerNo) => normalizeReceivingSession(await overseasRequest('GET', `${RECEIVING_BASE}/${encodeURIComponent(containerNo)}`))
export const prepareReceivingContainer = async (containerNo, request) => normalizeReceivingSession(await overseasRequest('POST', `${RECEIVING_BASE}/${encodeURIComponent(containerNo)}/prepare`, request))
export const uploadCurrentUnloadPhoto = (filePath) => overseasUpload(current('/unload-photos/files'), filePath)
-export const downloadCurrentUnloadPhoto = (downloadPath) => overseasDownload(downloadPath)
-export const downloadCurrentDomesticLoadingPhoto = (downloadPath) => overseasDownload(downloadPath)
+export const downloadCurrentUnloadPhoto = (downloadPath) => overseasDownload(downloadPath, { expectedContentTypePrefix: 'image/' })
+export const downloadCurrentDomesticLoadingPhoto = (downloadPath) => overseasDownload(downloadPath, { expectedContentTypePrefix: 'image/' })
export const signCurrentReceiving = async (request) => normalizeReceivingSession(await overseasRequest('POST', current('/sign'), request))
export const getCurrentReceiving = async () => normalizeReceivingSession(await overseasRequest('GET', current()))
export const resolveCurrentCarton = async (cartonMark) => normalizeReceivingInbound(await overseasRequest('POST', current('/cartons/resolve'), { cartonMark }))
diff --git a/src/api/overseas.js b/src/api/overseas.js
index c792931..44d70df 100644
--- a/src/api/overseas.js
+++ b/src/api/overseas.js
@@ -116,7 +116,8 @@ export async function overseasUpload(path, filePath, name = 'file', retry = true
return body.data
}
-export async function overseasDownload(path, retry = true) {
+export async function overseasDownload(path, options = {}) {
+ const { retry = true, expectedContentTypePrefix = '' } = options
const attemptedToken = session.token
let response = await rawDownload(path, attemptedToken)
if (retry && unauthorized(response)) {
@@ -126,6 +127,10 @@ export async function overseasDownload(path, retry = true) {
if (response?.statusCode !== 200 || !response?.tempFilePath) {
throw errorFrom({ ...response, data: {} }, '签收照片下载失败')
}
+ const contentType = String(response?.header?.['content-type'] || response?.header?.['Content-Type'] || '').toLowerCase()
+ if (expectedContentTypePrefix && !contentType.startsWith(expectedContentTypePrefix.toLowerCase())) {
+ throw errorFrom({ ...response, data: {} }, '照片服务返回了无效内容,请稍后重试')
+ }
return response.tempFilePath
}
diff --git a/src/api/relabel.js b/src/api/relabel.js
index b3312a8..d0d24f0 100644
--- a/src/api/relabel.js
+++ b/src/api/relabel.js
@@ -49,4 +49,4 @@ export const reportRelabelException = (relabelNo, request) => detailAction(relab
export const completeRelabelTask = (relabelNo, request) => detailAction(relabelNo, '/complete', request)
export const reviewRelabelTask = (relabelNo, request) => detailAction(relabelNo, '/review', request)
export const uploadRelabelPhoto = (relabelNo, filePath) => overseasUpload(`${BASE}/${encoded(relabelNo)}/photos/files`, filePath)
-export const downloadRelabelPhoto = (relabelNo, fileId) => overseasDownload(`${BASE}/${encoded(relabelNo)}/photos/files/${encoded(fileId)}/download`)
+export const downloadRelabelPhoto = (relabelNo, fileId) => overseasDownload(`${BASE}/${encoded(relabelNo)}/photos/files/${encoded(fileId)}/download`, { expectedContentTypePrefix: 'image/' })
diff --git a/tests/overseas-request.test.mjs b/tests/overseas-request.test.mjs
index a417d44..0a2a2ee 100644
--- a/tests/overseas-request.test.mjs
+++ b/tests/overseas-request.test.mjs
@@ -174,6 +174,21 @@ test('authenticated photo download maps the public api path through the H5 overs
assert.equal(calls[0].header.Authorization, `Bearer ${value.overseasAccessToken}`)
})
+test('image download rejects a JSON error envelope returned with HTTP 200', async () => {
+ prepareSession()
+ globalThis.uni.downloadFile = (options) => options.success({
+ statusCode: 200,
+ tempFilePath: '/tmp/error.json',
+ header: { 'Content-Type': 'application/json', 'X-Request-Id': 'photo-request', 'X-Trace-Id': 'photo-trace' }
+ })
+
+ await assert.rejects(
+ overseasDownload('/api/pda/v1/last-leg/photo', { expectedContentTypePrefix: 'image/' }),
+ (error) => error.message === '照片服务返回了无效内容,请稍后重试'
+ && error.requestId === 'photo-request' && error.traceId === 'photo-trace'
+ )
+})
+
test('business and network errors retain diagnostics without refreshing or clearing a valid session', async () => {
prepareSession(); let refreshCount = 0
globalThis.uni.request = (options) => {
From 2dabdfde7cd8399f50f480fd0ecd4be92576ff34 Mon Sep 17 00:00:00 2001
From: ouyangxq <775153912@qq.com>
Date: Tue, 15 Sep 2026 21:05:09 +0800
Subject: [PATCH 2/2] =?UTF-8?q?=E6=94=AF=E6=8C=81PDA=E6=95=B4=E6=89=98?=
=?UTF-8?q?=E6=8B=A3=E8=B4=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
AGENTS.md | 2 +-
src/api/fulfillment.js | 4 ++++
src/pages/outbound/outbound.vue | 12 +++++++++---
tests/fulfillment-workflows.test.mjs | 11 ++++++++---
4 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/AGENTS.md b/AGENTS.md
index 21f8416..6771a53 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -48,7 +48,7 @@
| 换标真实作业 | `src/pages/relabel/relabel.vue`、`src/api/relabel.js` | 同一页面按操作参数承载换标收货、执行与复核;执行页按当前单明细切换 SKU/箱唛 Tab,组合明细同时显示两类需求。SKU 逐条扫码旧新SKU并携带审批件数,箱唛扫码仓库唛头/新FBA箱唛;组合单按同箱保留两个Tab的核对输入,全部核对后只提交一次箱事实。认领同步处理中;支持拍照、异常/返工和幂等恢复 |
| 现场查询真实作业 | `src/pages/query/query.vue`、`src/api/warehouse-operations.js` | 同一页面按操作参数承载库存、库位与托盘查询,只展示稳定业务标识 |
| 排车拣货与发车 | `src/pages/dispatch/dispatch.vue`、`src/api/fulfillment.js`、`src/stores/fulfillment.js` | 任务列表/详情、托盘业务码扫码拣货、幂等完成;发车必须同时完成拣货核平、平台转派送并严格消费服务端 `canDepart`、`departBlockReason`,不得按本地数量自行推断;公网前缀 `/api/pda/v1/last-leg/dispatches/**` |
-| 普通/一件代发出库 | `src/pages/outbound/outbound.vue`、`src/api/fulfillment.js`、`src/stores/fulfillment.js` | 任务列表/详情、稳定业务码扫码拣货和幂等完成;公网前缀 `/api/pda/v1/last-leg/outbound-tasks/**`;派送由 Admin/Client 承载 |
+| 普通/一件代发出库 | `src/pages/outbound/outbound.vue`、`src/api/fulfillment.js`、`src/stores/fulfillment.js` | 任务列表/详情、逐箱扫码、扫描 DP 托盘码原子拣取整托剩余箱件及幂等完成;公网前缀 `/api/pda/v1/last-leg/outbound-tasks/**`;派送由 Admin/Client 承载 |
| 扫码设备与诊断 | `src/utils/scan-broadcast.js`、`src/pages/scanner-test/scanner-test.vue` | `src/components/ScanTargetInput.vue`、声音反馈工具;广播 Action/Extra 见本文件服务边界 |
| 作业记录与人员 | `src/pages/history/history.vue`、`src/pages/profile/profile.vue` | 会话隔离、退出清理和后续真实作业历史契约 |
| 业务标识与内部 ID 隔离 | `tests/business-identifiers-ui.test.mjs`;页面入口 `src/pages/`、`src/components/`,会话映射 `src/stores/session.js` | 页面只展示集装箱号、入仓码、箱码、托盘码、库位码、仓库名称等业务值;不得以 orderId、inboundId、planId、warehouseId、palletId、locationId、user.id 等内部 ID 或截断值回退,错误诊断保留 code、requestId、traceId |
diff --git a/src/api/fulfillment.js b/src/api/fulfillment.js
index a1c74a5..0a82511 100644
--- a/src/api/fulfillment.js
+++ b/src/api/fulfillment.js
@@ -112,6 +112,10 @@ export function pickOutboundTask(taskNo, request) {
return overseasRequest('POST', `${OUTBOUND_BASE}/${encoded(taskNo)}/pick`, request)
}
+export function pickOutboundPallet(taskNo, palletNo, request) {
+ return overseasRequest('POST', `${OUTBOUND_BASE}/${encoded(taskNo)}/pallets/${encoded(palletNo)}/pick`, request)
+}
+
export function outboundPickPayload(item, position, packages) {
return {
outboundPalletItemId: item.outboundPalletItemId,
diff --git a/src/pages/outbound/outbound.vue b/src/pages/outbound/outbound.vue
index 9032f8d..a455740 100644
--- a/src/pages/outbound/outbound.vue
+++ b/src/pages/outbound/outbound.vue
@@ -3,11 +3,11 @@
- 出库任务列表仅显示普通/一件代发出库任务;扫描排车业务号会转入“排车拣货”
+ 出库任务一件代发扫描托盘码可整托出库;扫描排车业务号会转入“排车拣货”
- 任务号直接恢复;出库单号或排车单号先由服务端解析流程,不会作为出库任务号误查。
+ 任务号直接恢复;进入任务后扫描 DP 托盘码,将一次拣取该托全部未拣箱件。
正在加载出库任务…
@@ -49,7 +49,7 @@