优化
This commit is contained in:
@@ -4,6 +4,7 @@ import { listPlatformUnifiedOrders, updatePlatformUnifiedOrderPlanning } from '@
|
||||
import { createPlatformExportTask } from '@/modules/downloads'
|
||||
import { showExportTaskCreatedDialog } from '@/shared/components/exportTaskCreatedDialog'
|
||||
import { ApiResponseError } from '@/shared/api/http'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import UnifiedOrdersView from './UnifiedOrdersView.vue'
|
||||
|
||||
vi.mock('vue-router', () => ({
|
||||
@@ -192,7 +193,8 @@ describe('UnifiedOrdersView list request coordination', () => {
|
||||
}))
|
||||
})
|
||||
|
||||
it('edits arrival and estimated volume with the row version and preserves evidence on failure', async () => {
|
||||
it('edits arrival and estimated volume and reports compact failure evidence through a message', async () => {
|
||||
const errorMessage = vi.spyOn(ElMessage, 'error').mockImplementation(() => undefined as never)
|
||||
vi.mocked(listPlatformUnifiedOrders).mockResolvedValue({
|
||||
records: [{
|
||||
orderId: '2089223263733518337', orderVersion: 7, tenantId: '2089176869176389634',
|
||||
@@ -225,7 +227,11 @@ describe('UnifiedOrdersView list request coordination', () => {
|
||||
expectedVersion: 8, expectedArrivalAt: '2026-09-12T16:00:00', estimatedVolumeCbm: 0.5,
|
||||
})
|
||||
expect(wrapper.getComponent({ name: 'ElDatePicker' }).props('modelValue')).toBe('2026-09-12T16:00:00')
|
||||
expect(wrapper.text()).toContain('订单版本已变化')
|
||||
expect(wrapper.text()).toContain('A0409 req-planning trace-planning')
|
||||
expect(wrapper.text()).not.toContain('订单版本已变化')
|
||||
expect(wrapper.find('.planning-error').exists()).toBe(false)
|
||||
expect(errorMessage).toHaveBeenCalledWith(expect.objectContaining({
|
||||
message: '订单版本已变化(错误码 A0409;Request ID req-planning;Trace ID trace-planning)',
|
||||
showClose: true,
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -138,7 +138,9 @@ describe('UnifiedOrdersView contract', () => {
|
||||
expect(source).toContain("can('platform:order:declaration:edit')")
|
||||
expect(source).toContain('updatePlatformUnifiedOrderPlanning(String(row.tenantId), String(row.orderId)')
|
||||
expect(source).toContain('expectedVersion: row.orderVersion!')
|
||||
expect(source).toContain('<ApiErrorMeta :details="planningDraft(row as UnifiedOrderResource).error!" />')
|
||||
expect(source).toContain('ElMessage.error({')
|
||||
expect(source).toContain('Request ID ${details.requestId}')
|
||||
expect(source).not.toContain('class="planning-error"')
|
||||
expect(columnSource).toContain("key: 'effectiveSailingAt', prop: 'effectiveSailingAt', label: '开船时间', minWidth: 180, slotName: 'field' }")
|
||||
expect(columnSource).toContain("key: 'effectivePortArrivalAt', prop: 'effectivePortArrivalAt', label: '到港时间', minWidth: 180, slotName: 'field' }")
|
||||
expect(source).toContain('preference-key="unified-orders-table-v2"')
|
||||
@@ -429,7 +431,7 @@ describe('UnifiedOrdersView contract', () => {
|
||||
|
||||
it('clears an invalidated measurement loading state before refreshing order details', () => {
|
||||
const detailLoaderSource = source.slice(source.indexOf('async function loadDetail(force = false)'), source.indexOf('watch([page, pageSize]'))
|
||||
expect(detailLoaderSource).toContain('measurementRequestSerial += 1\n measurementLoading.value = false')
|
||||
expect(detailLoaderSource).toMatch(/measurementRequestSerial \+= 1\r?\n\s+measurementLoading\.value = false/)
|
||||
})
|
||||
|
||||
it('restores the saved outer-carton rule and server-calculated dimensions on re-entry', () => {
|
||||
@@ -538,12 +540,11 @@ describe('UnifiedOrdersView contract', () => {
|
||||
expect(feeSection).not.toContain('String(detail.summary.inboundId)')
|
||||
})
|
||||
|
||||
it('mounts downstream progress inside the existing order detail without opening unsafe writes', () => {
|
||||
expect(source).toContain('UnifiedOrderWorkflowPanel')
|
||||
expect(source).toContain("{ key: 'workflow', label: '后半程作业', icon: OrderFulfillmentIcon }")
|
||||
expect(source).toContain(':order-id="String(detail.summary.orderId)"')
|
||||
expect(source).not.toContain(':show-delivery=')
|
||||
expect(source).toContain("detail.value?.summary.inboundId")
|
||||
it('keeps downstream work out of each order detail after the order is linked to a container', () => {
|
||||
expect(source).not.toContain('UnifiedOrderWorkflowPanel')
|
||||
expect(source).not.toContain("key: 'workflow'")
|
||||
expect(source).not.toContain('后半程作业')
|
||||
expect(source).not.toContain("activeDetailTab === 'workflow'")
|
||||
expect(routeSource).not.toContain('unified-order-workflow')
|
||||
})
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@ import {
|
||||
firstLegTradeTermLabel,
|
||||
firstLegTransportModeLabel,
|
||||
} from '@/shared/utils/firstLegBusinessLabels'
|
||||
import { UnifiedOrderWorkflowPanel } from '@/modules/unified-orders/workflows'
|
||||
import { SmartLoadingDialog } from '@/modules/container-loading'
|
||||
import {
|
||||
unifiedOrderHistoryActionLabel,
|
||||
@@ -117,7 +116,6 @@ type PlanningDraft = {
|
||||
savedExpectedArrivalAt: string
|
||||
savedEstimatedVolumeCbm: number | null
|
||||
saving: boolean
|
||||
error: ApiErrorDetails | null
|
||||
}
|
||||
const planningDrafts = reactive<Record<string, PlanningDraft | undefined>>({})
|
||||
const customerLoading = ref(false)
|
||||
@@ -255,10 +253,6 @@ const detailTabs = [
|
||||
{ key: 'fulfillment', label: '拆分履约', icon: OrderFulfillmentIcon },
|
||||
]
|
||||
|
||||
const detailTabOptions = computed(() => detail.value?.summary.inboundId
|
||||
? [...detailTabs, { key: 'workflow', label: '后半程作业', icon: OrderFulfillmentIcon }]
|
||||
: detailTabs)
|
||||
|
||||
const customerMeasurementColumns = [
|
||||
{ key: 'itemNo', label: '#' }, { key: 'lengthCm', label: '箱规长(cm)' }, { key: 'widthCm', label: '箱规宽(cm)' },
|
||||
{ key: 'heightCm', label: '箱规高(cm)' }, { key: 'packageCount', label: '箱数' }, { key: 'unitWeightKg', label: '每箱毛重' },
|
||||
@@ -634,7 +628,6 @@ function planningDraft(row: UnifiedOrderResource) {
|
||||
savedExpectedArrivalAt: row.expectedArrivalAt || '',
|
||||
savedEstimatedVolumeCbm: row.estimatedVolumeCbm ?? null,
|
||||
saving: false,
|
||||
error: null,
|
||||
}
|
||||
planningDrafts[key] = draft
|
||||
}
|
||||
@@ -647,18 +640,17 @@ async function savePlanning(row: UnifiedOrderResource) {
|
||||
const estimatedVolumeCbm = draft.estimatedVolumeCbm ?? null
|
||||
draft.estimatedVolumeCbm = estimatedVolumeCbm
|
||||
if (!Number.isInteger(row.orderVersion)) {
|
||||
draft.error = { message: '订单版本缺失,请刷新列表后重试' }
|
||||
ElMessage.error('订单版本缺失,请刷新列表后重试')
|
||||
return
|
||||
}
|
||||
if (estimatedVolumeCbm !== null && (!Number.isFinite(estimatedVolumeCbm) || estimatedVolumeCbm <= 0)) {
|
||||
draft.error = { message: '预估体积必须大于 0,或清空该值' }
|
||||
ElMessage.warning('预估体积必须大于 0,或清空该值')
|
||||
return
|
||||
}
|
||||
if (draft.expectedArrivalAt === draft.savedExpectedArrivalAt
|
||||
&& estimatedVolumeCbm === draft.savedEstimatedVolumeCbm) return
|
||||
|
||||
draft.saving = true
|
||||
draft.error = null
|
||||
try {
|
||||
const result = await updatePlatformUnifiedOrderPlanning(String(row.tenantId), String(row.orderId), {
|
||||
expectedVersion: row.orderVersion!,
|
||||
@@ -672,7 +664,17 @@ async function savePlanning(row: UnifiedOrderResource) {
|
||||
draft.savedEstimatedVolumeCbm = estimatedVolumeCbm
|
||||
ElMessage.success('订单运营预估已更新')
|
||||
} catch (error) {
|
||||
draft.error = getApiErrorDetails(error, '订单运营预估保存失败')
|
||||
const details = getApiErrorDetails(error, '订单运营预估保存失败')
|
||||
const evidence = [
|
||||
details.code ? `错误码 ${details.code}` : '',
|
||||
details.requestId ? `Request ID ${details.requestId}` : '',
|
||||
details.traceId ? `Trace ID ${details.traceId}` : '',
|
||||
].filter(Boolean).join(';')
|
||||
ElMessage.error({
|
||||
message: evidence ? `${details.message}(${evidence})` : details.message,
|
||||
duration: 6_000,
|
||||
showClose: true,
|
||||
})
|
||||
} finally {
|
||||
draft.saving = false
|
||||
}
|
||||
@@ -1938,7 +1940,7 @@ onBeforeUnmount(() => {
|
||||
</section>
|
||||
|
||||
<div class="detail-tabs" role="tablist" aria-label="订单详情模块">
|
||||
<button v-for="tab in detailTabOptions" :id="`order-detail-tab-${tab.key}`" :key="tab.key" type="button" role="tab" :class="{ active: activeDetailTab === tab.key }" :aria-selected="activeDetailTab === tab.key" :aria-controls="`order-detail-panel-${tab.key}`" :tabindex="activeDetailTab === tab.key ? 0 : -1" @click="selectDetailTab(tab.key)"><component :is="tab.icon" :size="17" /><span>{{ tab.label }}</span></button>
|
||||
<button v-for="tab in detailTabs" :id="`order-detail-tab-${tab.key}`" :key="tab.key" type="button" role="tab" :class="{ active: activeDetailTab === tab.key }" :aria-selected="activeDetailTab === tab.key" :aria-controls="`order-detail-panel-${tab.key}`" :tabindex="activeDetailTab === tab.key ? 0 : -1" @click="selectDetailTab(tab.key)"><component :is="tab.icon" :size="17" /><span>{{ tab.label }}</span></button>
|
||||
</div>
|
||||
|
||||
<section :id="`order-detail-panel-${activeDetailTab}`" class="detail-panel" role="tabpanel" :aria-labelledby="`order-detail-tab-${activeDetailTab}`" tabindex="0">
|
||||
@@ -2089,18 +2091,6 @@ onBeforeUnmount(() => {
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<template v-else-if="activeDetailTab === 'workflow'">
|
||||
<UnifiedOrderWorkflowPanel
|
||||
:tenant-id="String(detail.summary.tenantId)"
|
||||
:inbound-id="String(detail.summary.inboundId)"
|
||||
:inbound-code="detail.summary.inboundCode"
|
||||
:order-id="String(detail.summary.orderId)"
|
||||
:main-status="detail.summary.mainStatus"
|
||||
:inbound-version="detail.summary.inboundVersion"
|
||||
:picking-version="detail.summary.loading?.pickingVersion"
|
||||
@updated="loadDetail(true)"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="activeDetailTab === 'measurement'">
|
||||
<div class="panel-title">
|
||||
<div><h2>测量数据</h2><p>{{ detail.summary.measurement?.completedAt ? `完成于 ${formatDate(detail.summary.measurement.completedAt)}` : '尚未完成测量' }}</p></div>
|
||||
@@ -2319,10 +2309,6 @@ onBeforeUnmount(() => {
|
||||
/>
|
||||
<span v-else>{{ formatNumber((row as UnifiedOrderResource).estimatedVolumeCbm, 4) }}</span>
|
||||
<small v-if="planningDraft(row as UnifiedOrderResource).saving">保存中…</small>
|
||||
<div v-if="planningDraft(row as UnifiedOrderResource).error" class="planning-error" role="alert">
|
||||
<span>{{ planningDraft(row as UnifiedOrderResource).error?.message }}</span>
|
||||
<ApiErrorMeta :details="planningDraft(row as UnifiedOrderResource).error!" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #expectedArrival="{ row }">
|
||||
@@ -2343,10 +2329,6 @@ onBeforeUnmount(() => {
|
||||
/>
|
||||
<span v-else>{{ formatDate((row as UnifiedOrderResource).expectedArrivalAt) }}</span>
|
||||
<small v-if="planningDraft(row as UnifiedOrderResource).saving">保存中…</small>
|
||||
<div v-if="planningDraft(row as UnifiedOrderResource).error" class="planning-error" role="alert">
|
||||
<span>{{ planningDraft(row as UnifiedOrderResource).error?.message }}</span>
|
||||
<ApiErrorMeta :details="planningDraft(row as UnifiedOrderResource).error!" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #field="{ row, column }"><LineClampText v-if="column.property === 'customerName'" :content="displayField(row as UnifiedOrderResource, column.property)" /><LineClampText v-else-if="['customerRemark', 'latestInternalNote'].includes(String(column.property))" :content="displayField(row as UnifiedOrderResource, column.property)" :lines="2" /><template v-else>{{ displayField(row as UnifiedOrderResource, column.property) }}</template></template>
|
||||
@@ -2497,8 +2479,6 @@ onBeforeUnmount(() => {
|
||||
.planning-volume-input,.planning-arrival-input { width: 100%; max-width: 100%; }
|
||||
.planning-cell :deep(.el-input__wrapper) { min-height: 32px; padding-right: 9px; padding-left: 9px; }
|
||||
.planning-cell :deep(.el-input__inner) { min-width: 0; text-align: left; }
|
||||
.planning-error { min-width: 0; color: var(--el-color-danger); font-size: 11px; line-height: 1.35; white-space: normal; }
|
||||
.planning-error :deep(.api-error-meta) { margin-top: 3px; }
|
||||
.inbound-relation-cell { display: grid; min-width: 0; gap: 5px; }
|
||||
.inbound-code-value { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.order-relation-link { width: fit-content; max-width: 100%; height: auto; justify-content: flex-start; padding: 0; font-size: 12px; }
|
||||
|
||||
Reference in New Issue
Block a user