This commit is contained in:
2026-08-14 14:50:44 +08:00
parent a81af4a664
commit 1c1c48f8d3
11 changed files with 116 additions and 23 deletions
@@ -219,6 +219,27 @@ describe('WeightOcrPanel confirmation behavior', () => {
expect(correctWeightResult).not.toHaveBeenCalled()
})
it('emits the latest server total after confirming a weight', async () => {
vi.mocked(getWeightWorkspace)
.mockResolvedValueOnce(workspace)
.mockResolvedValueOnce({
...workspace,
actualGrossWeightKg: 285.5,
weightResults: [{ ...pendingWeight, recognitionStatus: 'CONFIRMED', finalWeightKg: 285.5, version: 1 }],
})
const wrapper = mountPanel()
await flushPromises()
await openWeightDialog(wrapper)
await dialogConfirmButton().trigger('click')
await flushPromises()
expect(wrapper.emitted('summaryChanged')).toEqual([[285.5]])
expect(wrapper.emitted('changed')).toHaveLength(1)
expect(wrapper.text()).toContain('已确认总毛重')
expect(wrapper.text()).toContain('285.500 KG')
})
it('requires explicit image review before confirming a low-confidence OCR value', async () => {
vi.mocked(getWeightWorkspace).mockResolvedValue({
...workspace,
@@ -8,7 +8,7 @@ describe('WeightOcrPanel contract', () => {
it('uses customer language at the page boundary while retaining a compatibility input', () => {
expect(source).toContain("defineProps<{ customerId?: string; tenantId?: string; inboundId: string }>()")
expect(source).toContain("const customerId = computed(() => props.customerId || props.tenantId || '')")
expect(source).toContain("defineEmits<{ changed: [] }>()")
expect(source).toContain('summaryChanged: [actualGrossWeightKg: number | null]')
expect(source).toContain("defineExpose({ reload: loadWorkspace })")
const template = source.match(/<template>([\s\S]*?)<\/template>/)?.[1] || ''
expect(template).not.toContain('租户')
@@ -26,6 +26,8 @@ describe('WeightOcrPanel contract', () => {
expect(source).toContain('识别重量')
expect(source).toContain('payload.adjustedWeightValue = Number(draft.value)')
expect(source).toContain('payload.adjustmentReason = buildAdjustmentReason(row)')
expect(source).toContain('已确认总毛重')
expect(source).toContain("emit('summaryChanged', workspace.value?.actualGrossWeightKg ?? null)")
})
it('matches backend image limits and keeps technical OCR data out of the list', () => {
@@ -25,7 +25,10 @@ import WeightImageThumbnail from './WeightImageThumbnail.vue'
import { inboundProcessStatusLabels, inboundStatusLabel, weightRecognitionStatusLabels } from '../../status'
const props = defineProps<{ customerId?: string; tenantId?: string; inboundId: string }>()
const emit = defineEmits<{ changed: [] }>()
const emit = defineEmits<{
changed: []
summaryChanged: [actualGrossWeightKg: number | null]
}>()
const access = useAccessPreviewStore()
const fileInput = ref<HTMLInputElement>()
@@ -145,6 +148,11 @@ function buildAdjustmentReason(row: WeightResultResource) {
return `OCR 原值 ${ocrValue},人工确认 ${formatWeight(Number(draft.value), draft.unit)}`
}
function emitWorkspaceChanged() {
emit('summaryChanged', workspace.value?.actualGrossWeightKg ?? null)
emit('changed')
}
async function loadWorkspace() {
if (!customerId.value || !props.inboundId || !canView.value) {
workspace.value = null
@@ -208,7 +216,7 @@ async function handleFileChange(event: Event) {
await openPreview(created, URL.createObjectURL(file))
await loadWorkspace()
ensureWeightDraft(created)
emit('changed')
emitWorkspaceChanged()
} catch (error) {
actionError.value = getApiErrorDetails(error, '称重图片上传失败,请检查后重试')
} finally {
@@ -244,7 +252,7 @@ async function confirmResult(row: WeightResultResource, closeAfterSuccess = fals
ElMessage.success('重量已确认')
await loadWorkspace()
if (closeAfterSuccess) previewOpen.value = false
emit('changed')
emitWorkspaceChanged()
return true
} catch (error) {
actionError.value = getApiErrorDetails(error, '重量确认失败,请刷新后重试')
@@ -303,7 +311,7 @@ async function removeResult(row: WeightResultResource) {
if (previewResultId.value === row.id) previewOpen.value = false
ElMessage.success('称重图片已删除')
await loadWorkspace()
emit('changed')
emitWorkspaceChanged()
} catch (error) {
actionError.value = getApiErrorDetails(error, '称重图片删除失败,请刷新后重试')
} finally {
@@ -345,6 +353,10 @@ defineExpose({ reload: loadWorkspace })
<p>上传后直接核对重量,确认结果自动计入实际总毛重。</p>
</div>
<div class="weight-heading__actions">
<div v-if="workspace" class="weight-summary" aria-live="polite">
<span>已确认总毛重</span>
<strong>{{ formatWeight(workspace.actualGrossWeightKg) }}</strong>
</div>
<input
ref="fileInput"
class="visually-hidden-input"
@@ -517,7 +529,10 @@ defineExpose({ reload: loadWorkspace })
.weight-heading h3 { display: flex; align-items: center; gap: 8px; margin: 0; font-size: 15px; }
.weight-heading h3 svg { color: var(--cc-primary); }
.weight-heading p { margin: 6px 0 0; color: var(--cc-muted); font-size: 12px; line-height: 1.6; }
.weight-heading__actions { flex: 0 0 auto; }
.weight-heading__actions { display: flex; flex: 0 0 auto; align-items: center; gap: 16px; }
.weight-summary { display: grid; gap: 2px; text-align: right; }
.weight-summary span { color: var(--cc-muted); font-size: 12px; }
.weight-summary strong { color: #067647; font-size: 18px; }
.visually-hidden-input { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
.weight-state-note, .weight-action-error { margin-bottom: 14px; }
.weight-list { min-height: 120px; margin-top: 16px; }
@@ -550,7 +565,9 @@ defineExpose({ reload: loadWorkspace })
@media (max-width: 720px) {
.weight-heading { flex-direction: column; }
.weight-heading__actions, .weight-heading__actions :deep(.el-button) { width: 100%; min-height: 44px; }
.weight-heading__actions { width: 100%; align-items: stretch; flex-direction: column; }
.weight-heading__actions :deep(.el-button) { width: 100%; min-height: 44px; }
.weight-summary { text-align: left; }
.weight-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}