优化
This commit is contained in:
@@ -40,7 +40,7 @@
|
||||
| 登录、刷新与会话 | `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/i18n/index.js`、`src/i18n/locales/`、`src/pages/profile/profile.vue` | 个人中心可选择并持久化 `zh-CN`、`en`、`es`;当前业务翻译资源仍为空,页面业务文案继续使用中文,不得把语言选择入口误报为全量翻译完成 |
|
||||
| 柜号签收、照片与入仓真实作业 | `src/pages/operation/operation.vue`、`src/api/inbound.js`、`src/api/receiving-adapter.js`、`src/api/overseas.js`、`src/stores/receiving.js` | 三个固定照片角色各一张,支持拍照/相册、鉴权预览及更换失败保留原图;Backend `lastleg-service/inboundplan/pda/controller/ReceivingContainerController.java`、`ReceivingContainerService.java`,公网前缀 `/api/pda/v1/last-leg/receiving-containers/**` |
|
||||
| 柜号签收、照片与入仓真实作业 | `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`,合托明确为现场可选而非完成门禁;扫码实收确认计划生成阶段已预留的真实托盘和库位,不在设备端重新分配。Backend `lastleg-service/inboundplan/pda/controller/ReceivingContainerController.java`、`ReceivingContainerService.java`,公网前缀 `/api/pda/v1/last-leg/receiving-containers/**` |
|
||||
| 托盘管理真实作业 | `src/pages/pallet/pallet.vue`、`src/api/pallet.js` | 扫码查询托盘内容,按服务端返回的托盘/库存版本执行组托、拆托、换托和解绑;公网前缀 `/api/pda/v1/last-leg/pallet-operations/**` |
|
||||
| 库存盘点真实作业 | `src/pages/stocktake/stocktake.vue`、`src/api/warehouse-operations.js`、`src/stores/warehouse-work.js` | 创建/恢复盘点任务,扫描库位和库存后计数、提交;严格使用服务端版本和幂等键 |
|
||||
| 商品移库真实作业 | `src/pages/move/move.vue`、`src/api/warehouse-operations.js`、`src/stores/warehouse-work.js` | 扫描来源托盘与目标库位执行同仓移库;冲突后重新读取服务端事实 |
|
||||
|
||||
@@ -42,7 +42,11 @@ export function normalizeReceivingInbound(value = {}) {
|
||||
plannedPalletCount: number(value.plannedPalletCount),
|
||||
totalPackages: number(value.totalPackages),
|
||||
receivedPackages: number(value.receivedPackages),
|
||||
pendingPackages: number(value.pendingPackages)
|
||||
pendingPackages: number(value.pendingPackages),
|
||||
assigned: value.assigned === true,
|
||||
mergeAllowed: value.mergeAllowed === true,
|
||||
urgent: value.urgent === true,
|
||||
mergeHint: text(value.mergeHint)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,16 @@
|
||||
<view><text class="metric-value">{{ totalPackages }}</text><text class="metric-label">计划箱数</text></view>
|
||||
<view><text class="metric-value metric-pending">{{ pendingPackages }}</text><text class="metric-label">待收箱数</text></view>
|
||||
</view>
|
||||
<view v-if="planFlags.length" class="receiving-plan-flags">
|
||||
<view v-for="item in planFlags" :key="item.inboundCode" class="receiving-plan-row">
|
||||
<text class="receiving-plan-code">{{ item.inboundCode || '—' }}</text>
|
||||
<view class="receiving-plan-tags">
|
||||
<text :class="item.assigned ? 'flag-positive' : 'flag-muted'">{{ item.assigned ? '已分配' : '未分配' }}</text>
|
||||
<text :class="item.mergeAllowed ? 'flag-option' : 'flag-muted'">{{ item.mergeAllowed ? '可合托·非强制' : '不合托' }}</text>
|
||||
<text :class="item.urgent ? 'flag-urgent' : 'flag-muted'">{{ item.urgent ? '加急' : '普通' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="receiving-summary-progress"><view><text>收货进度</text><text>{{ receivedPackages }} / {{ totalPackages }} 箱</text></view><view class="progress-track"><view class="progress-fill" :style="{ width: percent + '%' }"></view></view></view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -22,6 +32,7 @@
|
||||
export default {
|
||||
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) },
|
||||
@@ -42,6 +53,7 @@
|
||||
.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-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}
|
||||
.receiving-summary-progress{margin-top:7px;color:#607287;font-size:9px}.progress-track{height:5px;margin-top:5px;overflow:hidden;border-radius:99px;background:#dfe8f2}.progress-fill{height:100%;border-radius:inherit;background:#1677e8}
|
||||
</style>
|
||||
|
||||
@@ -74,6 +74,8 @@
|
||||
<view class="matched-inbound-card">
|
||||
<view class="mode-banner" :class="activeInbound.destinationType === 'FBA' ? 'fba' : 'overseas'"><text>{{ activeInbound.destinationType === 'FBA' ? 'FBA 按箱数收货' : '海外仓逐箱收货' }}</text><text>{{ destinationLabel(activeInbound) }}</text></view>
|
||||
<text class="matched-inbound-code">{{ activeInbound.inboundCode }}</text>
|
||||
<view class="matched-plan-flags"><text>{{ activeInbound.assigned ? '已分配' : '未分配' }}</text><text :class="{ optional: activeInbound.mergeAllowed }">{{ activeInbound.mergeAllowed ? '可合托(现场自选)' : '不合托' }}</text><text :class="{ urgent: activeInbound.urgent }">{{ activeInbound.urgent ? '加急' : '普通' }}</text></view>
|
||||
<text v-if="activeInbound.mergeHint" class="merge-option-hint">{{ activeInbound.mergeHint }}</text>
|
||||
<view class="matched-inbound-progress"><text>总计 {{ activeInbound.totalPackages }} 箱</text><text>已收 {{ activeInbound.receivedPackages }} 箱</text><text>待收 {{ activeInbound.pendingPackages }} 箱</text></view>
|
||||
</view>
|
||||
<text class="section-label">2. 扫描实物托盘码</text>
|
||||
@@ -199,6 +201,7 @@
|
||||
.matched-inbound-card{margin:14px 0;padding:12px;border:1px solid #cfe0f2;border-radius:10px;background:#f8fbff}
|
||||
.matched-inbound-card .mode-banner{margin-bottom:10px}
|
||||
.matched-inbound-code{display:block;color:#17324d;font-size:15px;font-weight:800}
|
||||
.matched-plan-flags{display:flex;flex-wrap:wrap;gap:5px;margin-top:7px}.matched-plan-flags text{padding:3px 7px;border-radius:99px;color:#617389;font-size:9px;background:#edf1f5}.matched-plan-flags .optional{color:#1668dc;background:#e7f0ff}.matched-plan-flags .urgent{color:#b54708;background:#fff0dc}.merge-option-hint{display:block;margin-top:7px;padding:7px 8px;border-radius:7px;color:#1668dc;font-size:10px;line-height:15px;background:#eef5ff}
|
||||
.matched-inbound-progress{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;margin-top:8px;color:#5f7185;font-size:10px}
|
||||
.matched-inbound-progress text{padding:6px 4px;border-radius:6px;text-align:center;background:#edf3f9}
|
||||
.error-card text{overflow-wrap:anywhere;word-break:break-word}
|
||||
|
||||
@@ -15,7 +15,11 @@ const inbounds = Array.from({ length: 15 }, (_, index) => ({
|
||||
plannedPalletCount: 1,
|
||||
totalPackages: 4,
|
||||
receivedPackages: index === 0 ? 4 : 0,
|
||||
pendingPackages: index === 0 ? 0 : 4
|
||||
pendingPackages: index === 0 ? 0 : 4,
|
||||
assigned: index % 3 === 0,
|
||||
mergeAllowed: index % 4 === 0,
|
||||
urgent: index === 0,
|
||||
mergeHint: index % 4 === 0 ? '可选择合托,不强制' : ''
|
||||
}))
|
||||
|
||||
test('one container candidate aggregates fifteen independent one-inbound plans', () => {
|
||||
@@ -85,6 +89,9 @@ test('current recovery retains all independent-plan inbound facts while progress
|
||||
assert.deepEqual(receivingProgress(), { total: 60, received: 4, pending: 56, percent: 7 })
|
||||
assert.equal(receiving.current.inbounds.length, 15)
|
||||
assert.equal(receiving.current.inbounds[0].inboundCode, 'NYEXX2608270001')
|
||||
assert.equal(receiving.current.inbounds[0].assigned, true)
|
||||
assert.equal(receiving.current.inbounds[0].mergeAllowed, true)
|
||||
assert.equal(receiving.current.inbounds[0].urgent, true)
|
||||
assert.equal(receiving.current.inbounds[14].inboundCode, 'NYEXX2608270015')
|
||||
resetReceivingSession()
|
||||
})
|
||||
@@ -98,8 +105,10 @@ test('container UI locks and signs once across independent plans, then renders o
|
||||
|
||||
assert.match(operation, /item\.totalOrderCount[\s\S]*item\.inboundCodeCount[\s\S]*item\.planCount[\s\S]*item\.pendingPlanCount/)
|
||||
assert.match(summary, /inboundCodeCount[\s\S]*plannedPalletCount[\s\S]*totalPackages/)
|
||||
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\.inboundCode[\s\S]*activeInbound\.pendingPackages/)
|
||||
assert.match(operation, /activeInbound\.mergeAllowed[\s\S]*现场自选[\s\S]*activeInbound\.urgent/)
|
||||
assert.match(operation, /resolveCurrentCarton\(cartonMark\)/)
|
||||
assert.doesNotMatch(operation, /cartonMark\.split|\.split\([^\n]*['"]#['"]/)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user