This commit is contained in:
2026-08-22 10:02:13 +08:00
parent 19683f8759
commit bff9ccbfb6
8 changed files with 63 additions and 18 deletions
@@ -44,4 +44,12 @@ describe('TruckAppointmentDetailView status contract', () => {
expect(source).toContain("status === 'DISPATCHED' && dispatchBlockReason.value")
expect(source).toContain('attachmentOpen.value = true')
})
it('keeps the main detail available when attachments or history fail', () => {
expect(source).toContain('Promise.allSettled([')
expect(source).toContain("attachments.status === 'fulfilled'")
expect(source).toContain("histories.status === 'fulfilled'")
expect(source).toContain('主详情数据不受影响')
expect(source).toContain('v-if="relatedError"')
})
})
@@ -15,9 +15,20 @@ import TruckAttachmentDialog from '../components/TruckAttachmentDialog.vue'
import { truckAppointmentActionLabel, truckAppointmentStatusLabel, truckAppointmentStatusLabels, type SaveTruckAppointmentRequest, type TruckAppointmentResource, type TruckAppointmentStatus, type TruckAppointmentStatusHistory } from '../types'
const route = useRoute(); const router = useRouter(); const loading = ref(false); const saving = ref(false); const statusSaving = ref(false); const attachmentOpen = ref(false); const resource = ref<TruckAppointmentResource | null>(null); const error = ref<ApiErrorDetails | null>(null); const form = reactive<SaveTruckAppointmentRequest>({ expectedVersion: 0, customerId: null, carrierId: null, plannedLoadingAt: null })
const history = ref<TruckAppointmentStatusHistory[]>([])
const relatedError = ref('')
const access = useAccessPreviewStore(); const editing = computed(() => route.query.edit === '1'); const id = computed(() => String(route.params.id)); const canEdit = computed(() => access.can('platform:firstleg:truck-appointment:edit') && ['DRAFT', 'PENDING_DISPATCH', 'RESCHEDULED'].includes(resource.value?.status || '') && editing.value); const canChangeStatus = computed(() => access.can('platform:firstleg:truck-appointment:operate')); const canManageAttachments = computed(() => access.can('platform:firstleg:truck-appointment:edit'))
const relationSnapshot = computed(() => resource.value ? [resource.value.customerName, resource.value.oceanBookingNo, resource.value.cabinetNo, resource.value.containerNo, resource.value.warehouseName].filter(Boolean).join(' / ') || '无历史关联' : '')
async function loadRelated() { if (!resource.value) return; const [attachments, histories] = await Promise.all([listTruckAppointmentAttachments(id.value), listTruckAppointmentHistory(id.value)]); resource.value.attachments = attachments; history.value = histories }
async function loadRelated() {
if (!resource.value) return
relatedError.value = ''
const [attachments, histories] = await Promise.allSettled([
listTruckAppointmentAttachments(id.value),
listTruckAppointmentHistory(id.value),
])
if (attachments.status === 'fulfilled') resource.value.attachments = attachments.value
if (histories.status === 'fulfilled') history.value = histories.value
if (attachments.status === 'rejected' || histories.status === 'rejected') relatedError.value = '附件或状态历史暂时未能加载,主详情数据不受影响,可稍后刷新重试。'
}
async function load() { loading.value = true; error.value = null; try { const result = await getTruckAppointment(id.value); resource.value = result; Object.assign(form, { ...result, expectedVersion: result.version ?? 0 }); await loadRelated() } catch (err) { error.value = getApiErrorDetails(err, '详情加载失败,请稍后重试') } finally { loading.value = false } }
async function submit() { saving.value = true; error.value = null; try { await updateTruckAppointment(id.value, form); ElMessage.success('拖车预约已保存'); await load() } catch (err) { error.value = getApiErrorDetails(err, '保存失败,请稍后重试') } finally { saving.value = false } }
const actionByTarget: Partial<Record<TruckAppointmentStatus, TruckAppointmentAction>> = { PENDING_DISPATCH: 'SUBMIT', DISPATCHED: 'DISPATCH', PICKUP_IN_PROGRESS: 'PICKUP', ARRIVED_WAREHOUSE: 'ARRIVE', LOADING: 'LOAD', DEPARTED: 'DEPART', COMPLETED: 'COMPLETE', EXCEPTION: 'EXCEPTION', CANCELLED: 'CANCEL' }
@@ -56,6 +67,7 @@ onMounted(load)
<template v-if="resource">
<p class="relation-snapshot"><strong>历史关联快照:</strong>{{ relationSnapshot }}</p>
<el-alert v-if="dispatchBlockReason" :title="dispatchBlockReason" type="warning" :closable="false" show-icon />
<el-alert v-if="relatedError" :title="relatedError" type="warning" :closable="false" show-icon />
<ApiErrorMeta v-if="error" :details="error" />
</template>
</section>