This commit is contained in:
2026-10-20 11:33:39 +08:00
parent d2f2ecf57a
commit 41a498657c
33 changed files with 909 additions and 88 deletions
@@ -11,6 +11,9 @@ import {
getPlatformUnifiedOrderBatchApprovalPreview,
updatePlatformUnifiedOrderRemarks,
listPlatformUnifiedOrders,
getPlatformUnifiedOrderStatusCounts,
type UnifiedOrderStatusCountQuery,
type UnifiedOrderStatusCounts,
updatePlatformUnifiedOrderPlanning,
unifiedOrderStatusOptions,
type UnifiedOrderDetailResource,
@@ -121,6 +124,10 @@ const page = ref(1)
const pageSize = ref(20)
const total = ref(0)
const loading = ref(false)
const statusCounts = ref<UnifiedOrderStatusCounts['counts']>({})
const statusCountsLoading = ref(false)
const statusCountsError = ref<ApiErrorDetails | null>(null)
let statusCountsRequestSerial = 0
const loadError = ref<ApiErrorDetails | null>(null)
const legacyImportOpen = ref(false)
const legacyImportFile = ref<File | null>(null)
@@ -714,11 +721,16 @@ function optionalBoolean(value: string | null | undefined) {
function selectStatus(status: UnifiedOrderMainStatus | '' | 'MEASUREMENT_REVIEW') {
if (selectedTab.value === status) return
const leavingMeasurementReview = measurementReviewSelected.value
measurementReviewSelected.value = status === 'MEASUREMENT_REVIEW'
if (measurementReviewSelected.value) selectedOrders.value = []
if (status === 'MEASUREMENT_REVIEW') return
if (status === 'MEASUREMENT_REVIEW') {
void loadStatusCounts()
return
}
selectedStatus.value = status
if (page.value === 1) void loadOrders()
if (leavingMeasurementReview) void loadStatusCounts()
if (page.value === 1) void loadOrders(false)
else page.value = 1
}
@@ -786,6 +798,7 @@ async function savePlanning(row: UnifiedOrderResource) {
row.estimatedVolumeCbm = estimatedVolumeCbm
draft.savedExpectedArrivalAt = draft.expectedArrivalAt
draft.savedEstimatedVolumeCbm = estimatedVolumeCbm
void loadStatusCounts()
ElMessage.success('订单运营预估已更新')
} catch (error) {
const details = getApiErrorDetails(error, '订单运营预估保存失败')
@@ -821,6 +834,7 @@ const canEditDeclaration = computed(() => Boolean(
))
async function handleDeclarationUpdated() {
void loadStatusCounts()
activeDetailTab.value = 'basic'
await loadDetail(true)
}
@@ -1089,6 +1103,7 @@ async function updateMeasurementCalculationMode(row: Record<string, unknown> | n
measurementSectionModes.outer = mode
measurementLoadedKey.value = ''
ElMessage.success(`${row ? '当前数据' : '全部测量数据'}已保存并绑定计算规则,体积已重新计算`)
void loadStatusCounts()
return true
} catch (error) {
measurementLoadedKey.value = ''
@@ -1141,6 +1156,7 @@ async function saveWarehouseOuterDimensions(row: Record<string, unknown>) {
if (refreshed) measurementDetail.value = refreshed
measurementLoadedKey.value = ''
ElMessage.success(`序号 ${row.itemNo || '—'} 的外箱规已更新,相关数值已重新计算`)
void loadStatusCounts()
} catch (error) {
measurementLoadedKey.value = ''
await loadMeasurementDetails()
@@ -1583,7 +1599,50 @@ async function loadWarehouses() {
}
}
async function loadOrders() {
function orderCountFilters(): UnifiedOrderStatusCountQuery {
const activeFilters = appliedFilters.value
const range = activeFilters.createdRange
return {
tenantId: activeFilters.tenantId || undefined,
receivingWarehouseId: activeFilters.receivingWarehouseId || undefined,
destinationWarehouseId: activeFilters.destinationWarehouseId || undefined,
inboundCode: trimmed(activeFilters.inboundCode),
expectedArrivalDate: activeFilters.expectedArrivalDate || undefined,
feeReviewStatus: activeFilters.feeReviewStatus || undefined,
channelName: trimmed(activeFilters.channelName),
containerNo: trimmed(activeFilters.containerNo),
cabinetNo: trimmed(activeFilters.cabinetNo),
sailingDate: activeFilters.sailingDate || undefined,
loaded: optionalBoolean(activeFilters.loaded),
fullContainer: optionalBoolean(activeFilters.fullContainer),
measurementReviewStatus: activeFilters.measurementReviewStatus || undefined,
fbaCode: trimmed(activeFilters.fbaCode),
createdFrom: range.length === 2 ? `${range[0]}T00:00:00` : undefined,
createdTo: range.length === 2 ? `${range[1]}T23:59:59` : undefined,
}
}
async function loadStatusCounts() {
const serial = ++statusCountsRequestSerial
statusCounts.value = {}
statusCountsLoading.value = true
statusCountsError.value = null
try {
const result = await getPlatformUnifiedOrderStatusCounts(orderCountFilters())
if (serial === statusCountsRequestSerial) statusCounts.value = result.counts
} catch (error) {
if (serial === statusCountsRequestSerial) statusCountsError.value = getApiErrorDetails(error, '订单页签数量加载失败')
} finally {
if (serial === statusCountsRequestSerial) statusCountsLoading.value = false
}
}
function tabCount(value: UnifiedOrderMainStatus | '' | 'MEASUREMENT_REVIEW') {
return value ? statusCounts.value[value] : undefined
}
async function loadOrders(refreshCounts = true) {
if (refreshCounts) void loadStatusCounts()
if (measurementReviewSelected.value) return
selectedApprovalOrders.value = []
selectedOrders.value = []
@@ -1592,26 +1651,9 @@ async function loadOrders() {
coLoadError.value = null
Object.keys(planningDrafts).forEach((key) => delete planningDrafts[key])
try {
const activeFilters = appliedFilters.value
const range = activeFilters.createdRange
const result = await listPlatformUnifiedOrders({
tenantId: activeFilters.tenantId || undefined,
...orderCountFilters(),
mainStatus: selectedStatus.value || undefined,
receivingWarehouseId: activeFilters.receivingWarehouseId || undefined,
destinationWarehouseId: activeFilters.destinationWarehouseId || undefined,
inboundCode: trimmed(activeFilters.inboundCode),
expectedArrivalDate: activeFilters.expectedArrivalDate || undefined,
feeReviewStatus: activeFilters.feeReviewStatus || undefined,
channelName: trimmed(activeFilters.channelName),
containerNo: trimmed(activeFilters.containerNo),
cabinetNo: trimmed(activeFilters.cabinetNo),
sailingDate: activeFilters.sailingDate || undefined,
loaded: optionalBoolean(activeFilters.loaded),
fullContainer: optionalBoolean(activeFilters.fullContainer),
measurementReviewStatus: activeFilters.measurementReviewStatus || undefined,
fbaCode: trimmed(activeFilters.fbaCode),
createdFrom: range.length === 2 ? `${range[0]}T00:00:00` : undefined,
createdTo: range.length === 2 ? `${range[1]}T23:59:59` : undefined,
page: page.value,
size: pageSize.value,
})
@@ -1628,17 +1670,19 @@ async function loadOrders() {
function search() {
appliedFilters.value = cloneFilters(filters.value)
void loadStatusCounts()
if (page.value !== 1) {
page.value = 1
return
}
void loadOrders()
void loadOrders(false)
}
function reset() {
filters.value = emptyFilters()
appliedFilters.value = emptyFilters()
if (page.value === 1) void loadOrders()
void loadStatusCounts()
if (page.value === 1) void loadOrders(false)
else page.value = 1
}
@@ -1806,6 +1850,7 @@ async function executeConfirmAndSendFee() {
automaticFee.value = { feeStatus: snapshot.feeStatus, totalAmount: Number(snapshot.totalAmount || 0) }
feePanelRevision.value += 1
ElMessage.success('计费量和单价已确认并发送给客户')
void loadStatusCounts()
feeConfirmOpen.value = false
feeConfirmAttempt = null
await loadDetail(true)
@@ -2298,7 +2343,7 @@ async function loadDetail(force = false) {
}
}
watch([page, pageSize], () => { void loadOrders() })
watch([page, pageSize], () => { void loadOrders(false) })
watch(businessInboundCode, (value) => {
detailAssociationRequestSerial += 1
resolvedDetailTarget.value = null
@@ -2321,12 +2366,16 @@ watch(detailRouteKey, (value) => {
watch(measurementPreviewOpen, (open) => {
if (!open) releaseMeasurementPreview()
})
watch(smartLoadingOpen, (open, wasOpen) => {
if (wasOpen && !open) void loadOrders()
})
onMounted(() => {
void loadCustomers()
void loadWarehouses()
if (!isDetailRoute.value) void loadOrders()
})
onBeforeUnmount(() => {
statusCountsRequestSerial += 1
detailAssociationRequestSerial += 1
measurementRequestSerial += 1
holdRequestSerial += 1
@@ -2506,7 +2555,7 @@ onBeforeUnmount(() => {
:inbound-id="normalizedInboundId"
:destination-type="detail.summary.destinationType"
@summary="otherFeeSummary = $event"
@changed="loadDetail(true)"
@changed="loadDetail(true); loadStatusCounts()"
/>
</template>
</template>
@@ -2703,9 +2752,9 @@ onBeforeUnmount(() => {
<template v-else>
<div v-if="measurementReviewSelected" id="unified-orders-results" role="tabpanel">
<InboundTaskList mode="review" title="订单列表">
<InboundTaskList mode="review" title="订单列表" @changed="loadStatusCounts">
<template #status-tabs>
<div ref="statusTabList" class="status-tabs density-status-tabs" role="tablist" aria-label="订单主状态">
<div ref="statusTabList" class="status-tabs density-status-tabs" role="tablist" aria-label="订单主状态" :aria-busy="statusCountsLoading">
<button
v-for="(tab, index) in statusTabs"
:key="tab.value || 'ALL'"
@@ -2717,7 +2766,13 @@ onBeforeUnmount(() => {
:class="['status-tab', { 'is-active': selectedTab === tab.value }]"
@click="selectStatus(tab.value)"
@keydown="handleTabKeydown($event, index)"
>{{ tab.label }}</button>
><span>{{ tab.label }}</span><span v-if="tabCount(tab.value) !== undefined" class="order-status-count">({{ tabCount(tab.value) }})</span></button>
</div>
<div v-if="statusCountsLoading" class="order-status-feedback" role="status">页签数量加载中…</div>
<div v-else-if="statusCountsError" class="order-status-feedback" role="status">
<span>{{ statusCountsError.message }}</span>
<el-button link type="primary" @click="loadStatusCounts">重试数量</el-button>
<ApiErrorMeta :details="statusCountsError" />
</div>
</template>
</InboundTaskList>
@@ -2732,7 +2787,7 @@ onBeforeUnmount(() => {
<template #toolbar-start><h1>订单列表</h1></template>
<template #filters>
<div class="orders-query-panel integrated-query-group">
<div ref="statusTabList" class="status-tabs density-status-tabs" role="tablist" aria-label="订单主状态">
<div ref="statusTabList" class="status-tabs density-status-tabs" role="tablist" aria-label="订单主状态" :aria-busy="statusCountsLoading">
<button
v-for="(tab, index) in statusTabs"
:key="tab.value || 'ALL'"
@@ -2744,9 +2799,15 @@ onBeforeUnmount(() => {
:class="['status-tab', { 'is-active': selectedTab === tab.value }]"
@click="selectStatus(tab.value)"
@keydown="handleTabKeydown($event, index)"
>{{ tab.label }}</button>
><span>{{ tab.label }}</span><span v-if="tabCount(tab.value) !== undefined" class="order-status-count">({{ tabCount(tab.value) }})</span></button>
</div>
<div v-if="statusCountsLoading" class="order-status-feedback" role="status">页签数量加载中…</div>
<div v-else-if="statusCountsError" class="order-status-feedback" role="status">
<span>{{ statusCountsError.message }}</span>
<el-button link type="primary" @click="loadStatusCounts">重试数量</el-button>
<ApiErrorMeta :details="statusCountsError" />
</div>
<QueryToolbar id="unified-orders-advanced-filters" v-model="filters" class="orders-query-toolbar" ariaLabel="订单列表筛选" :fields="queryFields" :available-fields="allQueryFields" preference-key="unified-orders-query-v2" :search-on-reset="false" @search="search" @reset="reset">
<template #actions>
<el-button :aria-expanded="showAdvancedFilters" aria-controls="unified-orders-advanced-filters" @click="showAdvancedFilters = !showAdvancedFilters">