feat: complete phase 5 merchant delivery
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user