修复人工复核必填字段提交

This commit is contained in:
andy
2026-07-13 10:09:59 +08:00
parent dec98b0632
commit 9ad294f406
2 changed files with 82 additions and 12 deletions

View File

@@ -141,6 +141,7 @@ import type {
} from '@/types/reservation' } from '@/types/reservation'
import { normalizeStableCode } from '@/utils/reservationDisplay' import { normalizeStableCode } from '@/utils/reservationDisplay'
import { import {
isReservationFieldRequired,
isReservationManualReviewFieldWritable, isReservationManualReviewFieldWritable,
readReservationFieldValue, readReservationFieldValue,
} from '@/utils/reservationFieldRules' } from '@/utils/reservationFieldRules'
@@ -184,11 +185,7 @@ const formErrorMessage = ref('')
const fieldOverrideInputs = ref<Record<string, string>>({}) const fieldOverrideInputs = ref<Record<string, string>>({})
const isResolved = computed(() => normalizeStableCode(props.reviewStatus) === 'RESOLVED') const isResolved = computed(() => normalizeStableCode(props.reviewStatus) === 'RESOLVED')
const reviewStatusLabel = computed(() => props.reviewStatus || t('task.manualResolution.pendingStatus')) const reviewStatusLabel = computed(() => props.reviewStatus || t('task.manualResolution.pendingStatus'))
const supportedMissingFields = computed(() => const supportedMissingFields = computed(() => buildReviewFieldItems())
extractMissingFields(props.manualReview)
.map((reference) => resolveMissingField(reference))
.filter((item): item is ManualResolutionFieldItem => item !== null),
)
const unsupportedMissingFields = computed(() => const unsupportedMissingFields = computed(() =>
extractMissingFields(props.manualReview).filter((reference) => resolveMissingField(reference) === null), extractMissingFields(props.manualReview).filter((reference) => resolveMissingField(reference) === null),
) )
@@ -253,17 +250,58 @@ function resolveMissingField(reference: string): ManualResolutionFieldItem | nul
item.legacy_field_path === referenceFieldPath, item.legacy_field_path === referenceFieldPath,
) )
if (field && isReservationManualReviewFieldWritable(field)) { if (field && isReservationManualReviewFieldWritable(field)) {
return { return reviewFieldItemFromField(field, pointer)
key: field.field_pointer ?? field.field_path,
pointer: field.field_pointer ?? pointer,
fieldPath: field.field_path,
label: field.display_name,
field,
}
} }
return null return null
} }
function buildReviewFieldItems(): ManualResolutionFieldItem[] {
const items: ManualResolutionFieldItem[] = []
const seenKeys = new Set<string>()
extractMissingFields(props.manualReview).forEach((reference) => {
const item = resolveMissingField(reference)
if (item) {
appendReviewFieldItem(items, seenKeys, item)
}
})
props.fields
.filter((field) => isReservationManualReviewFieldWritable(field))
.filter((field) => isReservationFieldRequired(field))
.filter((field) => isEmptyReviewFieldValue(readReservationFieldValue(field, props.fieldValues)))
.forEach((field) => appendReviewFieldItem(items, seenKeys, reviewFieldItemFromField(field)))
return items
}
function reviewFieldItemFromField(
field: ReservationTaskFieldResult,
fallbackPointer?: string,
): ManualResolutionFieldItem {
return {
key: field.field_pointer ?? field.field_path,
pointer: field.field_pointer ?? fallbackPointer,
fieldPath: field.field_path,
label: field.display_name,
field,
}
}
function appendReviewFieldItem(
items: ManualResolutionFieldItem[],
seenKeys: Set<string>,
item: ManualResolutionFieldItem,
): void {
const dedupeKeys = [
item.pointer,
item.fieldPath,
item.field?.legacy_field_path,
].filter((key): key is string => Boolean(key))
if (dedupeKeys.some((key) => seenKeys.has(key))) {
return
}
dedupeKeys.forEach((key) => seenKeys.add(key))
items.push(item)
}
function readCurrentFieldValue(item: ManualResolutionFieldItem): string { function readCurrentFieldValue(item: ManualResolutionFieldItem): string {
const value = item.field const value = item.field
? readReservationFieldValue(item.field, props.fieldValues) ? readReservationFieldValue(item.field, props.fieldValues)
@@ -275,6 +313,19 @@ function fieldPointerToFieldPath(pointer: string): string {
return pointer.replace(/^\//, '').replace(/\//g, '.') return pointer.replace(/^\//, '').replace(/\//g, '.')
} }
function isEmptyReviewFieldValue(value: unknown): boolean {
if (value === null || value === undefined) {
return true
}
if (typeof value === 'string') {
return value.trim() === ''
}
if (Array.isArray(value)) {
return value.length === 0
}
return false
}
function formatJson(value: unknown): string { function formatJson(value: unknown): string {
return JSON.stringify(value ?? {}, null, 2) return JSON.stringify(value ?? {}, null, 2)
} }

View File

@@ -920,6 +920,17 @@ describe('ReservationTaskDetailPanel', () => {
input_editable: 'N', input_editable: 'N',
value: null, value: null,
}), }),
createRequiredField({
field_path: 'extracted_fields.room_items.0.room_quantity',
field_pointer: '/extracted_fields/room_items/0/room_quantity',
legacy_field_path: 'extracted_fields.room_quantity',
display_name: '房量',
control_type: 'number',
edit_scope: 'normal_and_manual_review',
write_target: 'draft_payload_and_review_resolution',
raw_readonly: false,
value: null,
}),
], ],
}), }),
) )
@@ -974,12 +985,15 @@ describe('ReservationTaskDetailPanel', () => {
expect(wrapper.text()).toContain('人工复核解阻') expect(wrapper.text()).toContain('人工复核解阻')
expect(wrapper.text()).toContain('/extracted_fields/room_items/0/pms_room_type_code') expect(wrapper.text()).toContain('/extracted_fields/room_items/0/pms_room_type_code')
expect(wrapper.text()).toContain('PMS 房型代码') expect(wrapper.text()).toContain('PMS 房型代码')
expect(wrapper.text()).toContain('/extracted_fields/room_items/0/room_quantity')
expect(wrapper.text()).toContain('房量')
expect(wrapper.text()).not.toContain('人工转换') expect(wrapper.text()).not.toContain('人工转换')
expect(wrapper.text()).not.toContain('保存草稿') expect(wrapper.text()).not.toContain('保存草稿')
expect(wrapper.text()).not.toContain('确认任务') expect(wrapper.text()).not.toContain('确认任务')
expect(wrapper.text()).not.toContain('OPERA 模拟操作') expect(wrapper.text()).not.toContain('OPERA 模拟操作')
await wrapper.find('input[name="manual_resolution_field_override_0"]').setValue('RM3') await wrapper.find('input[name="manual_resolution_field_override_0"]').setValue('RM3')
await wrapper.find('input[name="manual_resolution_field_override_1"]').setValue('2')
await wrapper.find('textarea[name="manual_resolution_reason"]').setValue('确认 PMS 房型代码') await wrapper.find('textarea[name="manual_resolution_reason"]').setValue('确认 PMS 房型代码')
await findButton(wrapper, '提交复核结果').trigger('click') await findButton(wrapper, '提交复核结果').trigger('click')
await flushPromises() await flushPromises()
@@ -993,6 +1007,11 @@ describe('ReservationTaskDetailPanel', () => {
field_path: 'extracted_fields.room_items.0.pms_room_type_code', field_path: 'extracted_fields.room_items.0.pms_room_type_code',
value: 'RM3', value: 'RM3',
}, },
{
field_pointer: '/extracted_fields/room_items/0/room_quantity',
field_path: 'extracted_fields.room_items.0.room_quantity',
value: '2',
},
], ],
}) })
expect(service.fetchReservationTaskDetail).toHaveBeenCalledTimes(2) expect(service.fetchReservationTaskDetail).toHaveBeenCalledTimes(2)