feat(client): optimize first-leg order actions

This commit is contained in:
2026-09-14 17:18:56 +08:00
parent cd057845bf
commit c2611a1bfd
5 changed files with 69 additions and 16 deletions
@@ -27,6 +27,7 @@ import {
getMerchantOrderAddresses,
getMerchantPricingChannels,
importMerchantFirstLegOrders,
submitMerchantFirstLegOrder,
updateMerchantOrderAddress,
uploadMerchantProductImage,
type CustomerOrderAddress,
@@ -72,6 +73,7 @@ const companyAuditLoaded = ref(false)
const companySubmissionBlocked = computed(() => companyAuditLoaded.value && tenantStore.company.auditStatus !== 'ACTIVE')
const canCreateOrder = computed(() => auth.hasPermission('order:first-leg:create'))
const canEditOrder = computed(() => auth.hasPermission('order:first-leg:edit'))
const canSubmitOrder = computed(() => auth.hasPermission('order:first-leg:submit'))
const canConfirmInboundFee = computed(() => auth.hasPermission('order:first-leg:edit'))
const availableWarehouses = computed(() => store.warehouses.filter((warehouse) => warehouse.warehouseType === 'DOMESTIC'))
const availableDestinationWarehouses = computed(() => store.warehouses.filter((warehouse) => warehouse.warehouseType === form.destinationType))
@@ -182,8 +184,10 @@ const tablePage = ref(1)
const tablePageSize = ref(10)
const downloadingMarks = ref(false)
const exportingOrders = ref(false)
const batchSubmitting = ref(false)
const selectedOrders = ref<FirstLegOrder[]>([])
const selectedOrderCount = computed(() => selectedOrders.value.length)
const selectedDraftOrders = computed(() => selectedOrders.value.filter((order) => order.status === 'DRAFT'))
const orderTableVersion = ref(0)
const editorOpen = ref(false)
const orderFormRef = ref<FormInstance>()
@@ -1485,6 +1489,46 @@ function handleOrderSelectionChange(rows: FirstLegOrder[]) {
selectedOrders.value = rows
}
async function submitSelectedDraftOrders() {
const drafts = selectedDraftOrders.value
if (batchSubmitting.value || !drafts.length) return
if (companySubmissionBlocked.value) {
ElMessage.warning('企业资料审核通过后才能提交订单')
return
}
try {
await ElMessageBox.confirm(`确认提交选中的 ${drafts.length} 条草稿订单吗?`, '批量提交订单', {
type: 'warning',
confirmButtonText: '确认提交',
cancelButtonText: '取消',
})
} catch (error) {
if (error === 'cancel' || error === 'close') return
throw error
}
batchSubmitting.value = true
try {
const results = await Promise.allSettled(drafts.map((order) => submitMerchantFirstLegOrder(
order.id,
createIdempotencyKey(`first-leg-batch-submit-${order.id}`),
)))
const successCount = results.filter((result) => result.status === 'fulfilled').length
const failures = results.filter((result): result is PromiseRejectedResult => result.status === 'rejected')
await searchOrders(appliedFilters.value)
if (!failures.length) {
ElMessage.success(`已提交 ${successCount} 条草稿订单`)
return
}
const firstReason = failures[0]?.reason
const reason = firstReason instanceof Error ? firstReason.message : '提交失败'
if (successCount > 0) ElMessage.warning(`${successCount} 条提交成功,${failures.length} 条失败:${reason}`)
else ElMessage.error(`${failures.length} 条草稿订单提交失败:${reason}`)
} finally {
batchSubmitting.value = false
}
}
async function downloadShippingMarks() {
if (downloadingMarks.value) return
const orders = selectedOrders.value
@@ -1816,12 +1860,7 @@ async function searchOrders(model: QueryModel) {
<template>
<section class="first-leg-page" :class="{ 'is-create-route': isCreatePage }">
<template v-if="!isCreatePage">
<QueryToolbar v-model="filters" class="first-leg-query-toolbar" :fields="queryFields" ariaLabel="订单查询" @search="searchOrders">
<template #actions>
<el-button class="query-export-button" type="primary" :loading="downloadingMarks" :disabled="selectedOrderCount === 0" @click="downloadShippingMarks"><Download :size="16" />下载唛头</el-button>
<el-button class="query-export-button" type="primary" :loading="exportingOrders" :disabled="selectedOrderCount === 0" @click="exportOrderInformation"><Download :size="16" />导出订单信息</el-button>
</template>
</QueryToolbar>
<QueryToolbar v-model="filters" class="first-leg-query-toolbar" :fields="queryFields" ariaLabel="订单查询" @search="searchOrders" />
<div class="client-card order-table-card">
<div class="order-table-toolbar">
@@ -1853,6 +1892,9 @@ async function searchOrders(model: QueryModel) {
<el-button link class="column-settings-reset" @click="resetOrderColumns"><RotateCcw :size="17" />恢复默认</el-button>
</div>
</el-popover>
<el-button :loading="downloadingMarks" :disabled="selectedOrderCount === 0" @click="downloadShippingMarks"><Download :size="16" />下载唛头</el-button>
<el-button :loading="exportingOrders" :disabled="selectedOrderCount === 0" @click="exportOrderInformation"><Download :size="16" />导出订单信息</el-button>
<el-button v-if="canSubmitOrder" :loading="batchSubmitting" :disabled="selectedDraftOrders.length === 0 || companySubmissionBlocked" @click="submitSelectedDraftOrders"><Send :size="17" />批量提交草稿</el-button>
<el-button v-if="auth.hasPermission('order:first-leg:create')" @click="openBatchImport"><Upload :size="17" />批量导入订单</el-button>
<el-button v-if="auth.hasPermission('order:first-leg:create')" type="primary" :loading="pageDataLoading" :disabled="pageDataLoading || availableWarehouses.length === 0" @click="openCreate"><FilePlus2 :size="17" />创建订单</el-button>
</div>
@@ -1878,7 +1920,7 @@ async function searchOrders(model: QueryModel) {
<template #actions="{ row }">
<div class="order-row-actions">
<el-button link type="primary" @click="openDetail(row as FirstLegOrder)"><Eye :size="15" />详情</el-button>
<el-button link type="primary" @click="openFeeConfirmation(row as FirstLegOrder)">确认计费重</el-button>
<el-button v-if="canConfirmInboundFee && (row as FirstLegOrder).businessSummary.customerFeeConfirmationRequired" link type="primary" @click="openFeeConfirmation(row as FirstLegOrder)">确认计费重</el-button>
<el-button v-if="canEditOrder && canClientEditFirstLegOrder((row as FirstLegOrder).status)" link type="primary" :loading="openingEditor" @click="openEdit(row as FirstLegOrder)"><Pencil :size="15" />编辑</el-button>
<el-button v-if="eligibleCustomsDetail(row as FirstLegOrder)" link type="primary" @click="openCustomsDocuments(row as FirstLegOrder)"><FileText :size="15" />报关资料</el-button>
</div>
@@ -1944,7 +1986,7 @@ async function searchOrders(model: QueryModel) {
<span>更新于 {{ formatDateTime(order.updatedAt) }}</span>
<div>
<el-button link type="primary" @click="openDetail(order)"><Eye :size="15" />详情</el-button>
<el-button link type="primary" @click="openFeeConfirmation(order)">确认计费重</el-button>
<el-button v-if="canConfirmInboundFee && order.businessSummary.customerFeeConfirmationRequired" link type="primary" @click="openFeeConfirmation(order)">确认计费重</el-button>
<el-button v-if="canEditOrder && canClientEditFirstLegOrder(order.status)" link type="primary" :loading="openingEditor" @click="openEdit(order)"><Pencil :size="15" />编辑</el-button>
<el-button v-if="eligibleCustomsDetail(order)" link type="primary" @click="openCustomsDocuments(order)"><FileText :size="15" />报关资料</el-button>
</div>