优化
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
||||
acceptPlatformFirstLegOrder,
|
||||
addPlatformFirstLegOrderNote,
|
||||
approvePlatformFirstLegOrder,
|
||||
batchApprovePlatformFirstLegOrders,
|
||||
cancelPlatformFirstLegOrder,
|
||||
completePlatformFirstLegOrder,
|
||||
getPlatformFirstLegOrder,
|
||||
@@ -96,6 +97,22 @@ describe('platform first-leg order API', () => {
|
||||
expect(http.post).toHaveBeenNthCalledWith(6, '/platform/v1/orders/first-leg/90001/internal-notes', { content: '已联系客户确认' }, { params: { tenantId: '10001' } })
|
||||
})
|
||||
|
||||
it('uses the formal tenant-scoped batch approval contract and preserves long order ids', async () => {
|
||||
vi.mocked(http.post).mockResolvedValue({
|
||||
code: '200', message: 'success',
|
||||
data: { total: 2, successCount: 1, failureCount: 1, successOrderIds: ['2089223263733518337'], failedOrders: [{ orderId: '2089223263733518338', code: 'A0409', message: '订单状态已变化' }] },
|
||||
})
|
||||
|
||||
await expect(batchApprovePlatformFirstLegOrders('2089176869176389634', [
|
||||
'2089223263733518337', '2089223263733518338',
|
||||
])).resolves.toMatchObject({ total: 2, successCount: 1, failureCount: 1 })
|
||||
expect(http.post).toHaveBeenCalledWith(
|
||||
'/platform/v1/orders/first-leg/batch-approve',
|
||||
{ orderIds: ['2089223263733518337', '2089223263733518338'] },
|
||||
{ params: { tenantId: '2089176869176389634' } },
|
||||
)
|
||||
})
|
||||
|
||||
it('writes the backend express contract as names, not a guessed master-data ID', async () => {
|
||||
vi.mocked(http.put).mockResolvedValue({ code: '200', message: 'success', data: null })
|
||||
|
||||
|
||||
@@ -115,6 +115,20 @@ export interface PlatformTenantOrderStatistics {
|
||||
asOf: string
|
||||
}
|
||||
|
||||
export interface PlatformOrderBatchReviewFailure {
|
||||
orderId: number | string
|
||||
code: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface PlatformOrderBatchReviewResult {
|
||||
total: number
|
||||
successCount: number
|
||||
failureCount: number
|
||||
successOrderIds: Array<number | string>
|
||||
failedOrders: PlatformOrderBatchReviewFailure[]
|
||||
}
|
||||
|
||||
type PlatformFirstLegOrderPagePayload = Omit<PlatformFirstLegOrderPageResource, 'page' | 'size' | 'total' | 'pages'> & {
|
||||
page: number | string
|
||||
size: number | string
|
||||
@@ -176,6 +190,14 @@ export function approvePlatformFirstLegOrder(tenantId: string, orderId: string)
|
||||
return postData<void>(`/platform/v1/orders/first-leg/${orderId}/approve`, undefined, { tenantId })
|
||||
}
|
||||
|
||||
export function batchApprovePlatformFirstLegOrders(tenantId: string, orderIds: string[]) {
|
||||
return postData<PlatformOrderBatchReviewResult>(
|
||||
'/platform/v1/orders/first-leg/batch-approve',
|
||||
{ orderIds },
|
||||
{ tenantId },
|
||||
)
|
||||
}
|
||||
|
||||
export function acceptPlatformFirstLegOrder(tenantId: string, orderId: string) {
|
||||
return postData<void>(`/platform/v1/orders/first-leg/${orderId}/accept`, undefined, { tenantId })
|
||||
}
|
||||
|
||||
@@ -34,6 +34,27 @@ describe('PlatformFirstLegOrdersPanel review queue', () => {
|
||||
expect(source).toContain('await nextTick()')
|
||||
})
|
||||
|
||||
it('supports tenant-aware batch approval with partial failure feedback', () => {
|
||||
expect(source).toContain("key: 'selection', type: 'selection'")
|
||||
expect(source).toContain("row.status === 'SUBMITTED' && !batchReviewLoading.value")
|
||||
expect(source).toContain('@selection-change="handleReviewSelectionChange"')
|
||||
expect(source).toContain('<template #toolbar-actions>')
|
||||
expect(source).toContain('批量审核通过')
|
||||
expect(source).toContain('groupOrdersByTenant(selected)')
|
||||
expect(source.indexOf('batchReviewLoading.value = true')).toBeLessThan(source.indexOf('await ElMessageBox.confirm('))
|
||||
expect(source).toContain('batchApprovePlatformFirstLegOrders(')
|
||||
expect(source).toContain('successCount += result.successCount')
|
||||
expect(source).toContain('failureCount += result.failureCount')
|
||||
expect(source).toContain('成功 ${successCount} 个,失败 ${failureCount} 个')
|
||||
})
|
||||
|
||||
it('keeps the operation column first before the batch selection column', () => {
|
||||
const actionColumn = source.indexOf("{ key: 'actions', label: '操作'")
|
||||
const selectionColumn = source.indexOf("key: 'selection', type: 'selection'")
|
||||
expect(actionColumn).toBeGreaterThan(0)
|
||||
expect(selectionColumn).toBeGreaterThan(actionColumn)
|
||||
})
|
||||
|
||||
it('shows up to three row actions directly and overflows only the fourth action onward', () => {
|
||||
expect(source).toContain('class="table-actions order-row-actions"')
|
||||
expect(source).toContain('directSecondaryActionCount')
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
acceptPlatformFirstLegOrder,
|
||||
addPlatformFirstLegOrderNote,
|
||||
approvePlatformFirstLegOrder,
|
||||
batchApprovePlatformFirstLegOrders,
|
||||
cancelPlatformFirstLegOrder,
|
||||
completePlatformFirstLegOrder,
|
||||
getPlatformFirstLegOrder,
|
||||
@@ -40,6 +41,8 @@ const page = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const loading = ref(false)
|
||||
const actionLoadingId = ref('')
|
||||
const batchReviewLoading = ref(false)
|
||||
const selectedReviewOrders = ref<PlatformFirstLegOrderResource[]>([])
|
||||
const loadError = ref<ApiErrorDetails | null>(null)
|
||||
const detailLoading = ref(false)
|
||||
const detail = ref<PlatformFirstLegOrderDetailResource | null>(null)
|
||||
@@ -96,8 +99,12 @@ const queryFields = computed<QueryField[]>(() => [
|
||||
{ key: 'dateRange', type: 'daterange', label: '订单创建日期', props: { 'aria-label': '订单创建日期' } },
|
||||
])
|
||||
|
||||
const orderColumns: DataTableColumn[] = [
|
||||
const orderColumns = computed<DataTableColumn[]>(() => [
|
||||
{ key: 'actions', label: '操作', minWidth: 288, fixed: 'left', slotName: 'actions' },
|
||||
...(can('platform:order:approve') ? [{
|
||||
key: 'selection', type: 'selection', width: 48, fixed: 'left',
|
||||
selectable: (row: PlatformFirstLegOrderResource) => row.status === 'SUBMITTED' && !batchReviewLoading.value,
|
||||
} satisfies DataTableColumn] : []),
|
||||
{ key: 'status', label: '状态', width: 108, slotName: 'status' },
|
||||
{ key: 'orderInfo', label: '订单信息', minWidth: 210, slotName: 'orderInfo' },
|
||||
{ key: 'customer', label: '客户', minWidth: 190, showOverflowTooltip: true, slotName: 'customer' },
|
||||
@@ -105,7 +112,7 @@ const orderColumns: DataTableColumn[] = [
|
||||
{ key: 'destination', label: '目的地', minWidth: 150, slotName: 'destination' },
|
||||
{ key: 'receivingWarehouse', label: '平台收货仓', minWidth: 180, slotName: 'receivingWarehouse' },
|
||||
{ key: 'express', label: '履约快递', minWidth: 160, slotName: 'express' },
|
||||
]
|
||||
])
|
||||
|
||||
const expressRules: FormRules = {
|
||||
expressCompany: [{ required: true, message: '请填写快递公司', trigger: 'blur' }],
|
||||
@@ -270,6 +277,7 @@ function actionLabel(order: PlatformFirstLegOrderResource) {
|
||||
|
||||
async function loadOrders() {
|
||||
loadError.value = null
|
||||
selectedReviewOrders.value = []
|
||||
loading.value = true
|
||||
try {
|
||||
const result = await listPlatformFirstLegOrders({
|
||||
@@ -292,6 +300,64 @@ async function loadOrders() {
|
||||
}
|
||||
}
|
||||
|
||||
function handleReviewSelectionChange(rows: PlatformFirstLegOrderResource[]) {
|
||||
selectedReviewOrders.value = rows.filter((row) => row.status === 'SUBMITTED')
|
||||
}
|
||||
|
||||
function groupOrdersByTenant(selected: PlatformFirstLegOrderResource[]) {
|
||||
const groups = new Map<string, PlatformFirstLegOrderResource[]>()
|
||||
for (const order of selected) {
|
||||
const tenantId = String(order.tenantId)
|
||||
groups.set(tenantId, [...(groups.get(tenantId) || []), order])
|
||||
}
|
||||
return groups
|
||||
}
|
||||
|
||||
async function batchApproveSelectedOrders() {
|
||||
const selected = [...selectedReviewOrders.value]
|
||||
if (!selected.length || batchReviewLoading.value) return
|
||||
const customerCount = new Set(selected.map((order) => String(order.tenantId))).size
|
||||
batchReviewLoading.value = true
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确认批量审核通过已勾选的 ${selected.length} 个订单吗?涉及 ${customerCount} 个客户,审核成功后订单将进入待测量。`,
|
||||
'批量审核通过',
|
||||
{ confirmButtonText: '确认批量审核', cancelButtonText: '取消', type: 'warning' },
|
||||
)
|
||||
let successCount = 0
|
||||
let failureCount = 0
|
||||
const failureMessages: string[] = []
|
||||
for (const [tenantId, tenantOrders] of groupOrdersByTenant(selected)) {
|
||||
try {
|
||||
const result = await batchApprovePlatformFirstLegOrders(
|
||||
tenantId,
|
||||
tenantOrders.map((order) => String(order.id)),
|
||||
)
|
||||
successCount += result.successCount
|
||||
failureCount += result.failureCount
|
||||
failureMessages.push(...result.failedOrders.map((failure) => failure.message))
|
||||
} catch (error) {
|
||||
failureCount += tenantOrders.length
|
||||
failureMessages.push(getApiErrorDetails(error, '该客户订单批量审核失败').message)
|
||||
}
|
||||
}
|
||||
await loadOrders()
|
||||
const reasons = [...new Set(failureMessages.filter(Boolean))].slice(0, 3).join(';')
|
||||
if (failureCount === 0) {
|
||||
ElMessage.success(`已批量审核通过 ${successCount} 个订单`)
|
||||
} else if (successCount > 0) {
|
||||
ElMessage.warning(`批量审核完成:成功 ${successCount} 个,失败 ${failureCount} 个${reasons ? `。${reasons}` : ''}`)
|
||||
} else {
|
||||
ElMessage.error(`批量审核失败:${failureCount} 个订单未通过${reasons ? `。${reasons}` : ''}`)
|
||||
}
|
||||
} catch (error) {
|
||||
if (error === 'cancel' || error === 'close') return
|
||||
ElMessage.error(getApiErrorDetails(error, '批量审核失败,请稍后重试').message)
|
||||
} finally {
|
||||
batchReviewLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function searchOrders() {
|
||||
if (page.value !== 1) {
|
||||
page.value = 1
|
||||
@@ -597,7 +663,17 @@ onMounted(() => void loadTenants())
|
||||
:page-sizes="[20, 50, 100]"
|
||||
empty-text="当前没有符合条件的头程订单"
|
||||
row-key="id"
|
||||
@selection-change="handleReviewSelectionChange"
|
||||
>
|
||||
<template #toolbar-actions>
|
||||
<el-button
|
||||
v-if="can('platform:order:approve')"
|
||||
type="success"
|
||||
:loading="batchReviewLoading"
|
||||
:disabled="loading || batchReviewLoading || selectedReviewOrders.length === 0"
|
||||
@click="batchApproveSelectedOrders"
|
||||
><CircleCheckBig :size="16" />批量审核通过<span v-if="selectedReviewOrders.length">({{ selectedReviewOrders.length }})</span></el-button>
|
||||
</template>
|
||||
<template #pagination-left="{ total: tableTotal }"><span>共 {{ tableTotal }} 条订单</span></template>
|
||||
<template #actions="{ row }">
|
||||
<div class="table-actions order-row-actions">
|
||||
|
||||
Reference in New Issue
Block a user