diff --git a/AGENTS.md b/AGENTS.md
index e6be3ad..d93b921 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -35,7 +35,7 @@
| AppID、平台图标与打包资源 | `src/manifest.json`、`static/app-icons/` |
| Android APK/WGT 在线更新 | `src/App.vue`、`src/utils/app-update.js`、`.env.example` |
| 登录、刷新与会话 | `src/pages/login/login.vue`、`src/api/auth.js`、`src/stores/session.js` |
-| 海外仓工作台与模块入口 | `src/pages/home/home.vue`、`src/pages/module/module.vue` |
+| 海外仓工作台与作业入口 | `src/pages/home/home.vue`;首页直接进入各作业,页面刷新后返回首页 |
| 中英西语言设置 | `src/i18n/index.js`、`src/i18n/locales/`、`src/pages/profile/profile.vue` |
| 柜号签收、照片与入仓真实作业 | `src/pages/operation/operation.vue`、`src/components/ReceivingSessionSummary.vue`、`src/api/inbound.js` |
| 退货入库签收与合箱 | `src/pages/return-receipt/return-receipt.vue`、`src/pages/return-packing/return-packing.vue`、`src/api/return-receipt.js` |
diff --git a/docs/business-index.md b/docs/business-index.md
index 8a89718..419844c 100644
--- a/docs/business-index.md
+++ b/docs/business-index.md
@@ -14,7 +14,7 @@
| AppID、平台图标与打包资源 | `src/manifest.json`、`static/app-icons/` | Android/iOS 尺寸映射由 `tests/app-icons.test.mjs` 锁定;独立 AppID、签名和发布通道不得复用国内 PDA |
| Android APK/WGT 在线更新 | `src/App.vue`、`src/utils/app-update.js`、`.env.example`、`src/manifest.json` | `VITE_PDA_UPDATE_URL` 使用海外独立 HTTPS 通道;合同 `tests/app-update.test.mjs`,H5 不执行更新逻辑 |
| 登录、刷新与会话 | `src/pages/login/login.vue`、`src/api/auth.js`、`src/stores/session.js` | 国内 Auth 签发 Token;API 根见 `src/config/runtime.js` |
-| 海外仓工作台与模块入口 | `src/pages/home/home.vue`、`src/pages/module/module.vue` | `src/data/overseas-workbench.js` 是模块与作业可用性入口;首页通过 `/api/pda/v1/last-leg/receiving-containers/warehouse-profile` 读取 Token 绑定仓的中文主档名称 |
+| 海外仓工作台与作业入口 | `src/pages/home/home.vue` | `src/data/overseas-workbench.js` 是分组与作业可用性入口;首页直接进入各作业,页面刷新后返回首页;首页通过 `/api/pda/v1/last-leg/receiving-containers/warehouse-profile` 读取 Token 绑定仓的中文主档名称 |
| 中英西语言设置 | `src/i18n/index.js`、`src/i18n/locales/`、`src/pages/profile/profile.vue` | 个人中心可选择并持久化 `zh-CN`、`en`、`es`;当前业务翻译资源仍为空,页面业务文案继续使用中文,不得把语言选择入口误报为全量翻译完成 |
| 柜号签收、照片与入仓真实作业 | `src/pages/operation/operation.vue`、`src/components/ReceivingSessionSummary.vue`、`src/api/inbound.js`、`src/api/receiving-adapter.js`、`src/api/overseas.js`、`src/stores/receiving.js` | 签收前展开显示国内空仓、半仓、满仓、锁仓和带锁码五类整柜只读照片,收货中折叠查看,单图加载失败只允许重试且不阻断拆柜;海外三个固定照片角色各一张,支持现场拍照、鉴权预览及更换失败保留原图;多计划同柜直接展示服务端 `assigned/mergeAllowed/urgent/mergeHint`,合托明确为现场可选而非完成门禁;扫码实收确认计划生成阶段已预留的真实托盘和库位,校验卡展示预留库位编码及 `A0` 共享溢出位提示,不在设备端重新分配。Backend `lastleg-service/inboundplan/pda/controller/ReceivingContainerController.java`、`ReceivingContainerService.java`,公网前缀 `/api/pda/v1/last-leg/receiving-containers/**` |
| 退货入库签收与合箱 | `src/pages/return-receipt/return-receipt.vue`、`src/pages/return-packing/return-packing.vue`、`src/api/return-receipt.js`、`src/api/return-packing.js`、`src/stores/warehouse-work.js` | 签收优先扫描退货单号,也可扫描 SKU 后选择候选退货单,按本包实际 SKU 数量生成退货箱与计费批次;合箱只选择同客户已完成退货单的可用库存并录入新箱箱规;公网前缀 `/api/pda/v1/last-leg/return-receipts/**`、`/api/pda/v1/last-leg/return-inventory/**` |
diff --git a/src/components/ReceivingSessionSummary.vue b/src/components/ReceivingSessionSummary.vue
index 9e80c34..242d696 100644
--- a/src/components/ReceivingSessionSummary.vue
+++ b/src/components/ReceivingSessionSummary.vue
@@ -5,7 +5,6 @@
{{ statusLabel }}
- 海外接柜仓:{{ receivingWarehouseLabel }}
目的仓:{{ value }}
@@ -33,7 +32,6 @@
props: { session: { type: Object, default: null } },
computed: {
planFlags() { return Array.isArray(this.session?.inbounds) ? this.session.inbounds : [] },
- receivingWarehouseLabel() { const code = String(this.session?.overseasWarehouseCode || '').trim(); const name = String(this.session?.overseasWarehouseName || '').trim(); return code && name ? `${code} · ${name}` : '—' },
inboundCodeCount() { return Array.isArray(this.session?.inbounds) ? this.session.inbounds.length : 0 },
plannedPalletCount() { return (this.session?.inbounds || []).reduce((sum, item) => sum + Math.max(0, Number(item?.plannedPalletCount || 0)), 0) },
destinationValues() { const values = (this.session?.inbounds || []).map((item) => item?.fbaWarehouseCode || item?.destinationWarehouseName || item?.destinationWarehouseCode).filter(Boolean); const unique = [...new Set(values)]; return unique.length ? unique : ['—'] },
@@ -51,7 +49,7 @@
.receiving-summary-head,.receiving-summary-progress>view:first-child{display:flex;align-items:center;justify-content:space-between;gap:10px}
.receiving-summary-label,.receiving-summary-container,.metric-value,.metric-label{display:block}.receiving-summary-label{color:#718095;font-size:9px;line-height:13px}.receiving-summary-container{margin-top:1px;color:#17324d;font-size:17px;font-weight:850;line-height:22px}
.receiving-summary-status{flex-shrink:0;padding:4px 8px;border-radius:99px;color:#1668dc;font-size:9px;font-weight:700;background:#e3efff}
- .receiving-summary-context{display:flex;flex-direction:column;gap:4px;margin-top:7px;color:#617389;font-size:9px;line-height:14px}.receiving-summary-warehouse{display:block}.receiving-summary-destinations{display:flex;min-width:0;align-items:flex-start}.destination-label{flex-shrink:0}.destination-tags{display:flex;min-width:0;flex-wrap:wrap;gap:3px 4px}.destination-tags text{padding:0 5px;border-radius:99px;color:#42617f;line-height:14px;background:#e8f0fa;white-space:nowrap}
+ .receiving-summary-context{display:flex;flex-direction:column;gap:4px;margin-top:7px;color:#617389;font-size:9px;line-height:14px}.receiving-summary-destinations{display:flex;min-width:0;align-items:flex-start}.destination-label{flex-shrink:0}.destination-tags{display:flex;min-width:0;flex-wrap:wrap;gap:3px 4px}.destination-tags text{padding:0 5px;border-radius:99px;color:#42617f;line-height:14px;background:#e8f0fa;white-space:nowrap}
.receiving-summary-metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));margin-top:7px;padding:7px 2px;border-radius:9px;background:rgba(255,255,255,.8)}.receiving-summary-metrics>view{display:flex;align-items:center;flex-direction:column;border-right:1px solid #e3eaf2}.receiving-summary-metrics>view:last-child{border-right:0}
.receiving-plan-flags{margin-top:7px;padding:3px 8px;border-radius:9px;background:rgba(255,255,255,.82)}.receiving-plan-row{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:5px 0;border-bottom:1px solid #e9eef5}.receiving-plan-row:last-child{border-bottom:0}.receiving-plan-code{min-width:0;color:#17324d;font-size:10px;font-weight:750;overflow-wrap:anywhere}.receiving-plan-tags{display:flex;flex-shrink:0;gap:3px}.receiving-plan-tags text{padding:2px 5px;border-radius:99px;font-size:8px;line-height:12px}.flag-positive{color:#08705f;background:#e2f6f0}.flag-option{color:#1668dc;background:#e7f0ff}.flag-urgent{color:#b54708;background:#fff0dc}.flag-muted{color:#718095;background:#edf1f5}
.metric-value{color:#17324d;font-size:17px;font-weight:850;line-height:21px}.metric-pending{color:#b66a08}.metric-label{margin-top:1px;color:#718095;font-size:8px;line-height:12px;text-align:center}
diff --git a/src/pages.json b/src/pages.json
index efd3a9a..8a04281 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -2,7 +2,6 @@
"pages": [
{ "path": "pages/login/login", "style": { "navigationStyle": "custom", "navigationBarTitleText": "柏悦物流海外版", "backgroundColor": "#F5F7FA" } },
{ "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/warehouse-transfer/warehouse-transfer", "style": { "navigationStyle": "custom", "navigationBarTitleText": "仓储转运测量", "backgroundColor": "#F5F7FA" } },
{ "path": "pages/return-receipt/return-receipt", "style": { "navigationStyle": "custom", "navigationBarTitleText": "退货入库", "backgroundColor": "#F5F7FA" } },
diff --git a/src/pages/module/module.vue b/src/pages/module/module.vue
deleted file mode 100644
index 22911b3..0000000
--- a/src/pages/module/module.vue
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
- {{ currentModule.icon }}{{ currentModule.name }}{{ currentModule.description }}{{ availableCount }}/{{ currentModule.operations.length }} 可用
- 业务功能
-
- 灰色功能尚未开放,仅可进入已接入真实接口的作业。
-
-
-
-
-
diff --git a/src/pages/move/move.vue b/src/pages/move/move.vue
index b7fbaaa..4b400a4 100644
--- a/src/pages/move/move.vue
+++ b/src/pages/move/move.vue
@@ -1,6 +1,6 @@
-
+
整托移库
正在恢复未完成的移库草稿…
diff --git a/src/pages/operation/operation.vue b/src/pages/operation/operation.vue
index fda012f..af25943 100644
--- a/src/pages/operation/operation.vue
+++ b/src/pages/operation/operation.vue
@@ -3,7 +3,7 @@
- {{ pageTitle }}{{ pageSubtitle }}
+ {{ pageTitle }}{{ pageSubtitle }}
正在恢复当前柜作业…
@@ -13,19 +13,18 @@
正在刷新待卸货集装箱…
暂无待卸货集装箱
- {{ item.containerNo }}计划到仓:{{ formatPlannedArrival(item.plannedArrivalAt) }}{{ item.operable ? statusLabel(item.status) : '待齐全' }}{{ item.operable ? (item.currentAccount ? '继续' : '详情') : '只读' }} ›
- {{ item.totalOrderCount }}总订单{{ item.inboundCodeCount }}入仓码{{ item.planCount }}已生成计划{{ item.pendingPlanCount }}未操作
- 已生成 {{ item.planCount }} 个入库计划,{{ item.pendingPlanCount }} 个未操作,其中 {{ item.missingInboundCodeCount }} 个缺入仓码
- {{ item.blockReason || '当前柜入库计划尚未齐全' }}
- 海外接柜仓:{{ receivingWarehouseLabel(item) }}
- 业务目的仓:{{ item.destinationCodes.join(' / ') }}
+ {{ item.containerNo }}计划到仓:{{ formatPlannedArrival(item.plannedArrivalAt) }}{{ item.operable ? statusLabel(item.status) : '待齐全' }}{{ item.operable ? (item.currentAccount ? '继续' : '详情') : '只读' }} ›
+ {{ item.totalOrderCount }}总订单{{ item.inboundCodeCount }}入仓码{{ item.planCount }}已生成计划{{ item.pendingPlanCount }}未操作
+ 已生成 {{ item.planCount }} 个入库计划,{{ item.pendingPlanCount }} 个未操作,其中 {{ item.missingInboundCodeCount }} 个缺入仓码
+ {{ item.pendingPlanCount }} 单待处理
+ 海外接柜仓:{{ receivingWarehouseLabel(item) }}
+ 业务目的仓:{{ item.destinationCodes.join(' / ') }}
- 接柜仓由平台预先分配;确认后只建立当前账号的接柜会话,完成前不能切柜。
@@ -36,7 +35,7 @@
{{ selected?.blockReason || '当前柜入库计划尚未齐全' }}
海外接柜仓:{{ receivingWarehouseLabel(selected) }}
-
+
@@ -75,7 +74,7 @@
- 国内装柜照片已同步 {{ domesticPhotoCount }} / 5,点按{{ domesticPhotosExpanded ? '收起' : '展开' }}对照{{ domesticPhotosExpanded ? '收起' : '查看' }} ›
+ 国内装柜照片({{ domesticPhotoCount }} / 5){{ domesticPhotosExpanded ? '收起' : '查看' }} ›
@@ -85,7 +84,7 @@
- 1. 扫描 DP、箱码或 FBA 任一箱
+ 1. 货物码
@@ -93,17 +92,17 @@
{{ activeInbound.destinationType === 'FBA' ? 'FBA 按箱数收货' : '海外仓逐箱收货' }}{{ destinationLabel(activeInbound) }}
{{ (activeInbound.inboundCodes || [activeInbound.inboundCode]).join(' / ') }}
- {{ activeInbound.operationMode === 'FBA_GROUP' ? '按 FBA 仓成组收货' : '逐箱收货' }}{{ activeInbound.assigned ? '已分配' : '未分配' }}{{ activeInbound.mergeAllowed ? '可合托(现场自选)' : '不合托' }}{{ activeInbound.urgent ? '加急' : '普通' }}
+ {{ activeInbound.assigned ? '已分配' : '未分配' }}{{ activeInbound.mergeAllowed ? '可合托(现场自选)' : '不合托' }}{{ activeInbound.urgent ? '加急' : '普通' }}
{{ activeInbound.mergeHint }}
总计 {{ activeInbound.totalPackages }} 箱已收 {{ activeInbound.receivedPackages }} 箱待收 {{ activeInbound.pendingPackages }} 箱
- 2. 扫描预留实物托盘码
+ 2. 托盘码
{{ resolvedPallet.palletCode }}{{ resolvedPallet.compatible ? '预留托盘校验通过,可收货' : '托盘不匹配' }}库位:{{ resolvedPallet.locationCode }}{{ resolvedPallet.locationType === 'SHARED_OVERFLOW' ? '(A0共享溢出位)' : '' }}库位:待现场确认
- 3. 输入该托累计箱数
+ 3. 该托累计箱数
@@ -162,7 +161,7 @@
allInboundsReconciled() { const values = this.current?.inbounds || []; return values.length > 0 && !this.current?.hasDifference && values.every((item) => Number(item.pendingPackages) === 0) },
canFinishOnsite() { return Boolean(this.current && ['SIGNED', 'RECEIVING'].includes(this.current.status)) },
pageTitle() { return { candidates: '待卸货集装箱', detail: '柜号详情', sign: '拆柜拍照签收', receive: '当前柜收货', completed: '入库完成' }[this.phase] },
- pageSubtitle() { return { candidates: '选择集装箱后拍摄 3 张照片完成签收', detail: '核对柜号与整柜计划箱数', sign: '完成现场取证后进入收货', receive: '扫描箱码定位入仓码后开始收货', completed: '库存与柜级进度已由服务端核平' }[this.phase] }
+ pageSubtitle() { return { candidates: '选择集装箱后拍摄 3 张照片完成签收', detail: '', sign: '完成现场取证后进入收货', receive: '扫描箱码定位入仓码后开始收货', completed: '' }[this.phase] }
},
onLoad(options) { this.moduleKey = options.module || 'query'; const key = options.op || (this.moduleKey === 'inbound' ? 'receiving' : 'inventoryQuery'); this.operationName = ({ receiving: '拆柜收货', stocktake: '库存盘点', move: '商品移库', pallet: '托盘管理', relabelReceive: '换标列表', relabelExecute: '换标执行', relabelReview: '换标复核', inventoryQuery: '库存查询', locationQuery: '库位查询', palletQuery: '托盘查询' })[key] || '库存查询' },
onShow() { if (!requireSession()) return; this.user = session.currentUser; this._pageActive = true; this.startScanner(); if (this.inboundMode && (!this.bootstrapped || this.phase === 'candidates' || this._resumeReceiving)) { if (!this.busy) { this._resumeReceiving = false; this.bootstrap() } } },
@@ -215,7 +214,7 @@
diff --git a/src/pages/outbound/outbound.vue b/src/pages/outbound/outbound.vue
index 439cf9d..82b535f 100644
--- a/src/pages/outbound/outbound.vue
+++ b/src/pages/outbound/outbound.vue
@@ -3,12 +3,11 @@
- 出库任务整托扫描托盘码,散箱扫描箱唛;扫描排车业务号会转入“排车拣货”
+ 出库任务
当前任务出库作业详情
- 任务号直接恢复;退货整托出库扫托盘码,散箱出库扫箱唛。
正在加载出库任务…
diff --git a/src/pages/query/query.vue b/src/pages/query/query.vue
index ec5d2b4..77bbd5c 100644
--- a/src/pages/query/query.vue
+++ b/src/pages/query/query.vue
@@ -1,8 +1,8 @@
-
+
- {{ title }}{{ subtitle }};查询范围固定为 Token 绑定的海外仓
+ {{ title }}
正在读取最新库存事实…
@@ -26,13 +26,13 @@
import { playErrorSound, playSuccessSound } from '@/utils/audio-feedback.js'
const MODES = {
- inventoryQuery: { mode: 'inventory', title: '库存查询', label: '商品码或箱码', placeholder: '扫描或输入 SKU / 箱码', subtitle: '查看可用、冻结和收货中库存' },
- locationQuery: { mode: 'location', title: '库位查询', label: '库位码', placeholder: '扫描或输入库位码', subtitle: '查看库位状态、容量和占用' },
- palletQuery: { mode: 'pallet', title: '托盘查询', label: '托盘码', placeholder: '扫描或输入托盘码', subtitle: '查看托盘状态和绑定库存' }
+ inventoryQuery: { mode: 'inventory', title: '库存查询', label: '商品码或箱码', placeholder: '扫描或输入 SKU / 箱码' },
+ locationQuery: { mode: 'location', title: '库位查询', label: '库位码', placeholder: '扫描或输入库位码' },
+ palletQuery: { mode: 'pallet', title: '托盘查询', label: '托盘码', placeholder: '扫描或输入托盘码' }
}
export default {
components: { PortalHeader, ScanTargetInput },
- data() { return { user: session.currentUser, operationKey: 'inventoryQuery', mode: 'inventory', title: '库存查询', label: '商品码或箱码', placeholder: '扫描或输入 SKU / 箱码', subtitle: '', loading: false, searched: false, result: null, error: null } },
+ data() { return { user: session.currentUser, operationKey: 'inventoryQuery', mode: 'inventory', title: '库存查询', label: '商品码或箱码', placeholder: '扫描或输入 SKU / 箱码', loading: false, searched: false, result: null, error: null } },
computed: {
storeField() { return `${this.mode}Code` },
code: { get() { return warehouseWork.query[this.storeField] || '' }, set(value) { updateWarehouseWork('query', { [this.storeField]: value }) } },
diff --git a/src/pages/relabel/relabel.vue b/src/pages/relabel/relabel.vue
index a012b34..a4c2a19 100644
--- a/src/pages/relabel/relabel.vue
+++ b/src/pages/relabel/relabel.vue
@@ -1,21 +1,32 @@
-
+
- {{ title }}{{ stageSubtitle }}
- {{ candidateHeading }}
+ {{ candidateHeading }}
正在加载换标任务…
{{ emptyText }}
- 退回重做优先处理{{ item.relabelNo }}所属公司:{{ item.customerName || '未提供' }}客户订单:{{ item.customerOrderNo || '—' }}换标类型:{{ relabelTypeName(item.operationType) }}{{ statusLabel(item.taskStatus || item.applicationStatus) }}{{ item.expectedPackages }}应处理{{ item.relabelledPackages }}已换标{{ item.skippedPackages + item.exceptionPackages }}跳过/异常{{ item.reviewDecision === 'REWORK' ? '优先重做' : '查看并执行' }} ›
- {{ item.relabelNo }}所属公司:{{ item.customerName || '未提供' }}客户订单:{{ item.customerOrderNo || '—' }}换标类型:{{ relabelTypeName(item.operationType) }}{{ statusLabel(item.taskStatus || item.applicationStatus) }}{{ item.expectedPackages }}应处理{{ item.relabelledPackages }}已换标{{ item.skippedPackages + item.exceptionPackages }}跳过/异常查看并复核 ›
+
+
+ {{ item.relabelNo }}{{ statusLabel(item.taskStatus || item.applicationStatus) }}
+ {{ relabelTypeName(item.operationType) }}退回重做
+ 已换标 {{ item.relabelledPackages }} / {{ item.expectedPackages }}跳过/异常 {{ item.skippedPackages + item.exceptionPackages }}进入 ›
+
+
+
+
+ {{ item.relabelNo }}{{ statusLabel(item.taskStatus || item.applicationStatus) }}
+ {{ relabelTypeName(item.operationType) }}
+ 已换标 {{ item.relabelledPackages }} / {{ item.expectedPackages }}跳过/异常 {{ item.skippedPackages + item.exceptionPackages }}查看并复核 ›
+
+
换标类型:{{ relabelTypeName(current.operationType) }}
- {{ current.relabelNo }}客户订单:{{ current.customerOrderNo || '—' }}标签截止:{{ formatTime(current.labelDeadline) }}{{ statusLabel(current.taskStatus || current.applicationStatus) }}
+ {{ current.relabelNo }}所属公司:{{ current.customerName || candidates.find(item => item.relabelNo === current.relabelNo)?.customerName || '未提供' }}客户订单:{{ current.customerOrderNo || '—' }}标签截止:{{ formatTime(current.labelDeadline) }}{{ statusLabel(current.taskStatus || current.applicationStatus) }}
FBA 目的仓:{{ destination }}待维护应处理 {{ current.expectedPackages }}已换标 {{ current.relabelledPackages }}跳过 {{ current.skippedPackages }}异常 {{ current.exceptionPackages }}
标签文件暂无标签文件{{ file.scope === 'CARTON_TOTAL' ? '总箱唛' : file.scope === 'SKU_TOTAL' ? '总 SKU 标签' : '明细标签' }}:{{ file.fileName }}
@@ -125,7 +136,6 @@
return [...groups.values()].map(group => ({ ...group, remaining: Math.max(0, group.expectedPackages - group.relabelledPackages - group.skippedPackages - group.exceptionPackages) }))
},
showCurrent() { if (!this.current) return false; if (this.stage === 'review') return this.current.taskStatus === 'WAIT_REVIEW'; const owned = this.candidates.some((item) => item.relabelNo === this.current.relabelNo && item.taskStatus === 'PROCESSING' && item.claimedByCurrentOperator === true); if (this.stage === 'execute') return this.current.taskStatus === 'PROCESSING' && owned; return !this.current.taskStatus || this.current.taskStatus === 'AVAILABLE' || (this.current.taskStatus === 'PROCESSING' && owned) },
- stageSubtitle() { return this.stage === 'review' ? '仅展示当前仓库已完成现场换标并等待客户或平台处理的单据' : this.stage === 'execute' ? '仅展示当前账号已认领且正在处理的换标任务' : '退回重做单置顶优先处理;同一账号一次只能执行一票' },
availableTabs() { const items = this.current?.items || []; return [{ key: 'sku', name: 'SKU 换标', types: ['SKU_LABEL', 'CARTON_AND_SKU'] }, { key: 'carton', name: '箱唛换标', types: ['CARTON_LABEL', 'CARTON_AND_SKU'] }].filter((tab) => items.some((item) => tab.types.includes(item.operationType))) },
unitDraft() { return this.unitDrafts?.[this.selectedItem?.itemId] || { cartonMark: '', fbaCartonMark: '', skuVerifications: [] } },
skuVerified() { if (!this.selectedItem || this.selectedItem.operationType === 'CARTON_LABEL') return true; const expected = this.skuDetails(this.selectedItem); return expected.length > 0 && expected.every((sku, index) => { const actual = this.unitDraft.skuVerifications[index]; return actual && actual.skuCode.trim() === sku.skuCode && actual.newSkuCode.trim() === sku.newSkuCode && Number(actual.quantity) > 0 && Number(actual.quantity) === Number(sku.quantity) }) },
@@ -376,9 +386,10 @@
}
-
+.candidate-toolbar{align-items:center;margin-bottom:12px}.candidate-toolbar .card-title{margin-bottom:0;font-size:16px;line-height:24px}.candidate-toolbar .link-button{display:flex;align-items:center;justify-content:center;box-sizing:border-box;min-width:88px;height:44px;min-height:44px;margin:0;padding:0 16px;border:1px solid #9dc0f4;border-radius:8px;color:#135ee8;font-size:16px;font-weight:700;line-height:normal;background:#eaf2ff}.candidate-toolbar .link-button::after{border:0}.candidate-toolbar .link-button[disabled]{color:#718095;border-color:#dce5f0;background:#e8edf3}
+.relabel-candidate .business-code{min-width:0;font-size:18px;line-height:24px;overflow-wrap:anywhere}.relabel-candidate .status-pill{font-size:12px;line-height:18px}.candidate-kind{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-top:8px;color:#52647c;font-size:14px;line-height:22px}.candidate-footer{display:flex;align-items:center;flex-wrap:wrap;gap:6px 12px;margin-top:10px}.candidate-progress{color:#52647c;font-size:14px;line-height:24px}.candidate-progress-value{color:#135ee8;font-size:18px;font-weight:700}.candidate-warning{color:#b54708;font-size:12px;line-height:20px}.candidate-enter{margin-left:auto;color:#135ee8;font-size:14px;font-weight:600;line-height:24px}
diff --git a/src/pages/return-counting/return-counting.vue b/src/pages/return-counting/return-counting.vue
index 4b6eb5c..1775a6a 100644
--- a/src/pages/return-counting/return-counting.vue
+++ b/src/pages/return-counting/return-counting.vue
@@ -1,6 +1,6 @@
-
+
{{ error.message }}错误码:{{ error.code }}请求号:{{ error.requestId }}跟踪号:{{ error.traceId }}
@@ -15,7 +15,7 @@
第 {{ index + 1 }} 箱{{ box.cartonMark }}
- 各箱 SKU 数量汇总必须与本托签收数量一致,确认后这些箱子才能换标、合箱或创建出库单。箱内清点已完成,可以继续换标或出库。
+ 箱内清点已完成,可以继续换标或出库。
@@ -88,6 +88,7 @@ export default {
diff --git a/src/pages/return-packing/return-packing.vue b/src/pages/return-packing/return-packing.vue
index 4fdc37d..a59aac9 100644
--- a/src/pages/return-packing/return-packing.vue
+++ b/src/pages/return-packing/return-packing.vue
@@ -1,6 +1,6 @@
-
+
diff --git a/src/pages/return-receipt/return-receipt.vue b/src/pages/return-receipt/return-receipt.vue
index dc5eef3..fd2ce88 100644
--- a/src/pages/return-receipt/return-receipt.vue
+++ b/src/pages/return-receipt/return-receipt.vue
@@ -1,6 +1,6 @@
-
+
扫描 SKU 69 码
diff --git a/src/pages/stocktake/stocktake.vue b/src/pages/stocktake/stocktake.vue
index 39753fe..0f1e271 100644
--- a/src/pages/stocktake/stocktake.vue
+++ b/src/pages/stocktake/stocktake.vue
@@ -1,6 +1,6 @@
-
+
库存盘点
diff --git a/src/pages/warehouse-transfer/warehouse-transfer.vue b/src/pages/warehouse-transfer/warehouse-transfer.vue
index 3380952..249e1d1 100644
--- a/src/pages/warehouse-transfer/warehouse-transfer.vue
+++ b/src/pages/warehouse-transfer/warehouse-transfer.vue
@@ -1,6 +1,6 @@
-
+
diff --git a/src/styles/pda.scss b/src/styles/pda.scss
index 91ed560..b269ab7 100644
--- a/src/styles/pda.scss
+++ b/src/styles/pda.scss
@@ -699,10 +699,6 @@
}
.portal-title,
- .module-hero-title,
- .module-hero-desc,
- .operation-name,
- .operation-desc,
.summary-number,
.summary-label,
.profile-name,
@@ -780,50 +776,15 @@
.group-title { font-size: 16px; font-weight: 700; }
- .module-hero-icon, .operation-icon {
- display: flex;
- align-items: center;
- justify-content: center;
- font-weight: 700;
- }
- .operation-info { display: flex; min-width: 0; flex: 1; flex-direction: column; margin-left: 11px; }
-
.inline-back { height: 48px; padding: 0 4px; color: #42617f; font-size: 13px; line-height: 48px; background: transparent; }
- .module-hero { position: relative; display: flex; align-items: center; margin-top: 5px; padding: 17px; overflow: hidden; border: 1px solid rgba(23, 105, 232, 0.09); border-radius: 16px; background: #e7f0ff; box-shadow: 0 5px 16px rgba(32, 67, 111, 0.07); }
- .module-hero-inbound { background: #e1f7f0; }
- .module-hero-inventory { background: #fff3da; }
- .module-hero-relabel { background: #f1eafe; }
- .module-hero-query { background: #f0e9ff; }
- .module-hero-icon { width: 52px; height: 52px; flex-shrink: 0; margin-right: 13px; border-radius: 14px; color: #ffffff; font-size: 18px; background: $primary; }
- .module-hero-inbound .module-hero-icon { background: #0d9488; }
- .module-hero-inventory .module-hero-icon { background: #d68c0c; }
- .module-hero-relabel .module-hero-icon { background: #7c3aed; }
- .module-hero-query .module-hero-icon { background: #6d55c7; }
- .module-hero-copy { min-width: 0; flex: 1; }
- .module-hero-title { font-size: 18px; font-weight: 700; }
- .module-hero-desc { margin-top: 4px; color: #617187; font-size: 11px; line-height: 17px; }
- .module-hero-count { flex-shrink: 0; margin-left: 8px; padding: 4px 7px; border-radius: 99px; color: #49627d; font-size: 9px; line-height: 15px; background: rgba(255, 255, 255, 0.72); }
.section-heading { display: flex; align-items: flex-end; justify-content: space-between; gap: 10px; margin: 22px 2px 11px; }
.section-heading .group-title { margin: 0; }
.section-heading-hint { color: #8190a3; font-size: 10px; line-height: 16px; }
.group-title { display: block; margin: 22px 2px 11px; }
- .operation-list { overflow: hidden; border-radius: 12px; background: #ffffff; box-shadow: 0 4px 15px rgba(32, 67, 111, 0.06); }
- .operation-item, .history-item { display: flex; align-items: center; box-sizing: border-box; width: 100%; min-height: 78px; padding: 13px 12px; border-bottom: 1px solid #edf0f3; text-align: left; background: #ffffff; }
- .operation-item:active { background: #f2f7ff; }
- .operation-item-disabled, .operation-item-disabled[disabled] { color: #8d98a6; opacity: 1; background: #f2f4f6; }
- .operation-item-disabled .operation-icon { color: #8b97a5; background: #e3e7eb; }
- .operation-item-disabled .operation-desc { color: #929daa; }
- .operation-item:last-child, .history-item:last-child { border-bottom: 0; }
- .operation-icon { width: 39px; height: 39px; flex-shrink: 0; border-radius: 10px; color: #366cae; font-size: 12px; background: #edf4fd; }
- .operation-name { font-size: 13px; font-weight: 700; }
- .operation-desc { margin-top: 4px; color: $text-secondary; font-size: 11px; line-height: 17px; }
- .operation-ready, .operation-locked { flex-shrink: 0; padding: 3px 6px; border-radius: 99px; font-size: 9px; line-height: 15px; }
- .operation-ready { color: #0b6f65; background: #dff7f0; }
- .operation-locked { color: #7c8795; background: #e5e9ee; }
+ .history-item { display: flex; align-items: center; box-sizing: border-box; width: 100%; min-height: 78px; padding: 13px 12px; border-bottom: 1px solid #edf0f3; text-align: left; background: #ffffff; }
+ .history-item:last-child { border-bottom: 0; }
.operation-count { min-width: 22px; height: 22px; border-radius: 11px; color: #ffffff; font-size: 11px; line-height: 22px; text-align: center; background: $danger; }
.operation-prototype { flex-shrink: 0; padding: 5px 7px; border-radius: 5px; color: #737f8d; font-size: 11px; background: #e1e5e9; }
- .operation-arrow { margin-left: 8px; color: #9aa5b1; font-size: 21px; }
- .module-empty-note { margin-top: 14px; padding: 12px; border-radius: 9px; color: #758194; font-size: 11px; line-height: 18px; text-align: center; background: #e9eef5; }
.operation-page { padding-top: 12px; }
.operation-page-title-row, .operation-scan-row, .task-toolbar, .prototype-task-head, .prototype-task-meta, .prototype-task-foot { display: flex; align-items: center; }
diff --git a/tests/business-identifiers-ui.test.mjs b/tests/business-identifiers-ui.test.mjs
index e1beb72..f2ddd2c 100644
--- a/tests/business-identifiers-ui.test.mjs
+++ b/tests/business-identifiers-ui.test.mjs
@@ -8,7 +8,7 @@ const interpolations = (source) => [...template(source).matchAll(/\{\{([\s\S]*?)
test('all registered user-facing pages avoid rendering internal identifiers', async () => {
const paths = [
- '../src/pages/login/login.vue', '../src/pages/home/home.vue', '../src/pages/module/module.vue',
+ '../src/pages/login/login.vue', '../src/pages/home/home.vue',
'../src/pages/operation/operation.vue', '../src/pages/pallet/pallet.vue', '../src/pages/profile/profile.vue',
'../src/pages/dispatch/dispatch.vue', '../src/pages/outbound/outbound.vue',
'../src/pages/stocktake/stocktake.vue', '../src/pages/move/move.vue', '../src/pages/relabel/relabel.vue', '../src/pages/query/query.vue',
@@ -86,7 +86,6 @@ test('missing user-visible relationships use explicit empty labels instead of ID
read('../src/components/ReceivingSessionSummary.vue'),
read('../src/pages/pallet/pallet.vue')
])
- assert.match(summary, /overseasWarehouseCode[\s\S]*overseasWarehouseName/)
assert.match(summary, /unique\.length \? unique : \['—'\]/)
assert.match(pallet, /inboundCode \|\| '无入仓码'/)
assert.match(pallet, /locationCode \|\| '未绑定库位'/)
diff --git a/tests/multi-plan-receiving.test.mjs b/tests/multi-plan-receiving.test.mjs
index 4171a31..64783e6 100644
--- a/tests/multi-plan-receiving.test.mjs
+++ b/tests/multi-plan-receiving.test.mjs
@@ -108,7 +108,7 @@ test('container UI locks and signs once, then renders the resolved destination g
for (const fact of ['已分配', '未分配', '可合托·非强制', '不合托', '加急', '普通']) assert.match(summary, new RegExp(fact))
assert.doesNotMatch(operation, /v-for="[^"]*inbound[^"]*"/)
assert.match(operation, /v-if="activeInbound"[\s\S]*activeInbound\.inboundCodes[\s\S]*activeInbound\.pendingPackages/)
- assert.match(operation, /activeInbound\.operationMode === 'FBA_GROUP'[\s\S]*按 FBA 仓成组收货/)
+ assert.match(operation, /class="mode-banner"[^\n]*activeInbound\.destinationType === 'FBA'[^\n]*FBA 按箱数收货[^\n]*海外仓逐箱收货/)
assert.doesNotMatch(operation, /activeInbound\.customer|客户/)
assert.match(operation, /resolveCurrentCarton\(cartonMark\)/)
assert.match(operation, /operationMode !== 'DROPSHIP_PALLET'[\s\S]*receiveCurrentWholePallet/)
@@ -121,7 +121,6 @@ test('container UI locks and signs once, then renders the resolved destination g
assert.match(operation, /applyWarehouseProfile\(await getCurrentWarehouseProfile\(\)\)/)
assert.match(operation, /values\.map\(\(item\) => this\.validateReceivingWarehouse/)
assert.match(operation, /prepareReceivingContainer[\s\S]*validateReceivingWarehouse/)
- assert.match(summary, /overseasWarehouseCode[\s\S]*overseasWarehouseName/)
for (const forbidden of ['customerId', 'planNo', 'allocationId', 'warehouseId', 'locationId']) {
assert.doesNotMatch(inboundApi, new RegExp(forbidden))
}
diff --git a/tests/navigation.test.mjs b/tests/navigation.test.mjs
index 9a53bca..0aa40c1 100644
--- a/tests/navigation.test.mjs
+++ b/tests/navigation.test.mjs
@@ -1,7 +1,12 @@
import test from 'node:test'
import assert from 'node:assert/strict'
+import { readFile } from 'node:fs/promises'
+import { MODULES } from '../src/data/overseas-workbench.js'
import { navigateBackOr, navigateToUnique } from '../src/utils/navigation.js'
+const read = (path) => readFile(new URL(path, import.meta.url), 'utf8')
+const component = (source, dependencies) => new Function(...Object.keys(dependencies), source.match(/