2026-09-01 14:24:00 +08:00
< script setup lang = "ts" >
import { computed , onMounted , reactive , ref } from 'vue'
import { useRouter } from 'vue-router'
import { Plus , RefreshCw , Search } from '@lucide/vue'
import dayjs from 'dayjs'
import { useAuthStore } from '@/modules/auth/public'
import { listDeliveryOrders , type DeliveryQuery , type DeliveryRow } from '../api/deliveryOrders'
const router = useRouter ()
const auth = useAuthStore ()
const loading = ref ( false )
const errorMessage = ref ( '' )
const records = ref < DeliveryRow [] >([])
const total = ref ( 0 )
const page = ref ( 1 )
const pageSize = ref ( 20 )
const filters = reactive ({ documentNo : '' , dispatchNo : '' , waybillNo : '' , status : '' , deliveryMethod : '' , plannedRange : [] as string [] })
const canCreate = computed (() => auth . hasPermission ( 'merchant:warehouse:delivery:create' ))
const statusOptions = [
{ value : 'PENDING' , label : '待派送' },
{ value : 'IN_TRANSIT' , label : '派送中' },
{ value : 'DELIVERED' , label : '已送达' },
{ value : 'SIGNED' , label : '已签收' },
{ value : 'CANCELLED' , label : '已取消' },
]
const statusLabels : Record < string , string > = Object . fromEntries ( statusOptions . map (( item ) => [ item . value , item . label ]))
function display ( value : string | number | null | undefined ) {
return value === null || value === undefined || value === '' ? '—' : String ( value )
}
function formatDateTime ( value : string | null | undefined ) {
return value ? dayjs ( value ). format ( 'YYYY-MM-DD HH:mm' ) : '—'
}
function statusLabel ( status : string ) {
return statusLabels [ status ] ?? status
}
function statusType ( status : string ) {
if ( status === 'DELIVERED' || status === 'SIGNED' ) return 'success'
if ( status === 'CANCELLED' ) return 'info'
if ( status === 'IN_TRANSIT' ) return 'warning'
return 'primary'
}
function buildQuery () : DeliveryQuery {
return {
page : page . value ,
size : pageSize . value ,
documentNo : filters . documentNo . trim () || undefined ,
dispatchNo : filters . dispatchNo . trim () || undefined ,
waybillNo : filters . waybillNo . trim () || undefined ,
status : filters . status || undefined ,
deliveryMethod : filters . deliveryMethod || undefined ,
plannedFrom : filters . plannedRange [ 0 ] || undefined ,
plannedTo : filters . plannedRange [ 1 ] || undefined ,
}
}
async function loadOrders () {
loading . value = true
errorMessage . value = ''
try {
const result = await listDeliveryOrders ( buildQuery ())
records . value = result . records
total . value = result . total
} catch ( error ) {
records . value = []
total . value = 0
errorMessage . value = error instanceof Error ? error . message : '派送单加载失败,请重试'
} finally {
loading . value = false
}
}
function search () {
page . value = 1
void loadOrders ()
}
function reset () {
Object . assign ( filters , { documentNo : '' , dispatchNo : '' , waybillNo : '' , status : '' , deliveryMethod : '' , plannedRange : [] })
search ()
}
function changePage ( nextPage : number ) {
page . value = nextPage
void loadOrders ()
}
function changePageSize ( nextSize : number ) {
pageSize . value = nextSize
page . value = 1
void loadOrders ()
}
function openDetail ( row : DeliveryRow ) {
if ( ! row . deliveryNo ) return
void router . push ({ name : 'orders-delivery-detail' , params : { deliveryNo : row . deliveryNo } })
}
function openCreate () {
void router . push ({ name : 'orders-delivery-create' })
}
onMounted ( loadOrders )
</ script >
< template >
< section class = "delivery-page" aria -labelledby =" delivery -list-title " >
< div class = "delivery-heading" >
< div >< h2 id = "delivery-list-title" > 派送订单 </ h2 >< p > 查看本企业海外仓派送进度与签收结果 。 </ p ></ div >
< el-button v-if = "canCreate" type="primary" @click="openCreate"><Plus :size="16" /> 创建派送单 </ el-button >
</ div >
< form class = "delivery-card delivery-filter" @submit.prevent ="search" >
< div class = "delivery-filter-grid" >
< el-input v-model = "filters.documentNo" clearable placeholder="派送单号 / 出库单号" aria-label="派送单号或出库单号" />
< el-input v-model = "filters.dispatchNo" clearable placeholder="排车单号" aria-label="排车单号" />
< el-input v-model = "filters.waybillNo" clearable placeholder="运单号" aria-label="运单号" />
< el-select v-model = "filters.status" clearable placeholder="全部状态" aria-label="派送状态"><el-option v-for="option in statusOptions" :key="option.value" :label="option.label" :value="option.value" /></ el-select >
< el-select v-model = "filters.deliveryMethod" clearable placeholder="全部派送方式" aria-label="派送方式"><el-option label="卡车派送" value="TRUCK" /><el-option label="快递派送" value="COURIER" /><el-option label="客户自提" value="SELF_PICKUP" /></ el-select >
< el-date-picker v-model = "filters.plannedRange" type="datetimerange" value-format="YYYY-MM-DDTHH:mm:ss" start-placeholder="计划开始时间" end-placeholder="计划结束时间" range-separator="至" aria-label="计划派送时间" />
</ div >
< div class = "delivery-filter-actions" >< el-button type = "primary" native -type =" submit " >< Search :size = "16" /> 查询 </ el-button >< el-button @click ="reset" >< RefreshCw :size = "16" /> 重置 </ el-button ></ div >
</ form >
< div class = "delivery-card delivery-table-card" >
< div v-if = "errorMessage" class="delivery-state delivery-state--error" role="alert"><p>{{ errorMessage }}</p><el-button @click="loadOrders" > 重试 </ el -button ></ div >
< el-table v-else v-loading = "loading" :data="records" empty-text="暂无派送单" table-layout="fixed" >
< el-table-column label = "操作" width = "96" fixed = "left" >< template # default = "{ row }" >< el-button link type = "primary" :disabled = "!row.deliveryNo" @click ="openDetail(row as DeliveryRow)" > 详情 </ el -button ></ template ></ el-table-column >
< el-table-column prop = "deliveryNo" label = "派送单号" min -width =" 180 " >< template # default = "{ row }" >{{ display ( row . deliveryNo ) }}</ template ></ el-table-column >
< el-table-column prop = "dispatchNo" label = "排车单号" min -width =" 170 " >< template # default = "{ row }" >{{ display ( row . dispatchNo ) }}</ template ></ el-table-column >
< el-table-column prop = "outboundNo" label = "出库单号" min -width =" 170 " >< template # default = "{ row }" >{{ display ( row . outboundNo ) }}</ template ></ el-table-column >
< el-table-column prop = "status" label = "状态" width = "110" >< template # default = "{ row }" >< el-tag :type = "statusType(row.status)" effect = "light" >{{ statusLabel ( row . status ) }}</ el-tag ></ template ></ el-table-column >
< el-table-column prop = "overseasWarehouseName" label = "海外仓" min -width =" 150 " >< template # default = "{ row }" >{{ display ( row . overseasWarehouseName ) }}</ template ></ el-table-column >
< el-table-column prop = "deliveryMethod" label = "派送方式" width = "120" >< template # default = "{ row }" >{{ display ( row . deliveryMethod ) }}</ template ></ el-table-column >
< el-table-column prop = "waybillNo" label = "运单号" min -width =" 150 " >< template # default = "{ row }" >{{ display ( row . waybillNo ) }}</ template ></ el-table-column >
< el-table-column prop = "carrierName" label = "承运商" min -width =" 140 " >< template # default = "{ row }" >{{ display ( row . carrierName ) }}</ template ></ el-table-column >
< el-table-column prop = "plannedDeliveryAt" label = "计划派送" min -width =" 160 " >< template # default = "{ row }" >{{ formatDateTime ( row . plannedDeliveryAt ) }}</ template ></ el-table-column >
< el-table-column prop = "expectedArrivalAt" label = "预计到达" min -width =" 160 " >< template # default = "{ row }" >{{ formatDateTime ( row . expectedArrivalAt ) }}</ template ></ el-table-column >
< el-table-column prop = "createdAt" label = "创建时间" min -width =" 160 " >< template # default = "{ row }" >{{ formatDateTime ( row . createdAt ) }}</ template ></ el-table-column >
</ el-table >
< div v-if = "!errorMessage && (records.length > 0 || total > 0)" class="delivery-pagination"><span>共 {{ total }} 条</span><el-pagination :current-page="page" :page-size="pageSize" :page-sizes="[10, 20, 50, 100]" layout="sizes, prev, pager, next" :total="total" @update:current-page="changePage" @update:page-size="changePageSize" /></ div >
</ div >
</ section >
</ template >
< style scoped src = "./deliveryOrders.css" ></ style >