补齐人工复核转换和列表筛选

This commit is contained in:
andy committed 2026-07-09 18:38:29 +08:00
1 parent e12bfd77e1
commit 1282d4aede
12 files changed
+983 -15

No files matched your search

@@ -104,6 +104,12 @@
<div class="task-detail-body">
<div class="task-card-main">
<ReservationManualReviewConversionPanel
v-if="isManualReviewTask"
:busy="mutationBusy !== null"
@submit="submitManualReviewConversion"
/>
<ReservationTaskFieldRenderer
v-model="fieldValues"
:fields="detail.fields"
@@ -159,16 +165,19 @@ import { RouterLink } from 'vue-router'
import { useI18n } from 'vue-i18n'
import ReservationAuditTimeline from '@/components/reservation/ReservationAuditTimeline.vue'
import ReservationManualReviewConversionPanel from '@/components/reservation/ReservationManualReviewConversionPanel.vue'
import ReservationOperaPanel from '@/components/reservation/ReservationOperaPanel.vue'
import ReservationStatusBadge from '@/components/reservation/ReservationStatusBadge.vue'
import ReservationTaskFieldRenderer from '@/components/reservation/ReservationTaskFieldRenderer.vue'
import {
convertReservationManualReviewTask,
confirmReservationTask,
fetchReservationTaskAudits,
fetchReservationTaskDetail,
saveReservationTaskDraft,
} from '@/services/reservationService'
import type {
ReservationManualReviewConversionRequest,
ReservationOperaOperationResult,
ReservationRecord,
ReservationTaskAuditLogResult,
@@ -197,10 +206,11 @@ const audits = ref<ReservationTaskAuditLogResult[]>([])
const fieldValues = ref<ReservationRecord>({})
const validationErrors = ref<ReservationFieldValidationErrors>({})
const validationSummaryActive = ref(false)
const mutationBusy = ref<'save' | 'confirm' | null>(null)
const mutationBusy = ref<'save' | 'confirm' | 'manual-convert' | null>(null)
const canSave = computed(() => Boolean(detail.value?.availability.editable))
const canConfirm = computed(() => Boolean(detail.value?.availability.confirmable))
const isManualReviewTask = computed(() => (detail.value ? taskHasManualReviewCode(detail.value) : false))
const validationErrorMessages = computed(() => toValidationErrorMessages(validationErrors.value))
watch(
@@ -229,11 +239,7 @@ async function loadTask(taskId: string): Promise<void> {
fetchReservationTaskDetail(taskId),
fetchReservationTaskAudits(taskId),
])
detail.value = nextDetail
audits.value = nextAudits.items
fieldValues.value = buildInitialFieldValues(nextDetail.fields, nextDetail.draft_payload ?? nextDetail.confirmed_payload)
validationErrors.value = {}
validationSummaryActive.value = false
applyTaskData(nextDetail, nextAudits.items)
} catch (error) {
errorMessage.value = error instanceof Error ? error.message : String(error)
} finally {
@@ -249,6 +255,36 @@ async function confirmTask(): Promise<void> {
await mutateTaskPayload('confirm')
}
async function submitManualReviewConversion(request: ReservationManualReviewConversionRequest): Promise<void> {
if (!detail.value) {
return
}
actionErrorMessage.value = ''
notice.value = ''
if (request.target_task_type !== 'NEW_BOOKING' && !request.target_order_id) {
actionErrorMessage.value = t('task.manualConversion.targetOrderRequired')
return
}
mutationBusy.value = 'manual-convert'
try {
const result = await convertReservationManualReviewTask(detail.value.task_id, request)
const [nextDetail, nextAudits] = await Promise.all([
fetchReservationTaskDetail(detail.value.task_id),
fetchReservationTaskAudits(detail.value.task_id),
])
applyTaskData(nextDetail, nextAudits.items)
notice.value = t('task.manualConversion.succeeded', {
taskType: formatReservationTaskCard(t, result.task_card_type),
orderId: result.order_id,
})
} catch (error) {
actionErrorMessage.value = error instanceof Error ? error.message : String(error)
} finally {
mutationBusy.value = null
}
}
async function mutateTaskPayload(action: 'save' | 'confirm'): Promise<void> {
if (!detail.value) {
return
@@ -283,6 +319,30 @@ async function mutateTaskPayload(action: 'save' | 'confirm'): Promise<void> {
}
}
function applyTaskData(nextDetail: ReservationTaskDetailResult, nextAudits: ReservationTaskAuditLogResult[]): void {
detail.value = nextDetail
audits.value = nextAudits
fieldValues.value = buildInitialFieldValues(nextDetail.fields, nextDetail.draft_payload ?? nextDetail.confirmed_payload)
validationErrors.value = {}
validationSummaryActive.value = false
}
function taskHasManualReviewCode(taskDetail: ReservationTaskDetailResult): boolean {
const taskCodes = [
taskDetail.system_task_type,
taskDetail.task_card_type,
...taskDetail.fields.flatMap((field) => [field.result_type, field.task_type, field.task_subtype]),
]
return taskCodes.some((code) => {
const normalizedCode = normalizeStableCode(code)
return ['MANUAL_REVIEW', 'FALLBACK', 'FALLBACK_REVIEW'].includes(normalizedCode)
})
}
function normalizeStableCode(code: string | null | undefined): string {
return code?.trim().replace(/[\s-]+/g, '_').toUpperCase() ?? ''
}
function validateBeforeConfirm(taskDetail: ReservationTaskDetailResult): boolean {
const nextErrors = validateReservationFieldValues(taskDetail.fields, fieldValues.value)
validationErrors.value = nextErrors