修复房表生成日期错误提示国际化

This commit is contained in:
andy committed 2026-07-24 13:03:46 +07:00
1 parent eaea8787d9
commit 3c9430ac5f
3 files changed
+202 -8

No files matched your search

@@ -320,6 +320,13 @@ type RoomingListFieldKey =
| 'roomType'
| 'paymentType'
| 'nationality'
type BackendDetailKind = 'arrivalRequired' | 'departureRequired' | 'departureAfterArrival'
interface BackendDetailMessage {
message: string
fieldKey?: RoomingListFieldKey
kind?: BackendDetailKind
}
const allowedPaymentTypes = new Set(['BTQR', 'CA'])
const allowedNationalities = new Set(['KR', 'CHN'])
@@ -340,7 +347,8 @@ const form = reactive({
const submitting = ref(false)
const validationAttempted = ref(false)
const messages = ref<string[]>([])
const detailMessages = ref<string[]>([])
const staticDetailMessages = ref<string[]>([])
const backendDetailMessages = ref<BackendDetailMessage[]>([])
const generatedFileName = ref('')
const alertTone = ref<AlertTone>('error')
@@ -349,7 +357,16 @@ const sourceFileLabel = computed(() => form.file?.name || t('roomingList.noFile'
const alertTitle = computed(() =>
alertTone.value === 'success' ? t('roomingList.resultSuccess') : t('roomingList.validationTitle'),
)
const fieldErrors = computed<FieldErrors>(() => (validationAttempted.value ? collectFieldErrors() : {}))
const detailMessages = computed<string[]>(() => [
...staticDetailMessages.value,
...backendDetailMessages.value
.filter((detail) => isBackendDetailActive(detail))
.map((detail) => detail.message),
])
const fieldErrors = computed<FieldErrors>(() => ({
...collectBackendFieldErrors(),
...(validationAttempted.value ? collectFieldErrors() : {}),
}))
function chooseSourceFile(event: Event): void {
const input = event.target
@@ -369,7 +386,7 @@ async function submitGeneration(): Promise<void> {
const errors = collectFieldErrors()
if (Object.keys(errors).length > 0) {
messages.value = [t('roomingList.errors.FRONTEND_VALIDATION')]
detailMessages.value = Object.values(errors)
staticDetailMessages.value = Object.values(errors)
return
}
if (!form.file) {
@@ -408,11 +425,12 @@ function handleGenerationError(error: unknown): void {
if (error instanceof RoomingListGenerationError) {
const messageKey = `roomingList.errors.${error.errorCode}`
messages.value = [te(messageKey) ? t(messageKey) : error.message || t('roomingList.errors.UNKNOWN')]
detailMessages.value = error.details
backendDetailMessages.value = mapBackendDetailMessages(error.details)
return
}
messages.value = [t('roomingList.errors.UNKNOWN')]
detailMessages.value = []
staticDetailMessages.value = []
backendDetailMessages.value = []
}
function downloadBlob(blob: Blob, fileName: string): void {
@@ -429,11 +447,121 @@ function downloadBlob(blob: Blob, fileName: string): void {
function clearResult(): void {
messages.value = []
detailMessages.value = []
staticDetailMessages.value = []
backendDetailMessages.value = []
generatedFileName.value = ''
alertTone.value = 'error'
}
function mapBackendDetailMessages(details: string[]): BackendDetailMessage[] {
return details.map((detail) => mapBackendDetailMessage(detail))
}
function mapBackendDetailMessage(detail: string): BackendDetailMessage {
const parsed = parseBackendDetail(detail)
if (parsed?.fieldKey === 'arrival' && isArrivalRequiredBackendDetail(parsed.message)) {
return {
message: t('roomingList.fieldErrors.arrivalRequired'),
fieldKey: 'arrival',
kind: 'arrivalRequired',
}
}
if (parsed?.fieldKey === 'departure') {
if (isDepartureRequiredBackendDetail(parsed.message)) {
return {
message: t('roomingList.fieldErrors.departureRequired'),
fieldKey: 'departure',
kind: 'departureRequired',
}
}
if (isDepartureAfterArrivalBackendDetail(parsed.message)) {
return {
message: t('roomingList.fieldErrors.departureAfterArrival'),
fieldKey: 'departure',
kind: 'departureAfterArrival',
}
}
}
return {
message: detail,
}
}
function parseBackendDetail(detail: string): { fieldKey: RoomingListFieldKey; message: string } | null {
const separatorIndex = detail.indexOf(':')
if (separatorIndex <= 0) {
return null
}
const fieldKey = detail.slice(0, separatorIndex).trim()
if (!isRoomingListFieldKey(fieldKey)) {
return null
}
return {
fieldKey,
message: detail.slice(separatorIndex + 1).trim(),
}
}
function isRoomingListFieldKey(value: string): value is RoomingListFieldKey {
return (
value === 'file' ||
value === 'peoplePerRoom' ||
value === 'arrival' ||
value === 'departure' ||
value === 'roomType' ||
value === 'paymentType' ||
value === 'nationality'
)
}
function isArrivalRequiredBackendDetail(message: string): boolean {
return message.includes('入住日期') && isRequiredBackendDetail(message)
}
function isDepartureRequiredBackendDetail(message: string): boolean {
return message.includes('离店日期') && isRequiredBackendDetail(message)
}
function isRequiredBackendDetail(message: string): boolean {
return message.includes('必须填写') || message.includes('必填') || message.includes('不能为空')
}
function isDepartureAfterArrivalBackendDetail(message: string): boolean {
return message.includes('晚于') && (message.includes('入住日期') || message.toLowerCase().includes('arrival'))
}
function collectBackendFieldErrors(): FieldErrors {
const errors: FieldErrors = {}
backendDetailMessages.value.forEach((detail) => {
if (detail.fieldKey && isBackendDetailActive(detail)) {
errors[detail.fieldKey] = detail.message
}
})
return errors
}
function isBackendDetailActive(detail: BackendDetailMessage): boolean {
switch (detail.kind) {
case 'arrivalRequired':
return !form.arrival.trim()
case 'departureRequired':
return !form.departure.trim()
case 'departureAfterArrival': {
const arrival = form.arrival.trim()
const departure = form.departure.trim()
return Boolean(
arrival &&
departure &&
isDateInputValue(arrival) &&
isDateInputValue(departure) &&
departure <= arrival,
)
}
default:
return true
}
}
function collectFieldErrors(): FieldErrors {
const errors: FieldErrors = {}
if (!form.file) {