修复V4目录字段只读与精确查询

This commit is contained in:
andy
2026-07-19 16:38:44 +07:00
parent d808732107
commit 22767e194d
2 changed files with 230 additions and 9 deletions

View File

@@ -146,8 +146,14 @@ type LookupState = {
error: string
result: ReservationV4CatalogLookupResult | null
}
type ExactLookupState = {
loading: boolean
searched: boolean
item: ReservationV4CatalogLookupItem | null
}
const invalidLookupValues = reactive<Record<string, string>>({})
const exactLookupStates = reactive<Record<string, ExactLookupState>>({})
const lookupStates = reactive<Record<ReservationV4LookupKind, LookupState>>({
ACCOUNT: createLookupState(),
ROOM_TYPE: createLookupState(),
@@ -160,14 +166,15 @@ const lookupRequestVersions: Record<ReservationV4LookupKind, number> = {
}
const lookupKinds = computed(() => {
const kinds = props.fields
.filter((field) => isSelectControl(field) && isReservationV4EditableField(field))
.filter((field) => isSelectControl(field) && isEditable(field))
.map((field) => reservationV4LookupKindForOptionsSource(field.options_source))
.filter((kind): kind is ReservationV4LookupKind => Boolean(kind))
return [...new Set(kinds)]
})
const lookupKindKey = computed(() => lookupKinds.value.join('|'))
watch(
[lookupKinds, () => props.hotelId],
[lookupKindKey, () => props.hotelId],
() => {
resetLookupStates()
lookupKinds.value.forEach((kind) => {
@@ -226,6 +233,14 @@ function fieldOptions(field: ReservationV4TaskCardFieldResult): FieldOption[] {
item,
}))
: []
const exactItem = exactLookupItemForField(field)
if (exactItem && !options.some((option) => option.value === exactItem.code)) {
options.push({
value: exactItem.code,
label: lookupItemLabel(exactItem),
item: exactItem,
})
}
return options
}
@@ -287,6 +302,13 @@ function lookupHints(field: ReservationV4TaskCardFieldResult): LookupHint[] {
}
const hints: LookupHint[] = []
if (isExactLookupLoading(field)) {
hints.push({
key: `${kind}-exact-loading`,
message: t('taskV4.lookup.loading'),
tone: 'normal',
})
}
const selectedItem = selectedLookupItem(field)
if (kind === 'ACCOUNT' && selectedItem && (selectedItem.market_code || selectedItem.source_code)) {
hints.push({
@@ -376,11 +398,13 @@ function lookupItemLabel(item: ReservationV4CatalogLookupItem): string {
function selectedLookupItem(field: ReservationV4TaskCardFieldResult): ReservationV4CatalogLookupItem | null {
const kind = reservationV4LookupKindForOptionsSource(field.options_source)
const value = String(fieldValue(field) || '')
const value = String(readV4FieldValue(field, props.modelValue) || '')
if (!kind || !value) {
return null
}
return lookupStates[kind].result?.items.find((item) => item.code === value) ?? null
return lookupStates[kind].result?.items.find((item) => item.code === value)
?? exactLookupStates[exactLookupKey(kind, value)]?.item
?? null
}
function createLookupState(): LookupState {
@@ -402,6 +426,9 @@ function resetLookupStates(): void {
Object.keys(invalidLookupValues).forEach((key) => {
delete invalidLookupValues[key]
})
Object.keys(exactLookupStates).forEach((key) => {
delete exactLookupStates[key]
})
}
async function loadLookup(kind: ReservationV4LookupKind): Promise<void> {
@@ -448,12 +475,12 @@ function clearInvalidLoadedCatalogValues(): void {
const nextValues = { ...props.modelValue }
let changed = false
props.fields.forEach((field) => {
if (!isSelectField(field) || !isReservationV4EditableField(field)) {
if (!isSelectField(field) || !isEditable(field)) {
return
}
const kind = reservationV4LookupKindForOptionsSource(field.options_source)
const result = kind ? lookupStates[kind].result : null
if (!kind || !result || !isFullyLoadedCatalog(result)) {
if (!kind || !result) {
return
}
@@ -466,8 +493,18 @@ function clearInvalidLoadedCatalogValues(): void {
delete invalidLookupValues[key]
return
}
invalidLookupValues[key] = currentValue
nextValues[key] = ''
if (!isFullyLoadedCatalog(result)) {
const exactState = exactLookupStates[exactLookupKey(kind, currentValue)]
if (!exactState) {
void loadExactLookup(kind, currentValue)
return
}
if (exactState.loading || (exactState.searched && exactState.item)) {
delete invalidLookupValues[key]
return
}
}
clearInvalidCatalogValue(field, currentValue, nextValues)
changed = true
})
if (changed) {
@@ -478,6 +515,68 @@ function clearInvalidLoadedCatalogValues(): void {
function isFullyLoadedCatalog(result: ReservationV4CatalogLookupResult): boolean {
return result.items.length >= result.page.total
}
async function loadExactLookup(kind: ReservationV4LookupKind, code: string): Promise<void> {
const key = exactLookupKey(kind, code)
exactLookupStates[key] = {
loading: true,
searched: false,
item: null,
}
try {
const result = await lookupFetcher(kind)({
...(props.hotelId ? { hotel_id: props.hotelId } : {}),
keyword: code,
page_num: 1,
page_size: 20,
})
exactLookupStates[key] = {
loading: false,
searched: true,
item: (result.items ?? []).find((item) => item.code === code) ?? null,
}
} catch {
exactLookupStates[key] = {
loading: false,
searched: true,
item: null,
}
} finally {
clearInvalidLoadedCatalogValues()
}
}
function clearInvalidCatalogValue(
field: ReservationV4TaskCardFieldResult,
currentValue: string,
nextValues: ReservationRecord,
): void {
const key = reservationV4FieldKey(field)
invalidLookupValues[key] = currentValue
nextValues[key] = ''
}
function exactLookupItemForField(field: ReservationV4TaskCardFieldResult): ReservationV4CatalogLookupItem | null {
const kind = reservationV4LookupKindForOptionsSource(field.options_source)
const value = String(readV4FieldValue(field, props.modelValue) || '')
if (!kind || !value) {
return null
}
return exactLookupStates[exactLookupKey(kind, value)]?.item ?? null
}
function isExactLookupLoading(field: ReservationV4TaskCardFieldResult): boolean {
const kind = reservationV4LookupKindForOptionsSource(field.options_source)
const value = String(readV4FieldValue(field, props.modelValue) || '')
if (!kind || !value) {
return false
}
return exactLookupStates[exactLookupKey(kind, value)]?.loading ?? false
}
function exactLookupKey(kind: ReservationV4LookupKind, code: string): string {
return `${props.hotelId ?? ''}:${kind}:${code}`
}
</script>
<style scoped>