适配 M002 V3 字段矩阵前端交互

This commit is contained in:
andy committed 2026-07-12 09:23:03 +08:00
1 parent db95aece20
commit 9bd6bc1cd0
11 files changed
+515 -66

No files matched your search

@@ -55,14 +55,14 @@
>
<label
v-for="(item, index) in supportedMissingFields"
:key="item.pointer"
:key="item.key"
>
<span>
{{ item.label }}
<small class="th-code">{{ item.pointer }}</small>
<small class="th-code">{{ item.pointer ?? item.fieldPath }}</small>
</span>
<input
v-model="fieldOverrideInputs[item.pointer]"
v-model="fieldOverrideInputs[item.key]"
:name="`manual_resolution_field_override_${index}`"
type="text"
:disabled="busy"
@@ -124,11 +124,15 @@ import type {
ReservationTaskFieldResult,
} from '@/types/reservation'
import { normalizeStableCode } from '@/utils/reservationDisplay'
import { readReservationFieldValue } from '@/utils/reservationFieldRules'
const supportedFieldPointers = [
'/extracted_fields/pms_room_type_code',
'/extracted_fields/room_items/0/pms_room_type_code',
] as const
interface ManualResolutionFieldItem {
key: string
pointer?: string
fieldPath: string
label: string
field?: ReservationTaskFieldResult
}
const props = withDefaults(
defineProps<{
@@ -163,17 +167,14 @@ const isResolved = computed(() => normalizeStableCode(props.reviewStatus) === 'R
const reviewStatusLabel = computed(() => props.reviewStatus || t('task.manualResolution.pendingStatus'))
const supportedMissingFields = computed(() =>
extractMissingFields(props.manualReview)
.filter((pointer) => supportedFieldPointers.includes(pointer as (typeof supportedFieldPointers)[number]))
.map((pointer) => ({
pointer,
label: findFieldLabel(pointer),
})),
.map((reference) => resolveMissingField(reference))
.filter((item): item is ManualResolutionFieldItem => item !== null),
)
const canSubmit = computed(() => {
if (!supportedMissingFields.value.length || !confirmedOrderId.value.trim()) {
return false
}
return supportedMissingFields.value.every((item) => String(fieldOverrideInputs.value[item.pointer] ?? '').trim())
return supportedMissingFields.value.every((item) => String(fieldOverrideInputs.value[item.key] ?? '').trim())
})
watch(
@@ -188,7 +189,7 @@ watch(
(items) => {
const nextInputs: Record<string, string> = {}
items.forEach((item) => {
nextInputs[item.pointer] = fieldOverrideInputs.value[item.pointer] || readCurrentFieldValue(item.pointer)
nextInputs[item.key] = fieldOverrideInputs.value[item.key] || readCurrentFieldValue(item)
})
fieldOverrideInputs.value = nextInputs
},
@@ -206,8 +207,9 @@ function submitResolution(): void {
confirmed_order_id: confirmedOrderId.value.trim(),
reason: reason.value.trim() || undefined,
field_overrides: supportedMissingFields.value.map((item) => ({
field_pointer: item.pointer,
value: String(fieldOverrideInputs.value[item.pointer] ?? '').trim(),
...(item.pointer ? { field_pointer: item.pointer } : {}),
field_path: item.fieldPath,
value: String(fieldOverrideInputs.value[item.key] ?? '').trim(),
})),
})
}
@@ -217,24 +219,37 @@ function extractMissingFields(manualReview: ReservationRecord | null | undefined
if (!Array.isArray(missingFields)) {
return []
}
return missingFields.filter((item): item is string => typeof item === 'string' && item.startsWith('/'))
return missingFields.filter((item): item is string => typeof item === 'string' && item.trim() !== '')
}
function findFieldLabel(pointer: string): string {
const fieldPath = fieldPointerToFieldPath(pointer)
return props.fields.find((field) => field.field_path === fieldPath)?.display_name ?? fieldPath
function resolveMissingField(reference: string): ManualResolutionFieldItem | null {
const pointer = reference.startsWith('/') ? reference : undefined
const referenceFieldPath = pointer ? fieldPointerToFieldPath(pointer) : reference
const field = props.fields.find((item) =>
item.field_pointer === reference ||
item.field_path === referenceFieldPath ||
item.legacy_field_path === referenceFieldPath,
)
if (field) {
return {
key: field.field_pointer ?? field.field_path,
pointer: field.field_pointer ?? pointer,
fieldPath: field.field_path,
label: field.display_name,
field,
}
}
return null
}
function readCurrentFieldValue(pointer: string): string {
const fieldPath = fieldPointerToFieldPath(pointer)
const value = props.fieldValues[fieldPath] ?? props.fields.find((field) => field.field_path === fieldPath)?.value
function readCurrentFieldValue(item: ManualResolutionFieldItem): string {
const value = item.field
? readReservationFieldValue(item.field, props.fieldValues)
: props.fieldValues[item.fieldPath]
return value === undefined || value === null ? '' : String(value)
}
function fieldPointerToFieldPath(pointer: string): string {
if (pointer === '/extracted_fields/room_items/0/pms_room_type_code') {
return 'extracted_fields.pms_room_type_code'
}
return pointer.replace(/^\//, '').replace(/\//g, '.')
}
@@ -594,7 +594,11 @@ async function mutateTaskPayload(action: 'save' | 'confirm'): Promise<void> {
function applyTaskData(nextDetail: ReservationTaskDetailResult, nextAudits: ReservationTaskAuditLogResult[]): void {
detail.value = nextDetail
audits.value = nextAudits
fieldValues.value = buildInitialFieldValues(nextDetail.fields, nextDetail.draft_payload ?? nextDetail.confirmed_payload)
fieldValues.value = buildInitialFieldValues(
nextDetail.fields,
nextDetail.draft_payload ?? nextDetail.confirmed_payload,
nextDetail.field_contract_version,
)
validationErrors.value = {}
validationSummaryActive.value = false
}
@@ -115,6 +115,7 @@ import {
isReservationFieldRequired,
isSelectField,
isTableField,
readReservationFieldValue,
parseEnumOptions,
stringifyReservationValue,
} from '@/utils/reservationFieldRules'
@@ -135,7 +136,7 @@ const { t } = useI18n()
const fieldGroups = computed(() => groupReservationFields(props.fields, t('task.fieldAreaFallback'), props.modelValue))
function fieldValue(field: ReservationTaskFieldResult): string | number | readonly string[] {
const value = props.modelValue[field.field_path] ?? field.value ?? ''
const value = readReservationFieldValue(field, props.modelValue)
if (Array.isArray(value)) {
return value.map((item) => stringifyReservationValue(item, t('task.emptyValue')))
}