优化
This commit is contained in:
@@ -104,6 +104,7 @@
|
||||
import { startScanBroadcast } from '@/utils/scan-broadcast.js'
|
||||
import { playErrorSound, playSuccessSound } from '@/utils/audio-feedback.js'
|
||||
import { completeCurrentReceiving, downloadCurrentUnloadPhoto, getCurrentReceiving, getReceivingContainer, listReceivingCandidates, prepareReceivingContainer, receiveCurrentBulk, receiveCurrentCarton, resolveCurrentCarton, resolveCurrentPallet, signCurrentReceiving, uploadCurrentUnloadPhoto } from '@/api/inbound.js'
|
||||
const MAX_UNLOAD_PHOTO_BYTES = 10 * 1024 * 1024
|
||||
|
||||
export default {
|
||||
components: { PortalHeader, ReceivingSessionSummary, ScanTargetInput },
|
||||
@@ -128,7 +129,7 @@
|
||||
statusLabel(status) { return ({ WAIT_SIGN: '待签收', SIGNED: '已签收', RECEIVING: '收货中', COMPLETED: '已完成' })[status] || status },
|
||||
statusTone(status) { return status === 'RECEIVING' ? 'status-current' : status === 'COMPLETED' ? 'status-good' : 'status-wait' },
|
||||
destinationLabel(item) { return item?.destinationType === 'FBA' ? (item.fbaWarehouseCode || 'FBA') : (item?.destinationWarehouseName || item?.destinationWarehouseCode || '海外仓') },
|
||||
photoStateLabel(photo) { if (photo?.status === 'uploading') return '正在上传…'; if (photo?.status === 'error') return photo.fileId ? '重拍失败,已保留原图' : '上传失败,请重试'; return photo?.fileId ? '点击左侧照片可查看大图' : '请拍摄现场照片' },
|
||||
photoStateLabel(photo) { if (photo?.status === 'uploading') return '正在上传…'; if (photo?.status === 'error') return photo.errorMessage || (photo.fileId ? '重拍失败,已保留原图' : '上传失败,请重试'); return photo?.fileId ? '点击左侧照片可查看大图' : '请拍摄现场照片' },
|
||||
startScanner() { this.stopScanner(); this._stopScanner = startScanBroadcast({ onScan: (value) => this.handleHardwareScan(value), onError: (error) => { this.error = error; this.showToast('硬件扫码接收器异常,请检查扫描服务', 'warning') } }) }, stopScanner() { if (typeof this._stopScanner === 'function') this._stopScanner(); this._stopScanner = null },
|
||||
handleHardwareScan(value) { console.info('[Overseas PDA scan] dispatch', { value, phase: this.phase, target: this.activeTarget, matchedInbound: this.activeInbound?.inboundCode }); if (!this.inboundMode) return this.showToast('该功能缺少海外 PDA 专用接口,扫码未提交', 'warning'); if (this.phase === 'candidates') return this.showToast('请直接点击待卸货集装箱进入详情', 'warning'); if (this.phase !== 'receive') return; if (this.activeTarget === 'pallet' && this.activeInbound) { this.palletInput = value; return this.resolvePallet(value) } this.cartonInput = value; return this.resolveCarton(value) },
|
||||
async run(action, work) { this.busy = true; this.error = null; try { const result = await work(); this.showToast(`${action}成功`, 'success'); return result } catch (error) { this.error = error; playErrorSound(); this.showToast(error.message || `${action}失败`, 'error'); throw error } finally { this.busy = false } },
|
||||
@@ -141,8 +142,8 @@
|
||||
async openContainer(raw) { const containerNo = String(raw?.detail?.value ?? raw ?? '').trim(); if (!containerNo || this.busy) return; try { receiving.selected = await this.run('查询柜号', () => getReceivingContainer(containerNo)); this.detailOpen = true; playSuccessSound() } catch (error) { await this.recoverCurrentAfterConflict(error) } },
|
||||
cancelDetail() { this.detailOpen = false; receiving.selected = null },
|
||||
async prepareContainer() { if (!this.selected?.containerNo || this.busy) return; const key = idempotencyKey('prepare', { containerNo: this.selected.containerNo }); try { const value = await this.run('选择当前柜', () => prepareReceivingContainer(this.selected.containerNo, { idempotencyKey: key.value })); releaseIdempotencyKey(key.signature); setCurrentReceiving(value); this.setScanTargetForCurrent(value); this.detailOpen = false } catch (error) { await this.recoverCurrentAfterConflict(error) } },
|
||||
choosePhoto(index) { if (this.busy) return; uni.chooseImage({ count: 1, sourceType: ['camera'], success: ({ tempFilePaths }) => { const path = tempFilePaths?.[0]; if (path) this.replacePhoto(index, path) } }) },
|
||||
async replacePhoto(index, path) { const previous = receiving.photoSlots[index]; receiving.photoSlots.splice(index, 1, { ...(previous || {}), role: this.photoSteps[index].role, status: 'uploading', errorMessage: '' }); this.busy = true; this.error = null; try { const file = await uploadCurrentUnloadPhoto(path); receiving.photoSlots.splice(index, 1, { ...file, fileId: String(file.fileId), role: this.photoSteps[index].role, localPath: path, uploaded: true, status: 'success' }); this.showToast(`${this.photoSteps[index].label}上传成功`, 'success') } catch (error) { receiving.photoSlots.splice(index, 1, previous?.fileId ? { ...previous, status: 'error', errorMessage: error.message || '重拍失败,已保留原图' } : { role: this.photoSteps[index].role, localPath: path, uploaded: false, status: 'error', errorMessage: error.message || '上传失败,请重试' }); this.error = error; playErrorSound() } finally { this.busy = false } },
|
||||
choosePhoto(index) { if (this.busy) return; uni.chooseImage({ count: 1, sourceType: ['camera'], success: ({ tempFilePaths, tempFiles }) => { const path = tempFilePaths?.[0]; const size = Number(tempFiles?.[0]?.size || 0); if (size > MAX_UNLOAD_PHOTO_BYTES) { const message = '照片不能超过10MB'; this.error = Object.assign(new Error(message), { code: 'A0400' }); this.showToast(message, 'error'); playErrorSound(); return } if (path) this.replacePhoto(index, path) } }) },
|
||||
async replacePhoto(index, path) { const previous = receiving.photoSlots[index]; receiving.photoSlots.splice(index, 1, { ...(previous || {}), role: this.photoSteps[index].role, status: 'uploading', errorMessage: '' }); this.busy = true; this.error = null; try { const file = await uploadCurrentUnloadPhoto(path); receiving.photoSlots.splice(index, 1, { ...file, fileId: String(file.fileId), role: this.photoSteps[index].role, localPath: path, uploaded: true, status: 'success' }); this.showToast(`${this.photoSteps[index].label}上传成功`, 'success') } catch (error) { receiving.photoSlots.splice(index, 1, previous?.fileId ? { ...previous, status: 'error', errorMessage: error.message || '重拍失败,已保留原图' } : { role: this.photoSteps[index].role, localPath: path, uploaded: false, status: 'error', errorMessage: error.message || '上传失败,请重试' }); this.error = error; this.showToast(error.message || '照片上传失败', 'error'); playErrorSound() } finally { this.busy = false } },
|
||||
previewPhoto(index) { const path = this.photoSlots[index]?.localPath; if (!path) return; uni.previewImage({ urls: [path], current: path }) },
|
||||
async signContainer() { if (!this.photosReady) return; const photos = this.photoSlots.map((item, index) => ({ role: this.photoSteps[index].role, fileId: String(item.fileId) })); const key = idempotencyKey('sign', { containerNo: this.current.containerNo, photos }); try { const value = await this.run('签收当前柜', () => signCurrentReceiving({ photos, idempotencyKey: key.value })); releaseIdempotencyKey(key.signature); setCurrentReceiving(value); this.clearReceiptContext() } catch (_) {} },
|
||||
clearReceiptContext() { receiving.resolvedCarton = null; receiving.resolvedPallet = null; this.cartonInput = ''; this.palletInput = ''; this.packageCount = 1; receiving.activeScanTarget = 'carton' },
|
||||
|
||||
@@ -47,13 +47,16 @@ test('three fixed receiving photo roles and string file IDs are frozen', () => {
|
||||
|
||||
test('camera replaces only one fixed slot and failed retake preserves its previous success', () => {
|
||||
assert.match(page, /choosePhoto\(index\)[\s\S]*uni\.chooseImage\(\{ count: 1, sourceType: \['camera'\]/)
|
||||
assert.match(page, /MAX_UNLOAD_PHOTO_BYTES = 10 \* 1024 \* 1024/)
|
||||
assert.match(page, /tempFiles\?\.\[0\]\?\.size[\s\S]*照片不能超过10MB/)
|
||||
assert.doesNotMatch(page, /sourceType:\s*\['album'\]|从相册选择|换照片/)
|
||||
assert.match(page, /async replacePhoto\(index, path\)[\s\S]*const previous = receiving\.photoSlots\[index\]/)
|
||||
assert.match(page, /await uploadCurrentUnloadPhoto\(path\)[\s\S]*fileId: String\(file\.fileId\)[\s\S]*localPath: path/)
|
||||
assert.match(page, /previous\?\.fileId \? \{ \.\.\.previous, status: 'error'/)
|
||||
assert.match(page, /status: 'uploading'/)
|
||||
assert.match(page, /status: 'error'/)
|
||||
assert.match(page, /photoStateLabel\(photo\)[\s\S]*已保留原图[\s\S]*上传失败/)
|
||||
assert.match(page, /photoStateLabel\(photo\)[\s\S]*photo\.errorMessage/)
|
||||
assert.match(page, /this\.showToast\(error\.message \|\| '照片上传失败', 'error'\)/)
|
||||
assert.match(page, /photoSlots\[index\]\?\.fileId \? '重拍' : '拍照'/)
|
||||
assert.match(page, /previewPhoto\(index\)/)
|
||||
assert.match(page, /uploadedPhotoCount\(\) \{ return this\.photoSlots\.filter\(\(item\) => item\?\.fileId\)\.length \}/)
|
||||
|
||||
Reference in New Issue
Block a user