接入前端真实接口并标记邮件HTML清洗

This commit is contained in:
andy committed 2026-07-08 23:37:03 +08:00
1 parent c83935a26c
commit a0cb3a05d7
25 files changed
+2581 -157

No files matched your search

@@ -31,7 +31,7 @@
{{ t('task.viewOrder') }} {{ detail.order_id }}
</RouterLink>
<span
v-if="!detail.source_message_id || !detail.external_conversation_id"
v-if="!detail.source_message_id"
class="task-link task-link--disabled"
>
{{ t('task.viewConversation') }} · {{ t('common.endpointMissing') }}
@@ -108,6 +108,7 @@
v-model="fieldValues"
:fields="detail.fields"
:read-only="detail.availability.read_only"
:validation-errors="validationErrorMessages"
/>
<footer class="task-actions">
@@ -173,7 +174,12 @@ import type {
ReservationTaskAuditLogResult,
ReservationTaskDetailResult,
} from '@/types/reservation'
import { buildInitialFieldValues } from '@/utils/reservationFieldRules'
import {
buildEditableFieldValues,
buildInitialFieldValues,
validateReservationFieldValues,
type ReservationFieldValidationErrors,
} from '@/utils/reservationFieldRules'
import { formatReservationDateTime } from '@/utils/reservationFormat'
const props = defineProps<{
@@ -188,10 +194,13 @@ const notice = ref('')
const detail = ref<ReservationTaskDetailResult | null>(null)
const audits = ref<ReservationTaskAuditLogResult[]>([])
const fieldValues = ref<ReservationRecord>({})
const validationErrors = ref<ReservationFieldValidationErrors>({})
const validationSummaryActive = ref(false)
const mutationBusy = ref<'save' | 'confirm' | null>(null)
const canSave = computed(() => Boolean(detail.value?.availability.editable))
const canConfirm = computed(() => Boolean(detail.value?.availability.confirmable))
const validationErrorMessages = computed(() => toValidationErrorMessages(validationErrors.value))
watch(
() => props.taskId,
@@ -201,6 +210,14 @@ watch(
{ immediate: true },
)
watch(
fieldValues,
() => {
refreshValidationAfterFieldChange()
},
{ deep: true },
)
async function loadTask(taskId: string): Promise<void> {
loading.value = true
errorMessage.value = ''
@@ -214,6 +231,8 @@ async function loadTask(taskId: string): Promise<void> {
detail.value = nextDetail
audits.value = nextAudits.items
fieldValues.value = buildInitialFieldValues(nextDetail.fields, nextDetail.draft_payload ?? nextDetail.confirmed_payload)
validationErrors.value = {}
validationSummaryActive.value = false
} catch (error) {
errorMessage.value = error instanceof Error ? error.message : String(error)
} finally {
@@ -236,10 +255,14 @@ async function mutateTaskPayload(action: 'save' | 'confirm'): Promise<void> {
const taskId = detail.value.task_id
mutationBusy.value = action
actionErrorMessage.value = ''
validationSummaryActive.value = false
notice.value = ''
try {
if (action === 'confirm' && !validateBeforeConfirm(detail.value)) {
return
}
const request = {
field_values: fieldValues.value,
field_values: buildEditableFieldValues(detail.value.fields, fieldValues.value, detail.value.availability.read_only),
}
const result =
action === 'save' ? await saveReservationTaskDraft(taskId, request) : await confirmReservationTask(taskId, request)
@@ -259,6 +282,42 @@ async function mutateTaskPayload(action: 'save' | 'confirm'): Promise<void> {
}
}
function validateBeforeConfirm(taskDetail: ReservationTaskDetailResult): boolean {
const nextErrors = validateReservationFieldValues(taskDetail.fields, fieldValues.value)
validationErrors.value = nextErrors
if (hasValidationErrors(nextErrors)) {
actionErrorMessage.value = t('task.validationFailed')
validationSummaryActive.value = true
return false
}
return true
}
function refreshValidationAfterFieldChange(): void {
if (!detail.value || (!hasValidationErrors(validationErrors.value) && !validationSummaryActive.value)) {
return
}
const nextErrors = validateReservationFieldValues(detail.value.fields, fieldValues.value)
validationErrors.value = nextErrors
if (!hasValidationErrors(nextErrors) && validationSummaryActive.value) {
actionErrorMessage.value = ''
validationSummaryActive.value = false
}
}
function hasValidationErrors(errors: ReservationFieldValidationErrors): boolean {
return Object.keys(errors).length > 0
}
function toValidationErrorMessages(errors: ReservationFieldValidationErrors): Record<string, string> {
return Object.fromEntries(
Object.entries(errors).map(([fieldPath, error]) => [
fieldPath,
t(`task.validation.${error.code}`, { field: error.field_name }),
]),
)
}
async function refreshAudits(taskId: string): Promise<void> {
const nextAudits = await fetchReservationTaskAudits(taskId)
audits.value = nextAudits.items