fix: avoid repeated PDA bootstrap validation

This commit is contained in:
2026-09-07 19:34:24 +08:00
parent 90fcff69f3
commit b00d58d34c
2 changed files with 6 additions and 4 deletions
+2 -2
View File
@@ -157,9 +157,9 @@
setScanTargetForCurrent(value) { receiving.activeScanTarget = value?.status === 'WAIT_SIGN' ? 'container' : 'carton' },
async restorePhotoPreviews() { await Promise.all(receiving.photoSlots.filter((photo) => photo?.downloadPath && !photo.localPath).map(async (photo) => { photo.status = 'restoring'; try { photo.localPath = await downloadCurrentUnloadPhoto(photo.downloadPath); photo.status = 'success' } catch (error) { photo.status = 'error'; photo.errorMessage = error.message || '照片加载失败'; this.error = error } })) },
async applyCurrentReceiving(value) { this.validateReceivingWarehouse(value, '当前接柜会话'); setCurrentReceiving(value); await this.restorePhotoPreviews(); this.setScanTargetForCurrent(value) },
async bootstrap() { if (this.loading) return; this.loading = true; this.error = null; this.detailOpen = false; try { await this.refreshBoundWarehouse(); try { const current = await getCurrentReceiving(); await this.applyCurrentReceiving(current); this.candidateError = null } catch (error) { if (!isNoCurrentReceiving(error)) throw error; resetReceivingDraft(); await this.loadCandidates(false) } this.bootstrapped = true } catch (error) { this.candidateError = error } finally { this.loading = false } },
async bootstrap() { if (this.loading) return; this.loading = true; this.error = null; this.detailOpen = false; try { await this.refreshBoundWarehouse(); try { const current = await getCurrentReceiving(); await this.applyCurrentReceiving(current); this.candidateError = null } catch (error) { if (!isNoCurrentReceiving(error)) throw error; resetReceivingDraft(); await this.loadCandidates(false, false) } this.bootstrapped = true } catch (error) { this.candidateError = error } finally { this.loading = false } },
async recoverCurrentAfterConflict(error, preserveEvidence = false) { if (!isReceivingConflict(error)) return false; try { const current = await getCurrentReceiving(); await this.applyCurrentReceiving(current); this.detailOpen = false; this.error = preserveEvidence ? error : null; this.showToast(`账号已有未完成柜 ${current.containerNo},已返回当前柜`, 'warning'); return true } catch (recoveryError) { if (isNoCurrentReceiving(recoveryError)) { this.detailOpen = false; receiving.selected = null; await this.loadCandidates(false); this.candidateError = preserveEvidence ? error : null; this.error = preserveEvidence ? error : null; this.showToast('柜状态已变化,列表已刷新', 'warning'); return true } this.error = error; return false } },
async loadCandidates(feedback = true) { if (this.candidateLoading) return; this.candidateLoading = true; this.candidateError = null; this.error = null; try { await this.refreshBoundWarehouse(); const values = await listReceivingCandidates(); receiving.candidates = values.map((item) => this.validateReceivingWarehouse(item, `柜号 ${item.containerNo || '—'}`)); if (feedback) this.showToast('刷新待卸柜成功', 'success') } catch (error) { receiving.candidates = []; this.candidateError = error; if (feedback) { playErrorSound(); this.showToast(error.message || '刷新待卸柜失败', 'error') } } finally { this.candidateLoading = false } },
async loadCandidates(feedback = true, refreshWarehouse = true) { if (this.candidateLoading) return; this.candidateLoading = true; this.candidateError = null; this.error = null; try { if (refreshWarehouse) await this.refreshBoundWarehouse(); const values = await listReceivingCandidates(); receiving.candidates = values.map((item) => this.validateReceivingWarehouse(item, `柜号 ${item.containerNo || '—'}`)); if (feedback) this.showToast('刷新待卸柜成功', 'success') } catch (error) { receiving.candidates = []; this.candidateError = error; if (feedback) { playErrorSound(); this.showToast(error.message || '刷新待卸柜失败', 'error') } } finally { this.candidateLoading = false } },
async openContainer(candidate) { const containerNo = String(candidate?.containerNo || '').trim(); if (!containerNo || this.busy) return; this.error = null; try { this.validateReceivingWarehouse(candidate, `柜号 ${containerNo}`); if (!candidate.operable) { receiving.selected = candidate; this.detailOpen = true; return } const detail = await this.run('查询柜号', async () => this.validateReceivingWarehouse(await getReceivingContainer(containerNo), `柜号 ${containerNo}`)); receiving.selected = { ...candidate, ...detail, allPlansReady: candidate.allPlansReady, operable: candidate.operable, blockReason: candidate.blockReason }; this.detailOpen = true; playSuccessSound() } catch (error) { if (!await this.recoverCurrentAfterConflict(error)) { this.error = error; this.showToast(error.message || '查询柜号失败', 'error') } } },
cancelDetail() { this.detailOpen = false; receiving.selected = null },
async prepareContainer() { if (!this.selected?.containerNo || !this.selected?.allPlansReady || !this.selected?.operable || this.busy) return; const key = idempotencyKey('prepare', { containerNo: this.selected.containerNo }); try { this.validateReceivingWarehouse(this.selected, `柜号 ${this.selected.containerNo}`); const value = await this.run('选择当前柜', async () => this.validateReceivingWarehouse(await prepareReceivingContainer(this.selected.containerNo, { idempotencyKey: key.value }), '新建接柜会话')); releaseIdempotencyKey(key.signature); await this.applyCurrentReceiving(value); this.detailOpen = false } catch (error) { if (!await this.recoverCurrentAfterConflict(error, true)) this.error = error } },
+4 -2
View File
@@ -79,7 +79,7 @@ test('login has no committed demo credentials and inbound operation implements t
assert.match(operation, /@tap="loadCandidates"[^>]*>重新加载/)
assert.doesNotMatch(operation, /扫描柜号|查询柜号<\/button>|v-model="containerInput"/)
assert.match(operation, /phase === 'candidates'[^\n]*请直接点击待卸货集装箱进入详情/)
assert.match(operation, /async loadCandidates\(feedback = true\)[\s\S]*if \(this\.candidateLoading\) return[\s\S]*this\.candidateError = error/)
assert.match(operation, /async loadCandidates\(feedback = true, refreshWarehouse = true\)[\s\S]*if \(this\.candidateLoading\) return[\s\S]*this\.candidateError = error/)
assert.match(operation, /async prepareContainer\(\)[\s\S]*this\.busy\) return/)
assert.match(operation, /prepareReceivingContainer[\s\S]*releaseIdempotencyKey\(key\.signature\)/)
assert.match(overseas, /replaceTokens|refreshOverseasToken/)
@@ -147,8 +147,10 @@ test('Chinese English and Spanish locale selection is persisted from profile wit
})
test('home reuses the persisted warehouse profile instead of requesting it on every show', async () => {
const [home, inbound] = await Promise.all([read('../src/pages/home/home.vue'), read('../src/api/inbound.js')])
const [home, inbound, operation] = await Promise.all([read('../src/pages/home/home.vue'), read('../src/api/inbound.js'), read('../src/pages/operation/operation.vue')])
assert.match(home, /if \(!this\.user\?\.warehouseCode \|\| !this\.user\?\.warehouseName\) this\.loadWarehouseProfile\(\)/)
assert.match(inbound, /let warehouseProfileRequest = null/)
assert.match(inbound, /if \(!warehouseProfileRequest\)[\s\S]*\.finally\(\(\) => \{ warehouseProfileRequest = null \}\)/)
assert.match(operation, /await this\.refreshBoundWarehouse\(\)[\s\S]*await this\.loadCandidates\(false, false\)/)
assert.match(operation, /if \(refreshWarehouse\) await this\.refreshBoundWarehouse\(\)/)
})