This commit is contained in:
2026-10-09 17:36:34 +08:00
parent 07ba9fcb7c
commit 9891ac3e29
8 changed files with 99 additions and 21 deletions
@@ -59,6 +59,8 @@ import {
summarizeFirstLegCargo,
validateFirstLegOrderDraft,
} from '@/modules/first-leg/utils/firstLegOrder'
import { ORDER_QUEUES, orderProgressLabel } from '../utils/orderProgress'
import type { OrderQueue } from '../api/unifiedOrders'
import { validateProductImageFile } from '@/modules/first-leg/utils/productImage'
import { createPriceEstimateBreakdown } from '@/modules/first-leg/utils/priceEstimate'
@@ -168,6 +170,7 @@ const fixedOrderColumns: DataTableColumn[] = [
]
const selectableOrderColumns: DataTableColumn[] = [
{ key: 'status', label: '当前订单状态', width: 126, align: 'center', headerAlign: 'center', slotName: 'status' },
{ key: 'progress', label: '履约进度', width: 142, align: 'center', headerAlign: 'center', slotName: 'progress' },
{ key: 'channel', label: '渠道', width: 150, align: 'left', headerAlign: 'left', showOverflowTooltip: true, slotName: 'channel' },
{ key: 'transportMode', label: '运输方式', width: 104, align: 'center', headerAlign: 'center', slotName: 'transportMode' },
{ key: 'pickupMethod', label: '交货方式', width: 112, align: 'center', headerAlign: 'center', slotName: 'pickupMethod' },
@@ -198,7 +201,7 @@ const selectableOrderColumns: DataTableColumn[] = [
{ key: 'updatedAt', label: '更新时间', width: 164, align: 'center', headerAlign: 'center', slotName: 'updatedAt' },
]
const allSelectableColumnKeys = selectableOrderColumns.map((column) => String(column.key))
const defaultVisibleColumnKeys = ['status', 'channel', 'transportMode', 'pickupMethod', 'receivingWarehouse', 'createdAt']
const defaultVisibleColumnKeys = ['status', 'progress', 'channel', 'transportMode', 'pickupMethod', 'receivingWarehouse', 'createdAt']
const visibleColumnKeys = ref<string[]>([...defaultVisibleColumnKeys])
const orderedColumnKeys = ref<string[]>([...allSelectableColumnKeys])
const draggedColumnKey = ref('')
@@ -243,7 +246,6 @@ const selectedOrders = ref<FirstLegOrder[]>([])
const selectedOrderCount = computed(() => selectedOrders.value.length)
const selectedDraftOrders = computed(() => selectedOrders.value.filter((order) => order.status === 'DRAFT'))
const orderTableVersion = ref(0)
type OrderQueue = 'all' | 'fee' | 'customs' | 'draft'
const activeOrderQueue = ref<OrderQueue>('all')
const editorOpen = ref(false)
const orderFormRef = ref<FormInstance>()
@@ -638,6 +640,7 @@ const emptyDescription = computed(() => {
if (activeOrderQueue.value === 'customs') return '暂无需要上传报关资料的订单'
if (activeOrderQueue.value === 'fee') return '暂无需要确认计费重的订单'
if (activeOrderQueue.value === 'draft') return '暂无草稿订单'
if (activeOrderQueue.value !== 'all') return `暂无${ORDER_QUEUES.find(q => q.key === activeOrderQueue.value)?.label ?? ''}的订单`
return '还没有订单'
})
@@ -1812,6 +1815,7 @@ function exportOrderInformation() {
displayListValue(order.destinationWarehouseName),
destinationTypeLabels[order.destinationType],
getFirstLegStatusLabel(order.status),
orderProgressLabel(store.orderProgress[order.id]),
formatListMetric(orderPackageCount(order)),
displayListValue(order.expressCompanyName),
displayListValue(order.trackingNumber),
@@ -1830,7 +1834,7 @@ function exportOrderInformation() {
formatExportDateTime(order.createdAt),
])
downloadCsv('订单信息.csv', [[
'入仓码', '渠道', '运输方式', '交货方式', '预约提货时间', '目的仓', '目的仓类型', '当前订单状态', '箱数', '快递公司', '快递单号', 'SKUS',
'入仓码', '渠道', '运输方式', '交货方式', '预约提货时间', '目的仓', '目的仓类型', '当前订单状态', '履约进度', '箱数', '快递公司', '快递单号', 'SKUS',
'总毛重', '总净重', '总体积', '总数量', '计费单价', '计费体积', '计费重量', '备注',
'货件编号', '跟踪编号', '国内仓入库时间', '创建时间',
], ...rows])
@@ -2106,10 +2110,12 @@ async function searchOrders(model: QueryModel) {
<DataTable :page="tablePage" :page-size="tablePageSize" @update:page="changeOrderPage" @update:page-size="changeOrderPageSize" :data="pagedOrders" :columns="visibleOrderColumns" :key="orderTableVersion" :loading="store.loading" :total="store.total" :page-sizes="[10, 20, 50]" row-key="id" empty-text="暂无订单" class="prototype-order-table" :row-actions="getOrderRowActions" @selection-change="handleOrderSelectionChange">
<template #toolbar>
<nav class="order-queue-tabs" aria-label="订单事项分类">
<button type="button" :class="{ active: activeOrderQueue === 'all' }" @click="selectOrderQueue('all')">全部</button>
<button type="button" :class="{ active: activeOrderQueue === 'fee' }" @click="selectOrderQueue('fee')">确认计费重<span v-if="orderQueueCounts.fee" class="queue-count">{{ orderQueueCounts.fee }}</span></button>
<button type="button" :class="{ active: activeOrderQueue === 'customs' }" @click="selectOrderQueue('customs')">上传资料<span v-if="orderQueueCounts.customs" class="queue-count">{{ orderQueueCounts.customs }}</span></button>
<button type="button" :class="{ active: activeOrderQueue === 'draft' }" @click="selectOrderQueue('draft')">草稿<span v-if="orderQueueCounts.draft" class="queue-count">{{ orderQueueCounts.draft }}</span></button>
<button v-for="queue in ORDER_QUEUES" :key="queue.key" type="button"
:class="{ active: activeOrderQueue === queue.key }" :aria-pressed="activeOrderQueue === queue.key"
@click="selectOrderQueue(queue.key)">
{{ queue.label }}<span v-if="queue.key !== 'all' && orderQueueCounts[queue.key]"
:class="queue.todo ? 'queue-count' : 'queue-stage-count'">{{ queue.todo ? orderQueueCounts[queue.key] : `(${orderQueueCounts[queue.key]})` }}</span>
</button>
</nav>
<span v-if="selectedOrderCount">已选 {{ selectedOrderCount }} 条</span>
</template>
@@ -2185,6 +2191,7 @@ async function searchOrders(model: QueryModel) {
<template #receivingWarehouse="{ row }"><span class="table-ellipsis" :title="displayListValue((row as FirstLegOrder).receivingWarehouseName)">{{ displayListValue((row as FirstLegOrder).receivingWarehouseName) }}</span></template>
<template #destinationWarehouse="{ row }"><span class="table-ellipsis" :title="displayListValue((row as FirstLegOrder).destinationWarehouseName)">{{ displayListValue((row as FirstLegOrder).destinationWarehouseName) }}</span></template>
<template #destinationType="{ row }">{{ destinationTypeLabels[(row as FirstLegOrder).destinationType] }}</template>
<template #progress="{ row }">{{ orderProgressLabel(store.orderProgress[row.id]) }}</template>
<template #status="{ row }"><span class="client-status" :data-tone="getFirstLegStatusTone((row as FirstLegOrder).status)">{{ getFirstLegStatusLabel((row as FirstLegOrder).status) }}</span></template>
<template #packageCount="{ row }">{{ formatListMetric(orderPackageCount(row as FirstLegOrder)) }}</template>
<template #expressCompany="{ row }"><span class="table-ellipsis" :title="displayListValue((row as FirstLegOrder).expressCompanyName)">{{ displayListValue((row as FirstLegOrder).expressCompanyName) }}</span></template>
@@ -2570,7 +2577,12 @@ async function searchOrders(model: QueryModel) {
.supporting-data-error span { overflow-wrap: anywhere; color: var(--muted); font-size: 11px; line-height: 1.55; }
.order-row-actions, .editor-actions { display: flex; justify-content: flex-end; gap: 8px; }
.order-table-card { padding: 0; overflow: hidden; border: 1px solid var(--client-border); border-radius: var(--client-radius); box-shadow: none; }
.order-queue-tabs { display: flex; align-items: center; gap: 4px; }
.prototype-order-table :deep(.data-table__toolbar) { flex-wrap: nowrap; }
.prototype-order-table :deep(.data-table__toolbar-heading) { min-width: 0; flex: 1; flex-wrap: nowrap; }
.prototype-order-table :deep(.data-table__toolbar-actions) { flex-shrink: 0; }
.order-queue-tabs { display: flex; min-width: 0; align-items: center; gap: 4px; overflow-x: auto; padding: 7px 0 10px; scrollbar-width: thin; }
.order-queue-tabs button { flex-shrink: 0; white-space: nowrap; }
.queue-stage-count { margin-left: 3px; }
.order-queue-tabs button { position: relative; min-height: 34px; padding: 0 12px; border: 1px solid transparent; border-radius: 7px; background: transparent; color: #667085; cursor: pointer; font: inherit; font-weight: 600; }
.order-queue-tabs button:hover { color: var(--client-action); }
.order-queue-tabs button.active { border-color: #cfe0ff; background: #f3f7ff; color: var(--client-action); }