优化
This commit is contained in:
@@ -13,7 +13,7 @@ import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { getTruckAppointment, listTruckAppointmentAttachments, listTruckAppointmentHistory, performTruckAppointmentAction, rescheduleTruckAppointment, updateTruckAppointment, type TruckAppointmentAction } from '../api/platformTrucking'
|
||||
import TruckAppointmentForm from '../components/TruckAppointmentForm.vue'
|
||||
import TruckAttachmentDialog from '../components/TruckAttachmentDialog.vue'
|
||||
import { truckAppointmentStatusLabels, type SaveTruckAppointmentRequest, type TruckAppointmentResource, type TruckAppointmentStatus, type TruckAppointmentStatusHistory } from '../types'
|
||||
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 })
|
||||
const history = ref<TruckAppointmentStatusHistory[]>([])
|
||||
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') && !['COMPLETED','CANCELLED'].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'))
|
||||
@@ -43,7 +43,7 @@ onMounted(load)
|
||||
<template v-else-if="resource">
|
||||
<section class="surface-panel status-panel"><div class="status-bar"><span>当前状态:<strong>{{ truckAppointmentStatusLabels[resource.status] }}</strong></span><div v-if="canChangeStatus" class="status-actions"><el-button v-for="status in availableTargets" :key="status" :type="status === 'CANCELLED' ? 'danger' : status === 'EXCEPTION' ? 'warning' : 'primary'" plain :loading="statusSaving" @click="changeStatus(status)">{{ truckAppointmentStatusLabels[status] }}</el-button></div></div><ApiErrorMeta v-if="error" :details="error" /></section>
|
||||
<section class="surface-panel form-panel"><TruckAppointmentForm v-model="form" :resource="resource" :readonly="!canEdit" :submitting="saving" @submit="submit" @attachments="attachmentOpen = true" /></section>
|
||||
<section class="surface-panel history-section"><h3>状态历史</h3><el-empty v-if="!history.length" description="暂无状态历史" :image-size="48" /><ol v-else><li v-for="(item, index) in history" :key="item.id || `${item.createdAt}-${index}`"><strong>{{ item.action }}</strong><span>{{ item.fromStatus || '—' }} → {{ item.toStatus || '—' }}</span><small>{{ item.operatorName || item.operatorId || '系统' }} · {{ item.createdAt || item.occurredAt || '—' }}<template v-if="item.reason"> · {{ item.reason }}</template></small></li></ol></section>
|
||||
<section class="surface-panel history-section"><h3>状态历史</h3><el-empty v-if="!history.length" description="暂无状态历史" :image-size="48" /><ol v-else><li v-for="(item, index) in history" :key="item.id || `${item.createdAt}-${index}`"><strong>{{ truckAppointmentActionLabel(item.action) }}</strong><span>{{ truckAppointmentStatusLabel(item.fromStatus) }} → {{ truckAppointmentStatusLabel(item.toStatus) }}</span><small>{{ item.operatorName || item.operatorId || '系统' }} · {{ item.createdAt || item.occurredAt || '—' }}<template v-if="item.reason"> · {{ item.reason }}</template></small></li></ol></section>
|
||||
<TruckAttachmentDialog v-model="attachmentOpen" :appointment-id="id" :attachments="resource.attachments || []" :can-manage="canManageAttachments" @changed="loadRelated" />
|
||||
</template>
|
||||
</DetailPageLayout>
|
||||
|
||||
@@ -12,7 +12,7 @@ import ListPageLayout from '@/shared/components/ListPageLayout.vue'
|
||||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { listTruckAppointments } from '../api/platformTrucking'
|
||||
import { TRUCK_APPOINTMENT_STATUSES, truckAppointmentStatusLabels, type TruckAppointmentQuery, type TruckAppointmentResource, type TruckAppointmentStatus } from '../types'
|
||||
import { TRUCK_APPOINTMENT_STATUSES, truckAppointmentStatusLabel, truckAppointmentStatusLabels, type TruckAppointmentQuery, type TruckAppointmentResource, type TruckAppointmentStatus } from '../types'
|
||||
|
||||
const router = useRouter(); const rows = ref<TruckAppointmentResource[]>([]); const loading = ref(false); const page = ref(1); const pageSize = ref(20); const total = ref(0); const loadError = ref<ApiErrorDetails | null>(null)
|
||||
const access = useAccessPreviewStore()
|
||||
@@ -46,9 +46,7 @@ function appointmentActionLabel(action: '查看' | '编辑', row: TruckAppointme
|
||||
return identifier ? `${action} ${identifier}` : `${action}拖车预约详情`
|
||||
}
|
||||
function statusLabel(value: unknown) {
|
||||
return typeof value === 'string' && value in truckAppointmentStatusLabels
|
||||
? truckAppointmentStatusLabels[value as TruckAppointmentStatus]
|
||||
: String(value || '—')
|
||||
return truckAppointmentStatusLabel(typeof value === 'string' ? value : null)
|
||||
}
|
||||
async function load() { loading.value = true; loadError.value = null; try { const result = await listTruckAppointments({ page: page.value, size: pageSize.value, ...applied.value } as TruckAppointmentQuery); rows.value = result.records; total.value = result.total } catch (error) { rows.value = []; total.value = 0; loadError.value = getApiErrorDetails(error, '拖车预约加载失败,请稍后重试') } finally { loading.value = false } }
|
||||
function search(model: QueryModel) { applied.value = { keyword: String(model.keyword || ''), status: (model.status || '') as TruckAppointmentStatus | '', containerBookingId: '', containerType: String(model.containerType || '') }; if (page.value === 1) void load(); else page.value = 1 }
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { truckAppointmentActionLabel, truckAppointmentStatusLabel } from './types'
|
||||
|
||||
describe('truck appointment status labels', () => {
|
||||
it('translates status history without exposing backend enum values', () => {
|
||||
expect(truckAppointmentStatusLabel('DISPATCHED')).toBe('已派车')
|
||||
expect(truckAppointmentStatusLabel('NEW_STATUS')).toBe('未知状态')
|
||||
expect(truckAppointmentActionLabel('DISPATCH')).toBe('确认派车')
|
||||
expect(truckAppointmentActionLabel('NEW_ACTION')).toBe('状态更新')
|
||||
})
|
||||
})
|
||||
@@ -28,6 +28,30 @@ export const truckAppointmentStatusLabels: Record<TruckAppointmentStatus, string
|
||||
CANCELLED: '已取消',
|
||||
}
|
||||
|
||||
export function truckAppointmentStatusLabel(status: string | null | undefined, emptyLabel = '—') {
|
||||
if (!status) return emptyLabel
|
||||
return truckAppointmentStatusLabels[status as TruckAppointmentStatus] || '未知状态'
|
||||
}
|
||||
|
||||
export const truckAppointmentActionLabels: Record<string, string> = {
|
||||
CREATE: '创建预约',
|
||||
SUBMIT: '提交派车',
|
||||
DISPATCH: '确认派车',
|
||||
PICKUP: '开始提柜',
|
||||
ARRIVE: '到达仓库',
|
||||
LOAD: '开始装柜',
|
||||
DEPART: '离开仓库',
|
||||
COMPLETE: '完成预约',
|
||||
RESCHEDULE: '预约改期',
|
||||
EXCEPTION: '标记异常',
|
||||
CANCEL: '取消预约',
|
||||
}
|
||||
|
||||
export function truckAppointmentActionLabel(action: string | null | undefined) {
|
||||
if (!action) return '状态更新'
|
||||
return truckAppointmentActionLabels[action] || '状态更新'
|
||||
}
|
||||
|
||||
export interface TruckAttachmentReference {
|
||||
id?: string
|
||||
fileId: string
|
||||
|
||||
Reference in New Issue
Block a user