优化
This commit is contained in:
+5
-7
@@ -160,12 +160,11 @@ export async function submitContainerPickingBatch(task, actions, { warehouseId,
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function listLoadingOrders({ page = 1, size = 50, keyword = '' } = {}) {
|
export function listLoadingOrders({ page = 1, size = 50, keyword = '' } = {}) {
|
||||||
const query = [`mainStatus=LOADING`, `page=${page}`, `size=${size}`]
|
const query = []
|
||||||
if (session.tenantId) query.push(`tenantId=${encodeURIComponent(session.tenantId)}`)
|
|
||||||
if (keyword) query.push(`keyword=${encodeURIComponent(keyword)}`)
|
if (keyword) query.push(`keyword=${encodeURIComponent(keyword)}`)
|
||||||
return request({ url: `/orders?${query.join('&')}`, method: 'GET' }).then((data) => ({
|
const suffix = query.length ? `?${query.join('&')}` : ''
|
||||||
...data,
|
return request({ url: `/first-leg/pda-loading/tasks/pending${suffix}`, method: 'GET' }).then((data) => ({
|
||||||
records: (data && data.records ? data.records : []).map(normalizePdaOrder)
|
page, size, records: (Array.isArray(data) ? data : data?.records || []).map(normalizePdaOrder)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,12 +175,11 @@ export function normalizePdaOrder(row = {}) {
|
|||||||
inboundId: row.inboundId ?? row.inbound?.id ?? null,
|
inboundId: row.inboundId ?? row.inbound?.id ?? null,
|
||||||
inboundVersion: row.inboundVersion ?? row.inbound?.version ?? null,
|
inboundVersion: row.inboundVersion ?? row.inbound?.version ?? null,
|
||||||
tenantId: row.tenantId ?? row.accountId ?? row.inbound?.tenantId ?? null,
|
tenantId: row.tenantId ?? row.accountId ?? row.inbound?.tenantId ?? null,
|
||||||
tenantId: row.tenantId ?? row.accountId ?? row.inbound?.tenantId ?? null,
|
|
||||||
pickingVersion: row.pickingVersion ?? row.loading?.pickingVersion ?? null,
|
pickingVersion: row.pickingVersion ?? row.loading?.pickingVersion ?? null,
|
||||||
taskId: row.taskId ?? row.pdaTaskId ?? row.loadingTaskId ?? row.loading?.taskId ?? null,
|
taskId: row.taskId ?? row.pdaTaskId ?? row.loadingTaskId ?? row.loading?.taskId ?? null,
|
||||||
containerNo: row.containerNo ?? row.container?.containerNo ?? row.loadingPlan?.containerNo ?? row.loading?.containerNo ?? '',
|
containerNo: row.containerNo ?? row.container?.containerNo ?? row.loadingPlan?.containerNo ?? row.loading?.containerNo ?? '',
|
||||||
warehouseId: row.warehouseId ?? row.warehouse?.id ?? row.loadingPlan?.warehouseId ?? null,
|
warehouseId: row.warehouseId ?? row.warehouse?.id ?? row.loadingPlan?.warehouseId ?? null,
|
||||||
displayNo: row.orderNo ?? row.inboundCode ?? row.order?.orderNo ?? row.id
|
displayNo: row.orderNo ?? row.inboundCode ?? row.order?.orderNo ?? `任务 ${row.taskId ?? row.id}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+48
-27
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
<view class="scan-section">
|
<view class="scan-section">
|
||||||
<view class="scan-row">
|
<view class="scan-row">
|
||||||
<ScanTargetInput v-model="scanValue" name="sku" label="SKU 扫码框" placeholder="扫描或输入 M001" :active="activeScanField === 'sku'" @activate="setScanTarget" @submit="handleManualSubmit" />
|
<ScanTargetInput v-model="scanValue" name="boxCode" label="箱码扫码框" placeholder="扫描或输入箱唛标签码" :active="activeScanField === 'boxCode'" @activate="setScanTarget" @submit="handleManualSubmit" />
|
||||||
</view>
|
</view>
|
||||||
<text class="scan-hardware-tip">支持硬件广播扫码,也可点击输入后按“完成”确认</text>
|
<text class="scan-hardware-tip">支持硬件广播扫码,也可点击输入后按“完成”确认</text>
|
||||||
<view v-if="lastScanResult" class="last-scan" :class="lastScanResult.type" role="status">
|
<view v-if="lastScanResult" class="last-scan" :class="lastScanResult.type" role="status">
|
||||||
@@ -58,11 +58,11 @@
|
|||||||
<template v-if="summary.partial > 0">
|
<template v-if="summary.partial > 0">
|
||||||
<text>已拣 </text><text class="progress-number">{{ summary.picked }}</text>
|
<text>已拣 </text><text class="progress-number">{{ summary.picked }}</text>
|
||||||
<text> · 部分拣 </text><text class="progress-number">{{ summary.partial }}</text>
|
<text> · 部分拣 </text><text class="progress-number">{{ summary.partial }}</text>
|
||||||
<text> / {{ summary.total }} 件</text>
|
<text> / {{ summary.total }} 箱</text>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<text>已拣 </text><text class="progress-number">{{ summary.picked }}</text>
|
<text>已拣 </text><text class="progress-number">{{ summary.picked }}</text>
|
||||||
<text> / {{ summary.total }} 件</text>
|
<text> / {{ summary.total }} 箱</text>
|
||||||
</template>
|
</template>
|
||||||
</view>
|
</view>
|
||||||
<button class="box-tasks-btn" @tap="openBoxList('pending')">箱任务 {{ completedCount }}/{{ boxes.length }}</button>
|
<button class="box-tasks-btn" @tap="openBoxList('pending')">箱任务 {{ completedCount }}/{{ boxes.length }}</button>
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
:class="itemState(item)"
|
:class="itemState(item)"
|
||||||
>
|
>
|
||||||
<view class="pick-item-row1">
|
<view class="pick-item-row1">
|
||||||
<text class="pick-item-sku">{{ item.sku }}</text>
|
<text class="pick-item-sku">{{ item.inboundCode || item.orderNo || '箱级拣货明细' }}</text>
|
||||||
<view class="pick-item-meta">
|
<view class="pick-item-meta">
|
||||||
<text v-if="itemState(item) === ITEM_STATUS.PICKED" class="pick-status-tag tag-picked">已拣 ✓</text>
|
<text v-if="itemState(item) === ITEM_STATUS.PICKED" class="pick-status-tag tag-picked">已拣 ✓</text>
|
||||||
<text v-else-if="itemState(item) === ITEM_STATUS.PARTIAL" class="pick-status-tag tag-partial">
|
<text v-else-if="itemState(item) === ITEM_STATUS.PARTIAL" class="pick-status-tag tag-partial">
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
</text>
|
</text>
|
||||||
<text v-else-if="itemState(item) === ITEM_STATUS.SKIPPED" class="pick-status-tag tag-skipped">已跳拣</text>
|
<text v-else-if="itemState(item) === ITEM_STATUS.SKIPPED" class="pick-status-tag tag-skipped">已跳拣</text>
|
||||||
<view class="pick-item-qty">
|
<view class="pick-item-qty">
|
||||||
<text>需拣 </text><text class="qty-number">{{ item.qty }}</text><text> {{ item.unit }}</text>
|
<text>需拣 </text><text class="qty-number">{{ item.qty }}</text><text> 箱</text>
|
||||||
<template v-if="itemState(item) === ITEM_STATUS.PARTIAL">
|
<template v-if="itemState(item) === ITEM_STATUS.PARTIAL">
|
||||||
<text> · 实拣 </text><text class="actual-number">{{ partialQuantities[item.id] || 0 }}</text>
|
<text> · 实拣 </text><text class="actual-number">{{ partialQuantities[item.id] || 0 }}</text>
|
||||||
</template>
|
</template>
|
||||||
@@ -147,7 +147,7 @@
|
|||||||
|
|
||||||
<view class="submit-section">
|
<view class="submit-section">
|
||||||
<view class="submit-info">
|
<view class="submit-info">
|
||||||
<view class="submit-pending"><text class="submit-pending-number">{{ summary.pending }}</text><text class="submit-pending-label"> 件待拣</text></view>
|
<view class="submit-pending"><text class="submit-pending-number">{{ summary.pending }}</text><text class="submit-pending-label"> 箱待拣</text></view>
|
||||||
<text class="submit-status-hint">{{ submitStatusHint }}</text>
|
<text class="submit-status-hint">{{ submitStatusHint }}</text>
|
||||||
</view>
|
</view>
|
||||||
<button class="submit-btn" aria-label="提交拣货完成" :disabled="submitting || completedBoxes[currentBoxIndex] || !summary.canSubmit" @tap="submitPicking">
|
<button class="submit-btn" aria-label="提交拣货完成" :disabled="submitting || completedBoxes[currentBoxIndex] || !summary.canSubmit" @tap="submitPicking">
|
||||||
@@ -340,7 +340,7 @@
|
|||||||
skipReasons: SKIP_REASONS,
|
skipReasons: SKIP_REASONS,
|
||||||
incompleteReasons: INCOMPLETE_REASONS,
|
incompleteReasons: INCOMPLETE_REASONS,
|
||||||
...getPickingSession(),
|
...getPickingSession(),
|
||||||
activeScanField: 'sku',
|
activeScanField: 'boxCode',
|
||||||
partialFocused: false,
|
partialFocused: false,
|
||||||
boxDropdownOpen: false,
|
boxDropdownOpen: false,
|
||||||
activeOverlay: '',
|
activeOverlay: '',
|
||||||
@@ -364,7 +364,7 @@
|
|||||||
return this.boxes[this.currentBoxIndex] || { boxNo: '', orderNo: '', items: [] }
|
return this.boxes[this.currentBoxIndex] || { boxNo: '', orderNo: '', items: [] }
|
||||||
},
|
},
|
||||||
summary() {
|
summary() {
|
||||||
return getPickSummary(this.currentBox.items, this.itemStates)
|
return getPickSummary(this.currentBox.items, this.itemStates, this.partialQuantities)
|
||||||
},
|
},
|
||||||
orderedItems() {
|
orderedItems() {
|
||||||
const pending = []
|
const pending = []
|
||||||
@@ -489,6 +489,7 @@
|
|||||||
const itemIds = new Set(this.currentBox.items.map((item) => String(item.id)))
|
const itemIds = new Set(this.currentBox.items.map((item) => String(item.id)))
|
||||||
this.itemStates = Object.fromEntries(Object.entries(restored.itemStates || {}).filter(([id]) => itemIds.has(String(id))))
|
this.itemStates = Object.fromEntries(Object.entries(restored.itemStates || {}).filter(([id]) => itemIds.has(String(id))))
|
||||||
this.partialQuantities = Object.fromEntries(Object.entries(restored.partialQuantities || {}).filter(([id]) => itemIds.has(String(id))))
|
this.partialQuantities = Object.fromEntries(Object.entries(restored.partialQuantities || {}).filter(([id]) => itemIds.has(String(id))))
|
||||||
|
this.scannedBoxCodes = { ...(restored.scannedBoxCodes || {}) }
|
||||||
this.boxDrafts = restored.boxDrafts || {}
|
this.boxDrafts = restored.boxDrafts || {}
|
||||||
this.completedBoxes = restored.completedBoxes || {}
|
this.completedBoxes = restored.completedBoxes || {}
|
||||||
this.scanHistory = restored.scanHistory || []
|
this.scanHistory = restored.scanHistory || []
|
||||||
@@ -511,7 +512,7 @@
|
|||||||
},
|
},
|
||||||
handleHardwareScan(value) {
|
handleHardwareScan(value) {
|
||||||
if (this.activeOverlay || this.boxDropdownOpen) return
|
if (this.activeOverlay || this.boxDropdownOpen) return
|
||||||
if (this.activeScanField !== 'sku') return
|
if (this.activeScanField !== 'boxCode') return
|
||||||
this.scanValue = value
|
this.scanValue = value
|
||||||
this.processScannedValue(value, 'broadcast')
|
this.processScannedValue(value, 'broadcast')
|
||||||
},
|
},
|
||||||
@@ -531,7 +532,7 @@
|
|||||||
},
|
},
|
||||||
persistSession() {
|
persistSession() {
|
||||||
this.saveCurrentBoxDraft()
|
this.saveCurrentBoxDraft()
|
||||||
savePickingSession({ boxes: this.boxes, currentBoxIndex: this.currentBoxIndex, itemStates: this.itemStates, partialQuantities: this.partialQuantities, boxDrafts: this.boxDrafts, completedBoxes: this.completedBoxes, scanValue: this.scanValue, scanHistory: this.scanHistory }, this.draftIdentity())
|
savePickingSession({ boxes: this.boxes, currentBoxIndex: this.currentBoxIndex, itemStates: this.itemStates, partialQuantities: this.partialQuantities, scannedBoxCodes: this.scannedBoxCodes, boxDrafts: this.boxDrafts, completedBoxes: this.completedBoxes, scanValue: this.scanValue, scanHistory: this.scanHistory }, this.draftIdentity())
|
||||||
},
|
},
|
||||||
leavePicking() { this.persistSession(); this.activeOverlay = ''; navigateBackOr('/pages/home/home') },
|
leavePicking() { this.persistSession(); this.activeOverlay = ''; navigateBackOr('/pages/home/home') },
|
||||||
clearTimers() { clearTimeout(this._toastTimer) },
|
clearTimers() { clearTimeout(this._toastTimer) },
|
||||||
@@ -543,6 +544,7 @@
|
|||||||
[this.currentBoxIndex]: {
|
[this.currentBoxIndex]: {
|
||||||
itemStates: { ...this.itemStates },
|
itemStates: { ...this.itemStates },
|
||||||
partialQuantities: { ...this.partialQuantities },
|
partialQuantities: { ...this.partialQuantities },
|
||||||
|
scannedBoxCodes: { ...this.scannedBoxCodes },
|
||||||
scanHistory: this.scanHistory.map((entry) => ({ ...entry }))
|
scanHistory: this.scanHistory.map((entry) => ({ ...entry }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -552,10 +554,12 @@
|
|||||||
if (draft) {
|
if (draft) {
|
||||||
this.itemStates = { ...draft.itemStates }
|
this.itemStates = { ...draft.itemStates }
|
||||||
this.partialQuantities = { ...draft.partialQuantities }
|
this.partialQuantities = { ...draft.partialQuantities }
|
||||||
|
this.scannedBoxCodes = { ...(draft.scannedBoxCodes || {}) }
|
||||||
this.scanHistory = draft.scanHistory.map((entry) => ({ ...entry }))
|
this.scanHistory = draft.scanHistory.map((entry) => ({ ...entry }))
|
||||||
} else {
|
} else {
|
||||||
this.itemStates = createItemStates(this.currentBox.items)
|
this.itemStates = createItemStates(this.currentBox.items)
|
||||||
this.partialQuantities = {}
|
this.partialQuantities = {}
|
||||||
|
this.scannedBoxCodes = {}
|
||||||
this.scanHistory = []
|
this.scanHistory = []
|
||||||
}
|
}
|
||||||
this.scanValue = ''
|
this.scanValue = ''
|
||||||
@@ -613,11 +617,11 @@
|
|||||||
if (!value) {
|
if (!value) {
|
||||||
const manual = source === 'manual'
|
const manual = source === 'manual'
|
||||||
playErrorSound()
|
playErrorSound()
|
||||||
this.recordScanResult('error', manual ? '未输入商品编码' : '未读取到条码', manual ? '输入内容为空' : '广播值为空', manual ? '请输入 SKU 后按“完成”' : '请重新扫描并检查设备广播配置')
|
this.recordScanResult('error', manual ? '未输入箱码' : '未读取到条码', manual ? '输入内容为空' : '广播值为空', manual ? '请输入箱码后按“完成”' : '请重新扫描并检查设备广播配置')
|
||||||
this.showToast(manual ? '请输入商品编码' : '广播扫码结果为空', 'warning')
|
this.showToast(manual ? '请输入箱码' : '广播扫码结果为空', 'warning')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const result = classifyScan(this.currentBox.items, this.itemStates, value)
|
const result = classifyScan(this.currentBox.items, this.itemStates, value, this.scannedBoxCodes)
|
||||||
if (result.type === 'processed') {
|
if (result.type === 'processed') {
|
||||||
playErrorSound()
|
playErrorSound()
|
||||||
const labels = {
|
const labels = {
|
||||||
@@ -625,18 +629,18 @@
|
|||||||
[ITEM_STATUS.PARTIAL]: '已部分拣货',
|
[ITEM_STATUS.PARTIAL]: '已部分拣货',
|
||||||
[ITEM_STATUS.SKIPPED]: '已跳拣'
|
[ITEM_STATUS.SKIPPED]: '已跳拣'
|
||||||
}
|
}
|
||||||
this.recordScanResult('warning', '重复扫描', value, `该 SKU ${labels[result.status]},无需重复处理`)
|
this.recordScanResult('warning', '重复扫描', value, `该箱码已${labels[result.status]},无需重复处理`)
|
||||||
this.showToast(`该SKU${labels[result.status]}:${value}`, 'warning')
|
this.showToast(`该箱码已处理:${value}`, 'warning')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (result.type === 'unknown') {
|
if (result.type === 'unknown') {
|
||||||
playErrorSound()
|
playErrorSound()
|
||||||
this.recordScanResult('error', '当前箱无此商品', value, '请核对箱号、SKU 或转入异常处理')
|
this.recordScanResult('error', '当前任务无此箱码', value, '请核对箱唛标签或转入异常处理')
|
||||||
this.showToast(`未找到匹配的待拣SKU:${value}`, 'error')
|
this.showToast(`未找到匹配的待拣箱码:${value}`, 'error')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
playSuccessSound()
|
playSuccessSound()
|
||||||
this.pickItem(result.item.id, 'scan')
|
this.pickScannedBox(result.item, value)
|
||||||
},
|
},
|
||||||
evidenceEntries(type) { return Array.isArray(this.evidenceFiles[type]) ? this.evidenceFiles[type].filter((item) => item.status !== 'VOID' && item.status !== 'VOIDED') : (this.evidenceFiles[type] ? [this.evidenceFiles[type]] : []) },
|
evidenceEntries(type) { return Array.isArray(this.evidenceFiles[type]) ? this.evidenceFiles[type].filter((item) => item.status !== 'VOID' && item.status !== 'VOIDED') : (this.evidenceFiles[type] ? [this.evidenceFiles[type]] : []) },
|
||||||
validEvidenceEntries(type) { return this.evidenceEntries(type).filter((item) => item.id && item.fileId && item.uploadStatus !== 'PENDING') },
|
validEvidenceEntries(type) { return this.evidenceEntries(type).filter((item) => item.id && item.fileId && item.uploadStatus !== 'PENDING') },
|
||||||
@@ -668,10 +672,11 @@
|
|||||||
...item,
|
...item,
|
||||||
id: item.id ?? item.itemId,
|
id: item.id ?? item.itemId,
|
||||||
taskItemId: item.itemId ?? item.id,
|
taskItemId: item.itemId ?? item.id,
|
||||||
sku: item.sku ?? item.productSku ?? item.code ?? '',
|
boxCodes: Array.isArray(item.boxCodes) ? item.boxCodes.map(String) : [],
|
||||||
desc: item.desc ?? item.description ?? item.sku ?? '',
|
orderNo: item.orderNo ?? '', inboundCode: item.inboundCode ?? '',
|
||||||
qty: Number(item.qty ?? item.packageCount ?? item.requiredPackageCount ?? 0),
|
desc: item.cargoName ?? item.desc ?? item.description ?? '箱级拣货明细',
|
||||||
unit: item.unit ?? '件',
|
qty: Number(item.qty ?? item.plannedPackageCount ?? item.packageCount ?? 0),
|
||||||
|
unit: '箱',
|
||||||
location: item.location ?? item.locationCode ?? '',
|
location: item.location ?? item.locationCode ?? '',
|
||||||
availablePackageCount: item.availablePackageCount ?? item.locationAvailablePackageCount ?? item.availableQty ?? item.stockAvailableQty ?? null,
|
availablePackageCount: item.availablePackageCount ?? item.locationAvailablePackageCount ?? item.availableQty ?? item.stockAvailableQty ?? null,
|
||||||
locationIds: item.locationIds ?? item.reservationIds ?? [],
|
locationIds: item.locationIds ?? item.reservationIds ?? [],
|
||||||
@@ -679,8 +684,12 @@
|
|||||||
locationStatus: item.locationStatus ?? null,
|
locationStatus: item.locationStatus ?? null,
|
||||||
skipReason: item.skipReason || '', finalState: item.finalState || '', finalPartialQuantity: item.finalPartialQuantity || 0
|
skipReason: item.skipReason || '', finalState: item.finalState || '', finalPartialQuantity: item.finalPartialQuantity || 0
|
||||||
}))
|
}))
|
||||||
|
const firstItem = this.currentBox.items[0]
|
||||||
|
this.currentBox.orderNo = firstItem.orderNo || firstItem.inboundCode || this.currentBox.orderNo
|
||||||
|
this.currentBox.inboundId = firstItem.inboundId || this.currentBox.inboundId
|
||||||
this.itemStates = createItemStates(this.currentBox.items)
|
this.itemStates = createItemStates(this.currentBox.items)
|
||||||
this.partialQuantities = {}
|
this.partialQuantities = {}
|
||||||
|
this.scannedBoxCodes = {}
|
||||||
}
|
}
|
||||||
this.remoteTaskId = task.taskId ?? task.id
|
this.remoteTaskId = task.taskId ?? task.id
|
||||||
if (!this.remoteTaskId) throw new Error('服务端任务缺少 taskId,无法开始拣货')
|
if (!this.remoteTaskId) throw new Error('服务端任务缺少 taskId,无法开始拣货')
|
||||||
@@ -782,15 +791,25 @@
|
|||||||
catch (error) { this.remoteError = error.network ? '网络失败,装柜完成未提交' : (error.message || '装柜完成失败'); this.showToast('装柜完成失败', 'error') }
|
catch (error) { this.remoteError = error.network ? '网络失败,装柜完成未提交' : (error.message || '装柜完成失败'); this.showToast('装柜完成失败', 'error') }
|
||||||
finally { this.submitting = false }
|
finally { this.submitting = false }
|
||||||
},
|
},
|
||||||
|
pickScannedBox(item, boxCode) {
|
||||||
|
const pickedCount = Number(this.partialQuantities[item.id] || 0) + 1
|
||||||
|
const plannedCount = Number(item.qty || 0)
|
||||||
|
this.scannedBoxCodes = { ...this.scannedBoxCodes, [boxCode]: item.id }
|
||||||
|
this.partialQuantities = { ...this.partialQuantities, [item.id]: pickedCount }
|
||||||
|
this.itemStates = { ...this.itemStates,
|
||||||
|
[item.id]: pickedCount >= plannedCount ? ITEM_STATUS.PICKED : ITEM_STATUS.PARTIAL }
|
||||||
|
this.recordScanResult('success', '箱码扫描成功', boxCode,
|
||||||
|
`${item.inboundCode || item.orderNo || '当前明细'} · 已拣 ${pickedCount}/${plannedCount} 箱`)
|
||||||
|
this.showToast(`已拣箱 ${pickedCount}/${plannedCount}`, 'success')
|
||||||
|
this.persistSession()
|
||||||
|
},
|
||||||
pickItem(id, source = 'manual') {
|
pickItem(id, source = 'manual') {
|
||||||
const item = this.currentBox.items.find((entry) => entry.id === id)
|
const item = this.currentBox.items.find((entry) => entry.id === id)
|
||||||
const available = this.availablePickQuantity(item)
|
const available = this.availablePickQuantity(item)
|
||||||
if (available !== null && available < Number(item.qty || 0)) { this.showToast(`库存/库位可用量不足:可拣 ${available}`, 'error'); return }
|
if (available !== null && available < Number(item.qty || 0)) { this.showToast(`库存/库位可用量不足:可拣 ${available}`, 'error'); return }
|
||||||
this.itemStates = { ...this.itemStates, [id]: ITEM_STATUS.PICKED }
|
this.itemStates = { ...this.itemStates, [id]: ITEM_STATUS.PICKED }
|
||||||
if (source === 'scan' && item) {
|
this.partialQuantities = { ...this.partialQuantities, [id]: Number(item?.qty || 0) }
|
||||||
this.recordScanResult('success', '扫描成功', item.sku, `${item.location} · 已拣 ${item.qty} ${item.unit}`)
|
this.showToast(item ? `已确认拣出 ${item.qty} 箱` : '已确认拣出', 'success')
|
||||||
}
|
|
||||||
this.showToast(item ? `已拣出 ${item.sku}` : '已确认拣出', 'success')
|
|
||||||
this.persistSession()
|
this.persistSession()
|
||||||
},
|
},
|
||||||
recordScanResult(type, title, sku, detail) {
|
recordScanResult(type, title, sku, detail) {
|
||||||
@@ -804,6 +823,8 @@
|
|||||||
const nextPartialQuantities = { ...this.partialQuantities }
|
const nextPartialQuantities = { ...this.partialQuantities }
|
||||||
delete nextPartialQuantities[id]
|
delete nextPartialQuantities[id]
|
||||||
this.partialQuantities = nextPartialQuantities
|
this.partialQuantities = nextPartialQuantities
|
||||||
|
this.scannedBoxCodes = Object.fromEntries(Object.entries(this.scannedBoxCodes)
|
||||||
|
.filter(([, itemId]) => String(itemId) !== String(id)))
|
||||||
this.showToast('已撤销操作', 'success')
|
this.showToast('已撤销操作', 'success')
|
||||||
this.persistSession()
|
this.persistSession()
|
||||||
},
|
},
|
||||||
@@ -944,7 +965,7 @@
|
|||||||
this.saveCurrentBoxDraft()
|
this.saveCurrentBoxDraft()
|
||||||
if (remoteRequired) clearPickingDraft(this.draftIdentity())
|
if (remoteRequired) clearPickingDraft(this.draftIdentity())
|
||||||
this.submitId = ''
|
this.submitId = ''
|
||||||
const afterSubmit = getPickSummary(this.currentBox.items, this.itemStates)
|
const afterSubmit = getPickSummary(this.currentBox.items, this.itemStates, this.partialQuantities)
|
||||||
this.completionSummary = {
|
this.completionSummary = {
|
||||||
picked: afterSubmit.picked,
|
picked: afterSubmit.picked,
|
||||||
partial: afterSubmit.partial,
|
partial: afterSubmit.partial,
|
||||||
|
|||||||
@@ -10,9 +10,9 @@
|
|||||||
<view v-if="loading" class="task-state">正在加载真实任务...</view>
|
<view v-if="loading" class="task-state">正在加载真实任务...</view>
|
||||||
<view v-else-if="!tasks.length && !error" class="task-state">暂无待处理拣货任务</view>
|
<view v-else-if="!tasks.length && !error" class="task-state">暂无待处理拣货任务</view>
|
||||||
<view v-for="task in tasks" :key="task.id || task.inboundId || task.displayNo" class="task-card">
|
<view v-for="task in tasks" :key="task.id || task.inboundId || task.displayNo" class="task-card">
|
||||||
<view class="task-card-head"><text class="task-code">{{ task.containerNo || '待分配柜次' }}</text><text class="task-status">{{ task.mainStatus || '待拣货' }}</text></view>
|
<view class="task-card-head"><text class="task-code">{{ task.containerNo || '待分配柜次' }}</text><text class="task-status">{{ statusLabel(task.workflowStatus) }}</text></view>
|
||||||
<text class="task-order">{{ task.displayNo || '未命名订单' }}</text>
|
<text class="task-order">{{ task.displayNo || '未命名订单' }}</text>
|
||||||
<text class="task-meta">{{ task.warehouseName || (task.warehouseId ? `仓库 ${task.warehouseId}` : '缺少仓库信息') }}</text>
|
<text class="task-meta">{{ task.warehouseName || (task.warehouseId ? `仓库 ${task.warehouseId}` : '缺少仓库信息') }} · 待处理 {{ task.pendingItemCount ?? task.itemCount ?? 0 }} 项</text>
|
||||||
<button class="task-enter" :disabled="!task.containerNo || !task.warehouseId" @tap="enterTask(task)">{{ task.containerNo && task.warehouseId ? '进入并开始拣货' : '缺少任务信息' }}</button>
|
<button class="task-enter" :disabled="!task.containerNo || !task.warehouseId" @tap="enterTask(task)">{{ task.containerNo && task.warehouseId ? '进入并开始拣货' : '缺少任务信息' }}</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -33,6 +33,7 @@
|
|||||||
onShow() { if (!requireSession()) return; this.user = session.currentUser; this.loadTasks() },
|
onShow() { if (!requireSession()) return; this.user = session.currentUser; this.loadTasks() },
|
||||||
onUnload() { if (this._stopNetwork) this._stopNetwork(); clearTimeout(this._toastTimer) },
|
onUnload() { if (this._stopNetwork) this._stopNetwork(); clearTimeout(this._toastTimer) },
|
||||||
methods: {
|
methods: {
|
||||||
|
statusLabel(status) { return status === 'IN_PROGRESS' ? '拣货中' : '待领取' },
|
||||||
async loadTasks() {
|
async loadTasks() {
|
||||||
if (!isOnline()) { this.error = '当前网络不可用,请联网后刷新'; return }
|
if (!isOnline()) { this.error = '当前网络不可用,请联网后刷新'; return }
|
||||||
this.loading = true; this.error = ''
|
this.loading = true; this.error = ''
|
||||||
|
|||||||
+2
-1
@@ -11,7 +11,7 @@ function createInitialState() {
|
|||||||
boxes,
|
boxes,
|
||||||
currentBoxIndex: 0,
|
currentBoxIndex: 0,
|
||||||
itemStates: createItemStates(boxes[0].items),
|
itemStates: createItemStates(boxes[0].items),
|
||||||
partialQuantities: {}, boxDrafts: {}, completedBoxes: {}, scanValue: '', scanHistory: []
|
partialQuantities: {}, scannedBoxCodes: {}, boxDrafts: {}, completedBoxes: {}, scanValue: '', scanHistory: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +46,7 @@ function draftPayload(nextState, identity) {
|
|||||||
currentBoxIndex: nextState.currentBoxIndex,
|
currentBoxIndex: nextState.currentBoxIndex,
|
||||||
itemStates: nextState.itemStates,
|
itemStates: nextState.itemStates,
|
||||||
partialQuantities: nextState.partialQuantities,
|
partialQuantities: nextState.partialQuantities,
|
||||||
|
scannedBoxCodes: nextState.scannedBoxCodes,
|
||||||
boxDrafts: nextState.boxDrafts,
|
boxDrafts: nextState.boxDrafts,
|
||||||
completedBoxes: nextState.completedBoxes,
|
completedBoxes: nextState.completedBoxes,
|
||||||
scanHistory: nextState.scanHistory,
|
scanHistory: nextState.scanHistory,
|
||||||
|
|||||||
+10
-5
@@ -12,7 +12,7 @@ export function createItemStates(items) {
|
|||||||
}, {})
|
}, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPickSummary(items, states) {
|
export function getPickSummary(items, states, partialQuantities = {}) {
|
||||||
const summary = {
|
const summary = {
|
||||||
total: items.length,
|
total: items.length,
|
||||||
picked: 0,
|
picked: 0,
|
||||||
@@ -23,7 +23,11 @@ export function getPickSummary(items, states) {
|
|||||||
|
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
const status = states[item.id] || ITEM_STATUS.PENDING
|
const status = states[item.id] || ITEM_STATUS.PENDING
|
||||||
summary[status] += 1
|
const quantity = Math.max(1, Number(item.qty || 1))
|
||||||
|
summary.total += quantity - 1
|
||||||
|
summary[status] += status === ITEM_STATUS.PARTIAL
|
||||||
|
? Math.max(0, Number(partialQuantities[item.id] || 0))
|
||||||
|
: quantity
|
||||||
})
|
})
|
||||||
|
|
||||||
summary.processed = summary.picked + summary.partial + summary.skipped
|
summary.processed = summary.picked + summary.partial + summary.skipped
|
||||||
@@ -56,12 +60,13 @@ export function submitStates(items, states) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function classifyScan(items, states, scanCode) {
|
export function classifyScan(items, states, scanCode, scannedBoxCodes = {}) {
|
||||||
const item = items.find((entry) => entry.sku === scanCode) || null
|
const item = items.find((entry) => (entry.boxCodes || []).includes(scanCode)) || null
|
||||||
if (!item) return { type: 'unknown', item: null }
|
if (!item) return { type: 'unknown', item: null }
|
||||||
|
if (scannedBoxCodes[scanCode]) return { type: 'processed', item, status: states[item.id] || ITEM_STATUS.PARTIAL }
|
||||||
|
|
||||||
const status = states[item.id] || ITEM_STATUS.PENDING
|
const status = states[item.id] || ITEM_STATUS.PENDING
|
||||||
if (status === ITEM_STATUS.PENDING) return { type: 'pending', item }
|
if (status !== ITEM_STATUS.PICKED && status !== ITEM_STATUS.SKIPPED) return { type: 'pending', item }
|
||||||
|
|
||||||
return { type: 'processed', item, status }
|
return { type: 'processed', item, status }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user