This commit is contained in:
2026-09-01 11:38:45 +08:00
parent 42262e5705
commit 619edfb575
3 changed files with 11 additions and 7 deletions
+7 -5
View File
@@ -139,13 +139,15 @@
<text v-if="evidenceQueueEntry(slot.type)" class="evidence-upload-state" :class="evidenceQueueEntry(slot.type).status.toLowerCase()">
{{ evidenceUploadState(slot.type) }}
</text>
<text v-else-if="hasPendingLocalEvidence(slot.type)" class="evidence-upload-state pending">本地暂存,待自动上传</text>
<text v-else-if="hasPendingLocalEvidence(slot.type)" class="evidence-upload-state pending">
{{ slot.type === 'SEAL' ? '请填写封签号,保存后上传' : '照片待自动上传' }}
</text>
<button v-if="evidenceQueueEntry(slot.type)?.status === 'FAILED'" class="evidence-retry-btn" @tap="retryEvidence(slot.type)">重试上传</button>
</view>
</view>
<view class="seal-row">
<input v-model="sealNo" class="seal-input" placeholder="输入封签号(必填)" maxlength="64" />
<button class="evidence-btn seal-btn" :disabled="sealSaving || !sealReadyToSave" @tap="saveSealNo">{{ sealSaving ? '保存中…' : '保存封签' }}</button>
<button class="evidence-btn seal-btn" :disabled="sealSaving || !sealReadyToSave" @tap="saveSealNo">{{ sealSaving ? '上传中…' : '保存并上传' }}</button>
</view>
<view v-if="remoteError" class="remote-error-row"><text class="remote-error" role="alert">{{ remoteError }}</text><button class="evidence-retry-btn" :disabled="remoteLoading" @tap="refreshTask">刷新任务</button></view>
<button class="loading-complete-btn" :disabled="submitting || !loadingStageReady || pickingVersion === null || !evidenceComplete" @tap="completeLoadingTask">确认装柜完成</button>
@@ -1243,7 +1245,7 @@
this.evidenceFiles = { ...this.evidenceFiles, [slot.type]: [...(this.evidenceFiles[slot.type] || []), next] }
this.rememberEvidenceDraft(identity)
if (slot.type === 'SEAL') {
this.showToast('带锁码照已拍摄,请输入封签号并保存', 'success')
this.showToast('照片已拍摄,请输入封签号并保存上传', 'success')
return
}
this.queueEvidenceUpload(slot.type, path)
@@ -1280,7 +1282,7 @@
this.debugSubmit('evidence.queue.result', { evidenceType: type, queueId: entry.id, status: latest?.status || 'missing', fileId: latest?.result?.fileId || null, error: latest?.error || '' })
if (latest?.status !== 'SUCCEEDED') { this.remoteError = latest?.error || '文件上传失败'; return }
this.applyEvidenceResponse(latest.result, type, path, identity)
this.showToast(`${type === 'SEAL' ? '封签' : '照片'}已上传并保存`, 'success')
this.showToast(type === 'SEAL' ? '带锁码照上传成功,封签号已保存' : '照片已上传并保存', 'success')
},
async flushPendingEvidence() {
if (!this.evidenceIdentity?.taskId || !this.evidenceIdentity?.warehouseId) return
@@ -1304,7 +1306,7 @@
const latest = queue.latest(type)
if (latest?.status !== 'SUCCEEDED') { this.remoteError = latest?.error || '文件上传失败'; return }
this.applyEvidenceResponse(latest.result, type, item.localPath, identity)
this.showToast(`${type === 'SEAL' ? '封签' : '照片'}重试成功`, 'success')
this.showToast(type === 'SEAL' ? '带锁码照上传成功,封签号已保存' : '照片重试上传成功', 'success')
},
async saveSealNo() {
if (this.sealSaving) return
+3 -1
View File
@@ -123,11 +123,13 @@ test('final completion preserves backend gate errors and refreshes after an atom
test('seal photo upload waits for an explicit seal-number save without waiting for picking completion', () => {
const chooser = page.match(/chooseEvidence\(slot\) \{[\s\S]*?\n\t\t\t\},/)?.[0] || ''
assert.match(chooser, /slot\.type === 'SEAL'[\s\S]*?带锁码照已拍摄,请输入封签号并保存[\s\S]*?return/)
assert.match(chooser, /slot\.type === 'SEAL'[\s\S]*?照片已拍摄,请输入封签号并保存上传[\s\S]*?return/)
assert.match(page, /async saveSealNo\(\)[\s\S]*?await this\.queueEvidenceUpload\('SEAL', current\.localPath, String\(this\.sealNo\)\.trim\(\)\)/)
const saver = page.match(/async saveSealNo\(\) \{[\s\S]*?\n\t\t\t\},/)?.[0] || ''
assert.doesNotMatch(saver, /if \(!this\.loadingStageReady\)/)
assert.match(page, /sealReadyToSave\(\)[\s\S]*?hasPendingLocalEvidence\('SEAL'\)/)
assert.match(page, /slot\.type === 'SEAL' \? '请填写封签号,保存后上传' : '照片待自动上传'/)
assert.match(page, /带锁码照上传成功,封签号已保存/)
})
test('cabinet evidence identity is task-scoped and never selects an inbound owner', () => {
+1 -1
View File
@@ -6,7 +6,7 @@ const page = await readFile(new URL('../pages/picking/picking.vue', import.meta.
test('saving a seal is single-flight and submits the pending photo through the atomic evidence command', () => {
assert.match(page, /:disabled="sealSaving \|\| !sealReadyToSave"/)
assert.match(page, /sealSaving \? '保存中…' : '保存封签'/)
assert.match(page, /sealSaving \? '上传中…' : '保存并上传'/)
assert.match(page, /async saveSealNo\(\) \{[\s\S]*?if \(this\.sealSaving\) return/)
assert.match(page, /find\(\(item\) => item\.localPath && !item\.id\)/)
assert.match(page, /await this\.queueEvidenceUpload\('SEAL', current\.localPath, String\(this\.sealNo\)\.trim\(\)\)/)