优化
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||||
import AdminPage from '@/shared/components/AdminPage.vue'
|
||||
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
||||
import DataTable, { type DataTableColumn } from '@/shared/components/DataTable.vue'
|
||||
import ListPageLayout from '@/shared/components/ListPageLayout.vue'
|
||||
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
|
||||
import QueryToolbar, { type QueryField, type QueryModel } from '@/shared/components/QueryToolbar.vue'
|
||||
import StatusBadge from '@/shared/components/StatusBadge.vue'
|
||||
import { listDispatches, type DispatchSummaryDto } from '../api/dispatches'
|
||||
import type { DispatchListItem } from '../types'
|
||||
|
||||
const rows = ref<DispatchListItem[]>([])
|
||||
const loading = ref(false)
|
||||
const loadError = ref<ApiErrorDetails | null>(null)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const total = ref(0)
|
||||
const filters = ref<QueryModel>({ dispatchNo: '', vehiclePlate: '', status: '', fbaAppointmentNo: '' })
|
||||
|
||||
const statusOptions = [
|
||||
{ label: '待发车', value: 'PENDING_DEPARTURE' },
|
||||
{ label: '拣货中', value: 'PICKING' },
|
||||
{ label: '待发车(拣货完成)', value: 'READY_TO_DEPART' },
|
||||
{ label: '待发车(部分完成)', value: 'PARTIAL_READY_TO_DEPART' },
|
||||
{ label: '已发车', value: 'DEPARTED' },
|
||||
{ label: '已发车(部分完成)', value: 'PARTIAL_DEPARTED' },
|
||||
{ label: '已取消', value: 'CANCELLED' },
|
||||
]
|
||||
|
||||
const fields = computed<QueryField[]>(() => [
|
||||
{ key: 'dispatchNo', type: 'input', label: '排车单号', placeholder: '请输入排车单号' },
|
||||
{ key: 'vehiclePlate', type: 'input', label: '车牌号', placeholder: '请输入车牌号' },
|
||||
{ key: 'status', type: 'select', label: '车辆状态', placeholder: '全部状态', options: statusOptions },
|
||||
{ key: 'fbaAppointmentNo', type: 'input', label: 'FBA预约号', placeholder: '请输入FBA预约号' },
|
||||
])
|
||||
|
||||
const columns: DataTableColumn[] = [
|
||||
{ key: 'dispatchNo', prop: 'dispatchNo', label: '排车单号', minWidth: 170, fixed: 'left' },
|
||||
{ key: 'vehiclePlate', prop: 'vehiclePlate', label: '车牌号', minWidth: 130 },
|
||||
{ key: 'driverName', prop: 'driverName', label: '司机', minWidth: 120 },
|
||||
{ key: 'driverPhone', prop: 'driverPhone', label: '联系电话', minWidth: 140 },
|
||||
{ key: 'carrierName', prop: 'carrierName', label: '托车公司名称', minWidth: 160 },
|
||||
{ key: 'orderNos', prop: 'orderNos', label: '关联订单', minWidth: 220 },
|
||||
{ key: 'route', prop: 'route', label: '路线', minWidth: 180 },
|
||||
{ key: 'overseasWarehouse', prop: 'overseasWarehouse', label: '海外仓', minWidth: 160 },
|
||||
{ key: 'departAt', prop: 'departAt', label: '发车时间', minWidth: 170 },
|
||||
{ key: 'fbaAppointment', prop: 'fbaAppointment', label: 'FBA预约', minWidth: 180 },
|
||||
{ key: 'vehicleAppointment', prop: 'vehicleAppointment', label: '车辆预约', minWidth: 130 },
|
||||
{ key: 'estimatedArrivalAt', prop: 'estimatedArrivalAt', label: '预计到达时间', minWidth: 170 },
|
||||
{ key: 'palletCount', prop: 'palletCount', label: '托盘数', width: 90, align: 'right' },
|
||||
{ key: 'packageCount', prop: 'packageCount', label: '箱数', width: 80, align: 'right' },
|
||||
{ key: 'status', prop: 'status', label: '车辆状态', width: 150, slotName: 'status' },
|
||||
{ key: 'createdAt', prop: 'createdAt', label: '创建时间', minWidth: 170 },
|
||||
]
|
||||
|
||||
function text(value: unknown) {
|
||||
const normalized = String(value ?? '').trim()
|
||||
return normalized || '—'
|
||||
}
|
||||
|
||||
function number(value: unknown) {
|
||||
const normalized = Number(value)
|
||||
return Number.isFinite(normalized) ? normalized : 0
|
||||
}
|
||||
|
||||
function statusLabel(value: unknown) {
|
||||
const status = String(value || '')
|
||||
return statusOptions.find((option) => option.value === status)?.label || text(value)
|
||||
}
|
||||
|
||||
function appointmentLabel(numberValue: unknown, statusValue: unknown) {
|
||||
const appointmentNo = text(numberValue)
|
||||
const appointmentStatus = text(statusValue)
|
||||
if (appointmentNo === '—') return appointmentStatus
|
||||
return appointmentStatus === '—' ? appointmentNo : `${appointmentNo} · ${appointmentStatus}`
|
||||
}
|
||||
|
||||
function toListItem(dto: DispatchSummaryDto): DispatchListItem {
|
||||
return {
|
||||
id: String(dto.id),
|
||||
dispatchNo: text(dto.dispatchNo),
|
||||
vehiclePlate: text(dto.vehiclePlate),
|
||||
driverName: text(dto.driverName),
|
||||
driverPhone: text(dto.driverPhone),
|
||||
carrierName: text(dto.carrierName),
|
||||
orderNos: text(dto.orderNos),
|
||||
route: text(dto.route),
|
||||
overseasWarehouse: text(dto.overseasWarehouseName || dto.overseasWarehouseId),
|
||||
departAt: text(dto.departAt),
|
||||
fbaAppointment: appointmentLabel(dto.fbaAppointmentNo, dto.fbaAppointmentStatus),
|
||||
vehicleAppointment: text(dto.vehicleAppointmentStatus),
|
||||
estimatedArrivalAt: text(dto.estimatedArrivalAt),
|
||||
status: statusLabel(dto.status),
|
||||
palletCount: number(dto.palletCount),
|
||||
packageCount: number(dto.packageCount),
|
||||
createdAt: text(dto.createdAt),
|
||||
}
|
||||
}
|
||||
|
||||
function dispatchError(error: unknown) {
|
||||
const details = getApiErrorDetails(error, '排车列表加载失败,请重试')
|
||||
return details.code === 'HTTP_404'
|
||||
? { ...details, message: '排车接口未启用或不存在,请确认海外尾程排车接口已发布' }
|
||||
: details
|
||||
}
|
||||
|
||||
async function load(model: QueryModel = filters.value) {
|
||||
Object.assign(filters.value, model)
|
||||
loading.value = true
|
||||
loadError.value = null
|
||||
try {
|
||||
const result = await listDispatches({
|
||||
page: page.value,
|
||||
size: pageSize.value,
|
||||
dispatchNo: String(filters.value.dispatchNo || '').trim() || undefined,
|
||||
vehiclePlate: String(filters.value.vehiclePlate || '').trim() || undefined,
|
||||
status: String(filters.value.status || '').trim() || undefined,
|
||||
fbaAppointmentNo: String(filters.value.fbaAppointmentNo || '').trim() || undefined,
|
||||
})
|
||||
rows.value = result.records.map(toListItem)
|
||||
total.value = result.total
|
||||
} catch (error) {
|
||||
rows.value = []
|
||||
total.value = 0
|
||||
loadError.value = dispatchError(error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function search(model: QueryModel) { page.value = 1; void load(model) }
|
||||
function reset(model: QueryModel) { page.value = 1; void load(model) }
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AdminPage><ListPageLayout aria-label="海外仓排车列表">
|
||||
<template #toolbar><QueryToolbar v-model="filters" class="surface-panel" :fields="fields" :loading="loading" ariaLabel="排车列表筛选" preference-key="overseas-dispatches-query" @search="search" @reset="reset" /></template>
|
||||
<template v-if="loadError" #state><LoadErrorState title="排车列表加载失败" :message="loadError.message" :loading="loading" @retry="load"><template #details><ApiErrorMeta :details="loadError" /></template></LoadErrorState></template>
|
||||
<section v-if="!loadError" class="surface-panel table-panel"><DataTable v-model:page="page" v-model:page-size="pageSize" :data="rows" :columns="columns" :loading="loading" :total="total" preference-key="overseas-dispatches" row-key="id" empty-text="暂无排车记录" @update:page="load()" @update:page-size="page = 1; load()">
|
||||
<template #status="{ row }"><StatusBadge :status="String((row as DispatchListItem).status || '—')" /></template>
|
||||
<template #pagination-left>共 {{ total }} 条排车记录</template>
|
||||
</DataTable></section>
|
||||
</ListPageLayout></AdminPage>
|
||||
</template>
|
||||
Reference in New Issue
Block a user