Merge origin/dev into release/test preserving current workflows
This commit is contained in:
@@ -54,12 +54,13 @@
|
||||
</view>
|
||||
|
||||
<view class="section-card">
|
||||
<view class="section-heading"><view><text class="section-title">2. 托盘操作</text><text class="section-desc">直接扫描托盘码,只显示当前操作的托盘</text></view><button class="text-button" :disabled="loading" @tap="loadTask">刷新</button></view>
|
||||
<view class="section-heading"><view><text class="section-title">2. 托盘操作</text><text class="section-desc">直接扫描托盘码,只显示当前操作的托盘;待绑托箱扫描目标托盘后批量归托</text></view><button class="text-button" :disabled="loading" @tap="loadTask">刷新</button></view>
|
||||
<view class="batch-print-row"><text>已生成 {{ task.generatedPalletCount || 0 }} 个托盘码</text><button class="text-button batch-print" :disabled="printing || !task.generatedPalletCount" @tap="printAllPallets">打开全部 A4 托盘单</button></view>
|
||||
<view v-if="currentPallet" class="pallet-card">
|
||||
<view class="pallet-head"><text class="pallet-code">{{ currentPallet.palletNo }}</text><text class="pallet-status" :class="currentPallet.status === 'SEALED' ? 'sealed' : ''">{{ currentPallet.status === 'SEALED' ? '已封托' : '当前托盘' }}</text></view>
|
||||
<text class="pallet-meta">{{ currentPallet.cartonCount || 0 }} 箱 · {{ currentPallet.skuCount || 0 }} SKU · {{ currentPallet.weightKg || 0 }} kg · {{ currentPallet.volumeCbm || 0 }} m³</text>
|
||||
<button v-if="currentPallet.palletId" class="secondary-button" :disabled="printing" @tap="printPallet(currentPallet)">{{ printing ? '正在生成 A4 文件…' : '打开当前托盘 A4 单' }}</button>
|
||||
<view v-for="item in (currentPalletDetails?.cartons || [])" :key="item.cartonId" class="carton-row"><text>{{ item.boxCode }}</text><button v-if="currentPallet.status === 'OPEN'" class="text-button" :disabled="submitting" @tap="moveCarton(item, null)">移回待绑托</button></view>
|
||||
<button v-if="currentPallet.palletId && currentPallet.status === 'OPEN'" class="primary-button" :disabled="!currentPallet.cartonCount || submitting" @tap="sealPallet(currentPallet)">封托</button>
|
||||
</view>
|
||||
</view>
|
||||
@@ -71,9 +72,13 @@
|
||||
<script>
|
||||
import PortalHeader from '@/components/PortalHeader.vue'
|
||||
import ScanTargetInput from '@/components/ScanTargetInput.vue'
|
||||
import { bindDropshipPendingCartons, claimDropshipPalletizationTask, countDropshipCarton, downloadAllDropshipPalletLabels, downloadDropshipPalletLabel, getDropshipPalletizationTask, resolveDropshipCarton, sealDropshipPallet } from '@/api/pda-dropship.js'
|
||||
import { bindDropshipPendingCartons, claimDropshipPalletizationTask, countDropshipCarton, downloadAllDropshipPalletLabels, downloadDropshipPalletLabel, getDropshipPalletizationTask, moveDropshipCarton, resolveDropshipCarton, sealDropshipPallet } from '@/api/pda-dropship.js'
|
||||
import { session, requireSession } from '@/stores/session.js'
|
||||
import { formatApiError } from '@/utils/user-facing.js'
|
||||
import { stableOperationId } from '@/utils/idempotency.js'
|
||||
import { isOnline } from '@/utils/network.js'
|
||||
import { clearDropshipPalletizingDraft, restoreDropshipPalletizingDraft, saveDropshipPalletizingDraft } from '@/stores/dropship-palletizing.js'
|
||||
import { createDropshipScanState, planDropshipScan, DROPSHIP_SCAN_INTENT } from '@/utils/dropship-palletizing-state.js'
|
||||
import { startScanBroadcast } from '@/utils/scan-broadcast.js'
|
||||
|
||||
export default {
|
||||
@@ -89,17 +94,17 @@
|
||||
currentPallet() { return this.currentPalletDetails?.status === 'OPEN' ? this.currentPalletDetails : (this.selectedPalletNo ? { palletNo: this.selectedPalletNo, status: 'OPEN' } : null) }
|
||||
},
|
||||
onLoad(options) { this.taskId = String(options.taskId || ''); this.warehouseId = String(options.warehouseId || '') },
|
||||
onShow() { if (!requireSession()) return; this.user = session.currentUser; this.startScanner(); this.loadTask() },
|
||||
onShow() { if (!requireSession()) return; this.user = session.currentUser; const draft = restoreDropshipPalletizingDraft(this.draftIdentity()); this.selectedPalletNo = draft?.activePalletNo || ''; this.startScanner(); this.loadTask() },
|
||||
onHide() { this.stopScanner() }, onUnload() { this.stopScanner() }, beforeUnmount() { this.stopScanner() },
|
||||
methods: {
|
||||
startScanner() { this.stopScanner(); this._stopScanner = startScanBroadcast({ onScan: (value) => this.handleScan(value), onError: () => { this.error = '扫码头连接失败,可使用输入框继续作业' } }) },
|
||||
stopScanner() { if (this._stopScanner) this._stopScanner(); this._stopScanner = null },
|
||||
handleScan(value) { if (!this.palletSetupReady) { this.error = this.palletSetupMessage; return } const code = String(value).trim().toUpperCase(); if (code.startsWith('DP-')) { this.palletCode = code; this.selectPallet(code) } else { this.boxCode = code; this.resolveCarton() } },
|
||||
handleScan(value) { if (!this.palletSetupReady) { this.error = this.palletSetupMessage; return } const plan = planDropshipScan(createDropshipScanState({ online: isOnline() }), value, { unboundCartons: this.task?.unboundCartons }); if ([DROPSHIP_SCAN_INTENT.BLOCKED, DROPSHIP_SCAN_INTENT.INVALID].includes(plan.intent)) { this.error = plan.message; return } const code = plan.palletNo || plan.boxCode; if (plan.palletNo) { this.palletCode = code; this.selectPallet(code) } else { this.boxCode = code; this.resolveCarton() } },
|
||||
resetMessages() { this.error = ''; this.notice = '' },
|
||||
async loadTask() {
|
||||
if (!this.taskId || !this.warehouseId) { this.error = '缺少合托任务或仓库信息,请返回任务列表'; return }
|
||||
this.loading = true; this.resetMessages()
|
||||
try { this.task = await getDropshipPalletizationTask(this.taskId, this.warehouseId) }
|
||||
try { this.task = await getDropshipPalletizationTask(this.taskId, this.warehouseId); if (this.task.taskStatus === 'COMPLETED') clearDropshipPalletizingDraft(this.draftIdentity()) }
|
||||
catch (error) { this.error = formatApiError(error, '一件代发入库任务加载失败') }
|
||||
finally { this.loading = false }
|
||||
},
|
||||
@@ -123,10 +128,17 @@
|
||||
if (!this.palletSetupReady) { this.error = this.palletSetupMessage; return }
|
||||
const code = String(value || this.palletCode || '').trim().toUpperCase(); const pallet = (this.task?.pallets || []).find((item) => item.palletNo === code)
|
||||
if (pallet && pallet.status !== 'OPEN') { this.error = '托盘已封托,不能继续归托'; return }
|
||||
this.palletCode = code; this.selectedPalletId = pallet ? String(pallet.palletId) : ''; this.selectedPalletNo = code; this.activeScan = 'boxCode'
|
||||
if (Number(this.task?.unboundCartons || 0) > 0) await this.runMutation(() => bindDropshipPendingCartons(this.taskId, this.warehouseId, code), `待归托箱已绑定至 ${code}`)
|
||||
this.palletCode = code; this.selectedPalletId = pallet ? String(pallet.palletId) : ''; this.selectedPalletNo = code; this.activeScan = 'boxCode'; saveDropshipPalletizingDraft(this.draftIdentity(), { activePalletNo: code })
|
||||
if (Number(this.task?.unboundCartons || 0) > 0) await this.runMutation(() => bindDropshipPendingCartons(this.taskId, this.warehouseId, code, this.task.version), `待归托箱已绑定至 ${code}`)
|
||||
else this.notice = pallet ? `当前托盘:${code}` : `已选择仓库开放托盘 ${code},清点箱码时将校验并归托`
|
||||
},
|
||||
draftIdentity() { return { operatorId: this.user?.id, taskId: this.taskId, warehouseId: this.warehouseId } },
|
||||
async moveCarton(carton, targetPalletNo) {
|
||||
if (this.submitting || !isOnline() || !this.palletSetupReady) return
|
||||
const payload = { warehouseId: this.warehouseId, targetPalletNo: targetPalletNo || null }
|
||||
payload.operationId = stableOperationId('dropship-move', String(this.user?.id) + '-' + this.taskId, { cartonId: carton.cartonId, taskVersion: this.task.version, ...payload })
|
||||
await this.runMutation(() => moveDropshipCarton(this.taskId, carton.cartonId, payload), targetPalletNo ? '箱码已换托' : '箱码已移回待绑托区')
|
||||
},
|
||||
continueOtherTask() { uni.redirectTo({ url: `/pages/dropship-tasks/dropship-tasks?warehouseId=${encodeURIComponent(this.warehouseId)}` }) },
|
||||
async sealPallet(pallet) { const result = await this.runMutation(() => sealDropshipPallet(this.taskId, this.warehouseId, pallet), `${pallet.palletNo} 已封托`); if (result && this.selectedPalletNo === pallet.palletNo) { this.selectedPalletId = ''; this.selectedPalletNo = ''; this.palletCode = '' } },
|
||||
async printPallet(pallet) {
|
||||
@@ -161,6 +173,7 @@
|
||||
},
|
||||
async runMutation(action, message) {
|
||||
if (this.submitting) return null
|
||||
if (!isOnline()) { this.error = '当前网络不可用,请联网后刷新'; return null }
|
||||
this.submitting = true; this.resetMessages()
|
||||
try {
|
||||
const result = await action()
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
import { session, requireSession, selectWarehouse } from '@/stores/session.js'
|
||||
|
||||
const ICON_TYPES = {
|
||||
'一件代发清点组托': 'download',
|
||||
'到货收货': 'download', '商品上架': 'upload', '入库质检': 'checkbox',
|
||||
'一件代发': 'map', '出库拣货': 'cart', '出库复核': 'compose', '装箱交接': 'paperplane',
|
||||
'库存盘点': 'list', '商品移库': 'loop', '库存调整': 'compose',
|
||||
|
||||
@@ -524,6 +524,10 @@
|
||||
if (!requireSession()) return
|
||||
this.currentUser = session.currentUser
|
||||
if (options.taskId) {
|
||||
// Session display state can belong to the task opened before this route.
|
||||
// Only the scoped draft restored after loading this task may restore its history.
|
||||
this.scanValue = ''
|
||||
this.scanHistory = []
|
||||
// A local completion marker belongs to the previously opened task. The
|
||||
// canonical server detail decides whether this task is complete.
|
||||
this.completedBoxes = {}
|
||||
@@ -540,6 +544,8 @@
|
||||
},
|
||||
async onShow() {
|
||||
if (!requireSession()) return
|
||||
if (this._pageVisible) return this._showRefreshPromise
|
||||
this._pageVisible = true
|
||||
this.currentUser = session.currentUser
|
||||
this.startHardwareScanner()
|
||||
if (this.cameraPicking) {
|
||||
@@ -551,10 +557,16 @@
|
||||
this.debugEvidence('preview.resume', { evidenceIdentity: this.evidenceIdentity })
|
||||
return
|
||||
}
|
||||
await this.refreshAvailableBoxes()
|
||||
await this.syncRemoteTask()
|
||||
if (this._showRefreshPromise) return this._showRefreshPromise
|
||||
this._showRefreshPromise = (async () => {
|
||||
await this.refreshAvailableBoxes()
|
||||
await this.syncRemoteTask()
|
||||
})()
|
||||
try { await this._showRefreshPromise }
|
||||
finally { this._showRefreshPromise = null }
|
||||
},
|
||||
onHide() {
|
||||
this._pageVisible = false
|
||||
if (this.evidencePreviewOpening) {
|
||||
this.evidencePreviewOpening = false
|
||||
this.evidencePreviewReturning = true
|
||||
|
||||
Reference in New Issue
Block a user