适配 M002 V3 字段矩阵前端交互
This commit is contained in:
@@ -2,6 +2,20 @@ import type { ReservationRecord, ReservationTaskFieldResult } from '@/types/rese
|
||||
|
||||
const truthyRuleValues = new Set(['Y', 'YES', 'TRUE', '1', '是', '可编辑', 'ENABLED'])
|
||||
const falsyRuleValues = new Set(['N', 'NO', 'FALSE', '0', '否', '不可编辑', 'DISABLED', '隐藏'])
|
||||
const roomItemFieldPathAliases: Record<string, string[]> = {
|
||||
'extracted_fields.room_quantity': ['extracted_fields.room_items.0.room_quantity'],
|
||||
'extracted_fields.room_type': [
|
||||
'extracted_fields.room_items.0.room_type_raw',
|
||||
'extracted_fields.room_items.0.room_type_normalized',
|
||||
],
|
||||
'extracted_fields.pms_room_type_code': ['extracted_fields.room_items.0.pms_room_type_code'],
|
||||
'extracted_fields.room_items.0.room_quantity': ['extracted_fields.room_quantity'],
|
||||
'extracted_fields.room_items.0.room_type_raw': [
|
||||
'extracted_fields.room_type',
|
||||
'extracted_fields.room_items.0.room_type_normalized',
|
||||
],
|
||||
'extracted_fields.room_items.0.pms_room_type_code': ['extracted_fields.pms_room_type_code'],
|
||||
}
|
||||
|
||||
export interface FieldAreaGroup {
|
||||
area: string
|
||||
@@ -166,9 +180,13 @@ export function parseEnumOptions(enumOptions: string | null): EnumOption[] {
|
||||
})
|
||||
}
|
||||
|
||||
export function buildInitialFieldValues(fields: ReservationTaskFieldResult[], payload: ReservationRecord | null): ReservationRecord {
|
||||
export function buildInitialFieldValues(
|
||||
fields: ReservationTaskFieldResult[],
|
||||
payload: ReservationRecord | null,
|
||||
fieldContractVersion?: string | null,
|
||||
): ReservationRecord {
|
||||
return fields.reduce<ReservationRecord>((values, field) => {
|
||||
values[field.field_path] = payload?.[field.field_path] ?? field.value ?? ''
|
||||
values[field.field_path] = readReservationFieldValue(field, payload ?? {}, fieldContractVersion)
|
||||
return values
|
||||
}, {})
|
||||
}
|
||||
@@ -185,7 +203,7 @@ export function buildEditableFieldValues(
|
||||
.filter((field) => isReservationFieldActive(field, values))
|
||||
.filter((field) => isReservationFieldEditable(field, readOnly))
|
||||
.reduce<ReservationRecord>((payload, field) => {
|
||||
payload[field.field_path] = field.field_path in values ? values[field.field_path] : field.value ?? ''
|
||||
payload[field.field_path] = readReservationFieldValue(field, values)
|
||||
return payload
|
||||
}, {})
|
||||
}
|
||||
@@ -195,13 +213,14 @@ export function validateReservationFieldValues(
|
||||
values: ReservationRecord,
|
||||
): ReservationFieldValidationErrors {
|
||||
return fields.filter((field) => isReservationFieldActive(field, values)).reduce<ReservationFieldValidationErrors>((errors, field) => {
|
||||
const value = values[field.field_path] ?? field.value ?? ''
|
||||
if (isReservationFieldRequired(field) && isEmptyReservationValue(value)) {
|
||||
const value = readReservationFieldValue(field, values)
|
||||
const shouldValidateInput = isReservationFieldEditable(field, false)
|
||||
if (shouldValidateInput && isReservationFieldRequired(field) && isEmptyReservationValue(value)) {
|
||||
errors[field.field_path] = createValidationError(field, 'required')
|
||||
return errors
|
||||
}
|
||||
|
||||
if (isEmptyReservationValue(value)) {
|
||||
if (!shouldValidateInput || isEmptyReservationValue(value)) {
|
||||
return errors
|
||||
}
|
||||
|
||||
@@ -227,8 +246,9 @@ function valueForConditionPath(
|
||||
currentField: ReservationTaskFieldResult,
|
||||
fieldPath: string,
|
||||
): unknown {
|
||||
if (fieldPath in values) {
|
||||
return values[fieldPath]
|
||||
const value = readValueByFieldPath(values, fieldPath)
|
||||
if (value !== undefined) {
|
||||
return value
|
||||
}
|
||||
if (fieldPath === currentField.field_path) {
|
||||
return currentField.value
|
||||
@@ -306,6 +326,121 @@ function isIsoLocalDate(value: string): boolean {
|
||||
return !Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === value
|
||||
}
|
||||
|
||||
export function readReservationFieldValue(
|
||||
field: ReservationTaskFieldResult,
|
||||
values: ReservationRecord,
|
||||
fieldContractVersion?: string | null,
|
||||
): unknown {
|
||||
const fieldValues = recordProperty(values, 'field_values') ?? values
|
||||
const legacyValues = recordProperty(values, 'legacy_field_values')
|
||||
const effectivePayload = recordProperty(values, 'effective_payload')
|
||||
const legacyFieldPath = field.legacy_field_path ?? undefined
|
||||
const codeV1 = normalizeContractVersion(fieldContractVersion) === 'CODE_V1'
|
||||
const mainCandidates = fieldPathCandidates(field.field_path)
|
||||
const legacyCandidates = legacyFieldPath ? fieldPathCandidates(legacyFieldPath) : []
|
||||
|
||||
if (codeV1) {
|
||||
return firstDefined([
|
||||
() => firstValueByPaths(legacyValues, legacyCandidates),
|
||||
() => firstValueByPaths(fieldValues, legacyCandidates),
|
||||
() => firstNestedValueByPaths(fieldValues, legacyCandidates),
|
||||
() => firstValueByPaths(values, legacyCandidates),
|
||||
() => firstNestedValueByPaths(effectivePayload, legacyCandidates),
|
||||
() => firstValueByPaths(fieldValues, mainCandidates),
|
||||
() => firstNestedValueByPaths(fieldValues, mainCandidates),
|
||||
() => firstValueByPaths(values, mainCandidates),
|
||||
() => firstNestedValueByPaths(effectivePayload, mainCandidates),
|
||||
() => field.value,
|
||||
() => '',
|
||||
])
|
||||
}
|
||||
|
||||
return firstDefined([
|
||||
() => firstValueByPaths(fieldValues, mainCandidates),
|
||||
() => firstNestedValueByPaths(fieldValues, mainCandidates),
|
||||
() => firstValueByPaths(values, mainCandidates),
|
||||
() => firstNestedValueByPaths(effectivePayload, mainCandidates),
|
||||
() => firstValueByPaths(legacyValues, legacyCandidates),
|
||||
() => firstValueByPaths(fieldValues, legacyCandidates),
|
||||
() => firstNestedValueByPaths(fieldValues, legacyCandidates),
|
||||
() => firstValueByPaths(values, legacyCandidates),
|
||||
() => field.value,
|
||||
() => '',
|
||||
])
|
||||
}
|
||||
|
||||
function readValueByFieldPath(values: ReservationRecord, fieldPath: string): unknown {
|
||||
const flatValue = firstValueByPaths(values, fieldPathCandidates(fieldPath))
|
||||
if (flatValue !== undefined) {
|
||||
return flatValue
|
||||
}
|
||||
return readNestedPath(values, fieldPath)
|
||||
}
|
||||
|
||||
function fieldPathCandidates(fieldPath: string): string[] {
|
||||
return [fieldPath, ...(roomItemFieldPathAliases[fieldPath] ?? [])]
|
||||
}
|
||||
|
||||
function firstValueByPaths(values: ReservationRecord | undefined, fieldPaths: string[]): unknown {
|
||||
if (!values) {
|
||||
return undefined
|
||||
}
|
||||
for (const fieldPath of fieldPaths) {
|
||||
if (Object.prototype.hasOwnProperty.call(values, fieldPath)) {
|
||||
return values[fieldPath]
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function firstNestedValueByPaths(values: ReservationRecord | undefined, fieldPaths: string[]): unknown {
|
||||
if (!values) {
|
||||
return undefined
|
||||
}
|
||||
for (const fieldPath of fieldPaths) {
|
||||
const value = readNestedPath(values, fieldPath)
|
||||
if (value !== undefined) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function readNestedPath(values: ReservationRecord, fieldPath: string): unknown {
|
||||
return fieldPath.split('.').reduce<unknown>((current, segment) => {
|
||||
if (current === null || current === undefined) {
|
||||
return undefined
|
||||
}
|
||||
if (Array.isArray(current)) {
|
||||
const index = Number(segment)
|
||||
return Number.isInteger(index) ? current[index] : undefined
|
||||
}
|
||||
if (typeof current === 'object' && Object.prototype.hasOwnProperty.call(current, segment)) {
|
||||
return (current as ReservationRecord)[segment]
|
||||
}
|
||||
return undefined
|
||||
}, values)
|
||||
}
|
||||
|
||||
function firstDefined(candidates: Array<() => unknown>): unknown {
|
||||
for (const candidate of candidates) {
|
||||
const value = candidate()
|
||||
if (value !== undefined && value !== null) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
function recordProperty(values: ReservationRecord, key: string): ReservationRecord | undefined {
|
||||
const value = values[key]
|
||||
return value && typeof value === 'object' && !Array.isArray(value) ? value as ReservationRecord : undefined
|
||||
}
|
||||
|
||||
function normalizeContractVersion(version: string | null | undefined): string {
|
||||
return version?.trim().replace(/[\s-]+/g, '_').toUpperCase() ?? ''
|
||||
}
|
||||
|
||||
function isRuleTruthy(value: string | null): boolean {
|
||||
if (!value) {
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user