接入前端真实接口并标记邮件HTML清洗
This commit is contained in:
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
|
||||
|
||||
Reference in new issue
Block a user