补齐人工复核转换和列表筛选
This commit is contained in:
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
|
||||
|
||||
Reference in new issue
Block a user