From c9bc6bf6168b449cbebb2e39c7f91f10f9bb6214 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Mon, 24 Aug 2026 17:04:40 +0800 Subject: [PATCH] feat(pda): add locked container receiving flow --- src/api/receiving.js | 14 ++++++ src/data/overseas-workbench.js | 6 +-- src/pages.json | 2 + src/pages/home/home.vue | 2 +- .../inbound-receiving/inbound-receiving.vue | 46 +++++++++++++++++++ src/pages/inbound-sign/inbound-sign.vue | 39 ++++++++++++++++ src/pages/module/module.vue | 2 +- tests/project-contract.test.mjs | 19 ++++++++ 8 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 src/api/receiving.js create mode 100644 src/pages/inbound-receiving/inbound-receiving.vue create mode 100644 src/pages/inbound-sign/inbound-sign.vue diff --git a/src/api/receiving.js b/src/api/receiving.js new file mode 100644 index 0000000..00a077e --- /dev/null +++ b/src/api/receiving.js @@ -0,0 +1,14 @@ +import { overseasRequest, overseasUpload } from '@/api/overseas.js' + +const root = '/pda/v1/last-leg/receiving-containers' +const container = (containerNo) => `${root}/${encodeURIComponent(containerNo)}` + +export const listReceivingContainers = (page = 1, size = 50) => overseasRequest('GET', `${root}?status=WAIT_SIGN&page=${page}&size=${size}`) +export const getActiveReceivingContainer = () => overseasRequest('GET', `${root}/active`) +export const getReceivingContainer = (containerNo) => overseasRequest('GET', container(containerNo)) +export const uploadSignPhoto = (containerNo, filePath) => overseasUpload(`${container(containerNo)}/sign-photos/files`, filePath) +export const signReceivingContainer = (containerNo, request) => overseasRequest('POST', `${container(containerNo)}/sign`, request) +export const resolveReceivingCarton = (containerNo, request) => overseasRequest('POST', `${container(containerNo)}/cartons/resolve`, request) +export const resolveReceivingPallet = (containerNo, request) => overseasRequest('POST', `${container(containerNo)}/pallets/resolve`, request) +export const receiveContainerCargo = (containerNo, request) => overseasRequest('POST', `${container(containerNo)}/receive-batches`, request) +export const completeReceivingContainer = (containerNo, request) => overseasRequest('POST', `${container(containerNo)}/complete`, request) diff --git a/src/data/overseas-workbench.js b/src/data/overseas-workbench.js index d474925..80c4591 100644 --- a/src/data/overseas-workbench.js +++ b/src/data/overseas-workbench.js @@ -1,8 +1,8 @@ export const MODULES = [ { key: 'inbound', name: '入仓作业', icon: '入', description: '到仓、卸柜收货与上架', operations: [ - { name: '到仓登记', icon: '到', iconType: 'download', description: '扫描入仓计划并登记车辆到仓', available: true, badge: '已接入' }, - { name: '卸柜收货', icon: '卸', iconType: 'paperplane', description: '按箱或托盘确认卸柜实收', available: true, badge: '已接入' }, - { name: '入仓上架', icon: '上', iconType: 'upload', description: '扫描容器与目标库位完成上架', available: true, badge: '已接入' } + { name: '入库签收', icon: '签', iconType: 'camera', description: '选择待处理柜号、拍照并确认签收', path: '/pages/inbound-sign/inbound-sign', available: true, badge: '真实接口' }, + { name: '收货', icon: '收', iconType: 'paperplane', description: '自动读取当前柜并按目的仓规则收货', path: '/pages/inbound-receiving/inbound-receiving', available: true, badge: '真实接口' }, + { name: '入仓上架', icon: '上', iconType: 'upload', description: '扫描容器与目标库位完成上架', available: false, badge: '待接入' } ] }, { key: 'inventory', name: '库内作业', icon: '库', description: '盘点、移库与托盘管理', operations: [ { name: '库存盘点', icon: '盘', iconType: 'list', description: '按任务扫描库位和库存', available: true, badge: '原型' }, diff --git a/src/pages.json b/src/pages.json index f9e33e7..8528e19 100644 --- a/src/pages.json +++ b/src/pages.json @@ -4,6 +4,8 @@ { "path": "pages/home/home", "style": { "navigationStyle": "custom", "navigationBarTitleText": "海外仓工作台", "backgroundColor": "#F5F7FA" } }, { "path": "pages/module/module", "style": { "navigationStyle": "custom", "navigationBarTitleText": "海外仓业务", "backgroundColor": "#F5F7FA" } }, { "path": "pages/operation/operation", "style": { "navigationStyle": "custom", "navigationBarTitleText": "现场作业", "backgroundColor": "#F5F7FA" } }, + { "path": "pages/inbound-sign/inbound-sign", "style": { "navigationStyle": "custom", "navigationBarTitleText": "入库签收", "backgroundColor": "#F5F7FA" } }, + { "path": "pages/inbound-receiving/inbound-receiving", "style": { "navigationStyle": "custom", "navigationBarTitleText": "收货", "backgroundColor": "#F5F7FA" } }, { "path": "pages/history/history", "style": { "navigationStyle": "custom", "navigationBarTitleText": "作业记录", "backgroundColor": "#F5F7FA" } }, { "path": "pages/profile/profile", "style": { "navigationStyle": "custom", "navigationBarTitleText": "个人中心", "backgroundColor": "#F5F7FA" } }, { "path": "pages/scanner-test/scanner-test", "style": { "navigationStyle": "custom", "navigationBarTitleText": "广播扫码测试", "backgroundColor": "#F5F7FA" } } diff --git a/src/pages/home/home.vue b/src/pages/home/home.vue index e6c5d52..ed0f237 100644 --- a/src/pages/home/home.vue +++ b/src/pages/home/home.vue @@ -41,7 +41,7 @@ openOperation(operation) { if (!operation.available) return const module = this.groups.find((group) => group.operations.some((item) => item.name === operation.name)) - uni.navigateTo({ url: `/pages/operation/operation?module=${module?.key || 'query'}&name=${encodeURIComponent(operation.name)}` }) + uni.navigateTo({ url: operation.path || `/pages/operation/operation?module=${module?.key || 'query'}&name=${encodeURIComponent(operation.name)}` }) } } } diff --git a/src/pages/inbound-receiving/inbound-receiving.vue b/src/pages/inbound-receiving/inbound-receiving.vue new file mode 100644 index 0000000..a4f79cc --- /dev/null +++ b/src/pages/inbound-receiving/inbound-receiving.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/src/pages/inbound-sign/inbound-sign.vue b/src/pages/inbound-sign/inbound-sign.vue new file mode 100644 index 0000000..e06e073 --- /dev/null +++ b/src/pages/inbound-sign/inbound-sign.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/src/pages/module/module.vue b/src/pages/module/module.vue index ccdba48..f28796a 100644 --- a/src/pages/module/module.vue +++ b/src/pages/module/module.vue @@ -21,7 +21,7 @@ onLoad(options) { this.moduleKey = options.key || 'inbound' }, onShow() { if (!requireSession()) return; this.user = session.currentUser }, methods: { - openOperation(operation) { if (operation.available) uni.navigateTo({ url: `/pages/operation/operation?module=${this.moduleKey}&name=${encodeURIComponent(operation.name)}` }) } + openOperation(operation) { if (operation.available) uni.navigateTo({ url: operation.path || `/pages/operation/operation?module=${this.moduleKey}&name=${encodeURIComponent(operation.name)}` }) } } } diff --git a/tests/project-contract.test.mjs b/tests/project-contract.test.mjs index 40561df..e80a22b 100644 --- a/tests/project-contract.test.mjs +++ b/tests/project-contract.test.mjs @@ -38,3 +38,22 @@ test('login has no committed demo credentials and inbound operation consumes rea assert.match(overseas, /replaceTokens|refreshOverseasToken/) assert.match(overseas, /requestId/) }) + +test('container receiving uses sign then locked-container receiving without internal IDs', async () => { + const [pages, workbench, api, sign, receiving] = await Promise.all([ + read('../src/pages.json'), read('../src/data/overseas-workbench.js'), read('../src/api/receiving.js'), + read('../src/pages/inbound-sign/inbound-sign.vue'), read('../src/pages/inbound-receiving/inbound-receiving.vue') + ]) + assert.match(pages, /pages\/inbound-sign\/inbound-sign/) + assert.match(pages, /pages\/inbound-receiving\/inbound-receiving/) + assert.match(workbench, /入库签收/) + assert.match(workbench, /name: '收货'/) + for (const path of ['/active', '/sign-photos/files', '/sign', '/cartons/resolve', '/pallets/resolve', '/receive-batches', '/complete']) assert.match(api, new RegExp(path.replaceAll('/', '\\/'))) + assert.match(sign, /当前柜完成前不能处理其他柜号/) + assert.doesNotMatch(sign, /切换柜号|customerId|planNo|allocationId|warehouseId|locationId/) + assert.match(receiving, /receiptMode: 'BULK_COUNT'/) + assert.match(receiving, /receiptMode: 'CARTON_SCAN'/) + assert.match(receiving, /cargo\.destinationType === 'FBA'/) + assert.match(receiving, /cargo\?\.destinationType === 'OVERSEAS'/) + assert.doesNotMatch(receiving, /切换柜号|customerId|planNo|allocationId|warehouseId|locationId/) +})