This commit is contained in:
2026-09-21 11:17:17 +08:00
parent cec0027376
commit e4f02c5e73
9 changed files with 66 additions and 26 deletions
+14 -5
View File
@@ -7,6 +7,7 @@
<text class="fulfillment-title">排车拣货与发车</text>
<text class="fulfillment-subtitle">客户与当前海外仓由登录令牌和服务端确定;扫描排车单号或任务中的托盘码开始作业</text>
</view>
<view v-if="current" class="detail-toolbar"><view><text class="detail-kicker">当前任务</text><text class="detail-title">排车拣货详情</text></view><button :disabled="busy" @tap="closeTask">‹ 返回列表</button></view>
<view class="fulfillment-card">
<ScanTargetInput name="dispatch" :model-value="scanCode" label="排车单号或托盘码" placeholder="扫描排车单号 / 托盘码" :active="true" @update:modelValue="updateScanCode" @submit="handleScan" />
@@ -25,11 +26,12 @@
</view>
<template v-if="!loading && !current">
<view class="list-toolbar"><text>待处理排车任务</text><button :disabled="loading" @tap="loadTasks">刷新</button></view>
<view class="list-toolbar"><view><text class="list-title">待处理排车任务</text><text class="list-count">{{ tasks.length }} 个任务</text></view><button :disabled="loading" @tap="loadTasks">刷新</button></view>
<view v-if="tasks.length" class="task-list">
<button v-for="task in tasks" :key="task.dispatchNo" class="task-card" @tap="openTask(task.dispatchNo)">
<view class="task-head"><text class="business-code">{{ task.dispatchNo || '—' }}</text><text class="status-badge">{{ statusLabel(task.status) }}</text></view>
<view class="task-head"><view class="task-code-row"><text class="business-code">{{ task.dispatchNo || '—' }}</text><text v-if="task.dispatchNo === recentTaskNo" class="recent-badge">上次作业</text></view><text class="status-badge">{{ statusLabel(task.status) }}</text></view>
<text class="task-meta">任务号 {{ task.taskNo || '—' }} · 车牌 {{ task.vehiclePlate || '—' }}</text>
<text class="task-enter">查看并开始拣货 ›</text>
</button>
</view>
<view v-else-if="!error" class="fulfillment-state"><text class="state-title">暂无排车拣货任务</text><text>可刷新列表,或直接扫描排车单号恢复作业。</text></view>
@@ -40,7 +42,6 @@
<view class="task-head"><text class="business-code">{{ current.dispatchNo || '—' }}</text><text class="status-badge">{{ statusLabel(current.status) }}</text></view>
<text class="task-meta">任务号 {{ current.taskNo || '—' }} · 车牌 {{ current.vehiclePlate || '—' }}</text>
<view class="metrics"><view><text class="metric-value">{{ plannedPackages }}</text><text>计划箱数</text></view><view><text class="metric-value success">{{ pickedPackages }}</text><text>已拣箱数</text></view><view><text class="metric-value warning">{{ remainingPackages }}</text><text>待拣箱数</text></view></view>
<button class="link-button" :disabled="busy" @tap="closeTask">返回任务列表</button>
</view>
<view v-if="current.items.length" class="item-list">
@@ -95,8 +96,9 @@
export default {
components: { PortalHeader, ScanTargetInput },
data() { return { user: session.currentUser, tasks: [], current: null, scanCode: '', loading: false, busy: false, busyAction: '', error: null, successMessage: '' } },
data() { return { user: session.currentUser, tasks: [], current: null, scanCode: '', loading: false, busy: false, busyAction: '', error: null, successMessage: '', bootstrapped: false, resumeOnEntry: false } },
computed: {
recentTaskNo() { return fulfillment.dispatch.currentNo || '' },
packages() { return fulfillment.dispatch.packages },
selectedItem() { return this.current?.items?.find((item) => item.palletNo === fulfillment.dispatch.palletCode) || null },
selectedPosition() { return this.selectedItem?.positions?.find((item) => item.availablePackages > 0) || this.selectedItem?.positions?.[0] || null },
@@ -112,12 +114,19 @@
canComplete() { return this.plannedPackages > 0 && this.remainingPackages === 0 },
canDepart() { return this.pickingCompleted && this.allPicked && this.current?.canDepart === true && !this.departed }
},
onLoad(options) { this.resumeOnEntry = options?.resume === '1' },
onShow() {
if (!requireSession()) return
this.user = session.currentUser
restoreFulfillmentDraft(`${this.user?.id || ''}:${this.user?.warehouseId || ''}`)
this.startScanner()
if (fulfillment.dispatch.currentNo) this.openTask(fulfillment.dispatch.currentNo)
if (!this.bootstrapped) {
this.bootstrapped = true
if (this.resumeOnEntry && fulfillment.dispatch.currentNo) this.openTask(fulfillment.dispatch.currentNo)
else this.loadTasks()
return
}
if (this.current?.dispatchNo) this.openTask(this.current.dispatchNo)
else this.loadTasks()
},
onHide() { this.stopScanner() },
+9
View File
@@ -21,13 +21,22 @@
.offline-note { margin-top: 7px; color: #8a5a00; }
.list-toolbar, .task-head, .count-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.list-toolbar { margin: 16px 2px 10px; color: #233951; font-size: 13px; font-weight: 800; }
.list-title, .list-count, .detail-kicker, .detail-title, .task-enter { display: block; }
.list-count { margin-top: 2px; color: #8795a7; font-size: 10px; font-weight: 500; }
.list-toolbar button { min-height: 32px; margin: 0; padding: 0 12px; border-radius: 7px; color: #135ee8; font-size: 11px; line-height: 32px; background: #e9f2ff; }
.task-list, .item-list { display: flex; flex-direction: column; gap: 9px; }
.task-card { box-sizing: border-box; width: 100%; min-height: 76px; margin: 0; padding: 13px; border: 1px solid #dce5f0; border-radius: 11px; text-align: left; background: #fff; }
.task-card::after { border: 0; }
.task-code-row { display: flex; min-width: 0; align-items: center; gap: 7px; }
.business-code { color: #16304d; font-size: 15px; font-weight: 800; overflow-wrap: anywhere; }
.recent-badge { flex-shrink: 0; padding: 3px 6px; border-radius: 99px; color: #8a570c; font-size: 9px; line-height: 14px; background: #fff1dd; }
.status-badge { flex-shrink: 0; padding: 4px 7px; border-radius: 99px; color: #165fbf; font-size: 10px; background: #e6f0ff; }
.task-meta { margin-top: 6px; color: #718095; font-size: 10px; line-height: 16px; }
.task-enter { margin-top: 8px; color: #135ee8; font-size: 10px; font-weight: 700; text-align: right; }
.detail-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 4px 0 10px; padding: 10px 12px; border: 1px solid #bed4f6; border-radius: 11px; background: #eef5ff; }
.detail-kicker { color: #6580a4; font-size: 9px; }
.detail-title { margin-top: 2px; color: #153d74; font-size: 14px; font-weight: 800; }
.detail-toolbar button { flex-shrink: 0; min-height: 34px; margin: 0; padding: 0 11px; border-radius: 8px; color: #135ee8; font-size: 10px; line-height: 34px; background: #fff; }
.summary-card { display: block; }
.metrics { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); margin-top: 12px; padding: 10px 0; border-radius: 9px; background: #f5f8fc; }
.metrics view { display: flex; align-items: center; flex-direction: column; gap: 3px; color: #718095; font-size: 9px; }