优化
This commit is contained in:
@@ -9,6 +9,15 @@ const routeSource = readFileSync(resolve(process.cwd(), 'src/modules/unified-ord
|
||||
const densitySource = readFileSync(resolve(process.cwd(), 'src/styles/adminDensity.css'), 'utf8')
|
||||
|
||||
describe('UnifiedOrdersView contract', () => {
|
||||
it('imports old-system workbooks only for the fixed test customer', () => {
|
||||
expect(source).toContain("can('platform:order:import')")
|
||||
expect(source).toContain('导入旧系统订单')
|
||||
expect(source).toContain('固定归属客户')
|
||||
expect(source).toContain('IT测试公司')
|
||||
expect(source).toContain('accept=".xlsx"')
|
||||
expect(source).toContain('legacyImportResult.items.filter((item) => !item.success)')
|
||||
})
|
||||
|
||||
it('exposes desktop pallet printing only for one-click dropship orders', () => {
|
||||
expect(source).toContain("detail.value?.summary.fulfillmentType === 'ONE_CLICK_DROPSHIP'")
|
||||
expect(source).toContain("{ key: 'dropship-print', label: '托盘打印', icon: '🖨️' }")
|
||||
@@ -48,7 +57,7 @@ describe('UnifiedOrdersView contract', () => {
|
||||
|
||||
it('exposes the product main statuses including dropship palletizing', () => {
|
||||
expect(unifiedOrderStatusOptions.map((item) => item.label)).toEqual([
|
||||
'待审核', '待测量数据', '待一件代发组托', '待分柜', '装柜中', '装柜完成', '派送完成',
|
||||
'待审核', '待测量数据', '待一件代发组托', '待分柜', '装柜中', '装柜完成', '派送完成', '已完成',
|
||||
])
|
||||
for (const legacyLabel of ['待客户确认', '差异处理中', '派送中', '进入分柜']) {
|
||||
expect(source).not.toContain(legacyLabel)
|
||||
@@ -151,7 +160,7 @@ describe('UnifiedOrdersView contract', () => {
|
||||
expect(source).toContain('v-model:page-size="pageSize"')
|
||||
const columnSource = source.slice(source.indexOf('const columns ='), source.indexOf('function emptyFilters'))
|
||||
const expectedColumnKeys = [
|
||||
'actions', 'inboundCode', 'mainStatus', 'customerName', 'receivingWarehouseName',
|
||||
'actions', 'inboundCode', 'orderType', 'mainStatus', 'customerName', 'receivingWarehouseName',
|
||||
'destinationWarehouseName', 'channelName', 'totalPackageCount', 'totalPalletCount', 'requestedPickupAt', 'locationCodes', 'measurementReviewStatus', 'actualGrossWeightKg',
|
||||
'actualVolumeCbm', 'totalNetWeightKg', 'chargeableWeightKg', 'chargeableVolumeCbm', 'expectedShipDate',
|
||||
'estimatedVolumeCbm', 'expectedVolumeCbm', 'expectedArrivalAt', 'feeReviewStatus', 'skuNos', 'totalQuantity', 'expressCompany', 'expressTrackingNo',
|
||||
@@ -171,6 +180,10 @@ describe('UnifiedOrdersView contract', () => {
|
||||
expect(columnSource).not.toContain("key: 'coLoadGroup'")
|
||||
expect(columnSource).not.toContain("key: 'orderNo'")
|
||||
expect(columnSource).toContain("key: 'inboundCode', prop: 'inboundCode', label: '入仓码', minWidth: 160, slotName: 'inboundCode'")
|
||||
expect(columnSource).toContain("key: 'orderType', label: '订单类型', minWidth: 120, slotName: 'orderType'")
|
||||
expect(source).toContain("if (row.fulfillmentType === 'ONE_CLICK_DROPSHIP') return '一件代发'")
|
||||
expect(source).toContain("if (row.businessType === 'FBA' || row.businessType === 'FBA_SEA') return 'FBA 头程'")
|
||||
expect(source).toContain("if (row.businessType === 'OVERSEAS_SEA') return '海外中转'")
|
||||
expect(columnSource).toContain("{ key: 'mainStatus', prop: 'mainStatus', label: '订单主状态'")
|
||||
expect(columnSource).not.toContain("key: 'loadingStatus'")
|
||||
expect(columnSource).not.toContain("key: 'businessDate'")
|
||||
|
||||
@@ -19,6 +19,9 @@ import {
|
||||
type UnifiedOrderResource,
|
||||
type UnifiedOrderBatchApprovalPreviewItem,
|
||||
type UnifiedOrderBatchReviewItem,
|
||||
importPlatformLegacyOrders,
|
||||
createLegacyOrderImportIdempotencyKey,
|
||||
type LegacyOrderImportResult,
|
||||
} from '@/modules/unified-orders/api/unifiedOrders'
|
||||
import { cancelPlatformCoLoadGroup, createPlatformCoLoadGroup, getPlatformCoLoadGroupByInboundCode, type CoLoadBusinessSummary, type CoLoadGroup } from '@/modules/unified-orders/api/coLoadGroups'
|
||||
import OrderFulfillmentTreePanel from '@/modules/unified-orders/components/OrderFulfillmentTreePanel.vue'
|
||||
@@ -117,6 +120,12 @@ const pageSize = ref(20)
|
||||
const total = ref(0)
|
||||
const loading = ref(false)
|
||||
const loadError = ref<ApiErrorDetails | null>(null)
|
||||
const legacyImportOpen = ref(false)
|
||||
const legacyImportFile = ref<File | null>(null)
|
||||
const legacyImportIdempotencyKey = ref('')
|
||||
const legacyImportSaving = ref(false)
|
||||
const legacyImportResult = ref<LegacyOrderImportResult | null>(null)
|
||||
const legacyImportError = ref<ApiErrorDetails | null>(null)
|
||||
type PlanningDraft = {
|
||||
expectedArrivalAt: string
|
||||
estimatedVolumeCbm: number | null
|
||||
@@ -633,6 +642,7 @@ const columns = computed<DataTableColumn[]>(() => [
|
||||
selectable: () => !batchApprovalSaving.value && !coLoadSaving.value && !exportSubmitting.value,
|
||||
} satisfies DataTableColumn] : []),
|
||||
{ key: 'inboundCode', prop: 'inboundCode', label: '入仓码', minWidth: 160, slotName: 'inboundCode' },
|
||||
{ key: 'orderType', label: '订单类型', minWidth: 120, slotName: 'orderType' },
|
||||
{ key: 'mainStatus', prop: 'mainStatus', label: '订单主状态', minWidth: 120, slotName: 'mainStatus' },
|
||||
{ key: 'customerName', prop: 'customerName', label: '客户', minWidth: 120, slotName: 'field' },
|
||||
{ key: 'receivingWarehouseName', prop: 'receivingWarehouseName', label: '平台收货仓', minWidth: 100, slotName: 'field' },
|
||||
@@ -1811,6 +1821,48 @@ function coLoadGroup(row: UnifiedOrderResource) {
|
||||
return row.coLoadGroup || null
|
||||
}
|
||||
|
||||
function openLegacyImport() {
|
||||
legacyImportFile.value = null
|
||||
legacyImportResult.value = null
|
||||
legacyImportError.value = null
|
||||
legacyImportIdempotencyKey.value = ''
|
||||
legacyImportOpen.value = true
|
||||
}
|
||||
|
||||
function selectLegacyImportFile(uploadFile: UploadFile) {
|
||||
legacyImportFile.value = uploadFile.raw || null
|
||||
legacyImportResult.value = null
|
||||
legacyImportError.value = null
|
||||
legacyImportIdempotencyKey.value = createLegacyOrderImportIdempotencyKey()
|
||||
}
|
||||
|
||||
async function submitLegacyImport() {
|
||||
if (!legacyImportFile.value || legacyImportSaving.value) return
|
||||
legacyImportSaving.value = true
|
||||
legacyImportError.value = null
|
||||
try {
|
||||
legacyImportResult.value = await importPlatformLegacyOrders(
|
||||
legacyImportFile.value,
|
||||
legacyImportIdempotencyKey.value,
|
||||
)
|
||||
const result = legacyImportResult.value
|
||||
if (result.failed) ElMessage.warning(`导入完成:成功 ${result.succeeded} 条,失败 ${result.failed} 条`)
|
||||
else ElMessage.success(`已导入 ${result.succeeded} 条旧系统订单`)
|
||||
await loadOrders()
|
||||
} catch (error) {
|
||||
legacyImportError.value = getApiErrorDetails(error, '旧系统订单导入失败,请核对文件后重试')
|
||||
} finally {
|
||||
legacyImportSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function orderTypeLabel(row: UnifiedOrderResource) {
|
||||
if (row.fulfillmentType === 'ONE_CLICK_DROPSHIP') return '一件代发'
|
||||
if (row.businessType === 'FBA' || row.businessType === 'FBA_SEA') return 'FBA 头程'
|
||||
if (row.businessType === 'OVERSEAS_SEA') return '海外中转'
|
||||
return firstLegBusinessTypeLabel(row.businessType)
|
||||
}
|
||||
|
||||
function unifiedOrderRowClassName({ row }: { row: UnifiedOrderResource }) {
|
||||
return row.coLoadGroup && row.coLoadGroup.status !== 'CANCELLED' ? 'is-coload-group-row' : ''
|
||||
}
|
||||
@@ -2654,6 +2706,7 @@ onBeforeUnmount(() => {
|
||||
<div v-if="coLoadError" class="coload-error" role="alert"><span>{{ coLoadError.message }}</span><ApiErrorMeta :details="coLoadError" /></div>
|
||||
<DataTable v-model:page="page" v-model:page-size="pageSize" :data="records" :columns="columns" preference-key="unified-orders-table-v2" :loading="loading" :total="total" :page-sizes="[20, 50, 100]" :row-class-name="unifiedOrderRowClassName" row-key="orderId" border stripe empty-text="暂无符合条件的订单" aria-label="统一订单列表" @selection-change="handleOrderSelectionChange">
|
||||
<template #toolbar-actions>
|
||||
<el-button v-if="can('platform:order:import')" :disabled="loading" @click="openLegacyImport"><FileUp :size="16" />导入旧系统订单</el-button>
|
||||
<el-button v-if="can('platform:order:export') && can('platform:download:list')" :loading="exportSubmitting" :disabled="loading || exportSubmitting" @click="exportOrders"><Download :size="16" />导出<span v-if="selectedOrders.length">(已选 {{ selectedOrders.length }})</span></el-button>
|
||||
<el-button v-if="can('platform:order:approve')" type="success" :loading="batchApprovalLoading || batchApprovalSaving" :disabled="loading || !selectedApprovalOrders.length" @click="openBatchApproval"><CircleCheck :size="16" />批量审核通过<span v-if="selectedApprovalOrders.length">({{ selectedApprovalOrders.length }})</span></el-button>
|
||||
<div v-if="can('platform:firstleg:smart-loading:assign')" class="coload-toolbar-action">
|
||||
@@ -2677,6 +2730,7 @@ onBeforeUnmount(() => {
|
||||
><GitBranch :size="14" />{{ orderRelationLabel(row as UnifiedOrderResource) }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #orderType="{ row }"><el-tag size="small" effect="light" type="info">{{ orderTypeLabel(row as UnifiedOrderResource) }}</el-tag></template>
|
||||
<template #orderStatus="{ row }"><StatusBadge :status="orderStatusLabel((row as UnifiedOrderResource).internalOrderStatus || (row as UnifiedOrderResource).mainStatus)" /></template>
|
||||
<template #measurementReviewStatus="{ row }"><StatusBadge :status="measurementStatusLabel((row as UnifiedOrderResource).measurement?.reviewStatus)" /></template>
|
||||
<template #channel="{ row }"><div class="channel-cell"><LineClampText :content="(row as UnifiedOrderResource).channelName" /><el-tag v-if="(row as UnifiedOrderResource).channelChanged" size="small" type="warning">曾变更渠道</el-tag></div></template>
|
||||
@@ -2731,7 +2785,29 @@ onBeforeUnmount(() => {
|
||||
</DataTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</ListPageLayout>
|
||||
<AppDialog class="admin-density-dialog" v-model="legacyImportOpen" title="导入旧系统订单" width="min(760px, calc(100vw - 28px))" confirm-text="开始导入" :confirm-loading="legacyImportSaving" :confirm-disabled="!legacyImportFile || legacyImportSaving || !!legacyImportResult" destroy-on-close @confirm="submitLegacyImport">
|
||||
<div class="legacy-import-content">
|
||||
<dl class="legacy-import-facts">
|
||||
<div><dt>固定归属客户</dt><dd>IT测试公司</dd></div>
|
||||
<div><dt>导入结果状态</dt><dd>待审核</dd></div>
|
||||
</dl>
|
||||
<el-upload :auto-upload="false" :limit="1" accept=".xlsx" :on-change="selectLegacyImportFile">
|
||||
<el-button><FileUp :size="16" />选择 Excel 文件</el-button>
|
||||
<template #tip><div class="el-upload__tip">仅支持旧系统“订单统计导出” .xlsx 文件,最多 200 条、5MB;入仓码保持不变。</div></template>
|
||||
</el-upload>
|
||||
<LoadErrorState v-if="legacyImportError" compact title="旧系统订单导入失败" :message="legacyImportError.message" :show-retry="false"><template #details><ApiErrorMeta :details="legacyImportError" /></template></LoadErrorState>
|
||||
<div v-if="legacyImportResult" class="legacy-import-result">
|
||||
<p>共 {{ legacyImportResult.total }} 条,成功 {{ legacyImportResult.succeeded }} 条,失败 {{ legacyImportResult.failed }} 条。</p>
|
||||
<el-table v-if="legacyImportResult.failed" :data="legacyImportResult.items.filter((item) => !item.success)" border stripe max-height="280">
|
||||
<el-table-column prop="excelRow" label="Excel 行" width="90" />
|
||||
<el-table-column prop="legacyInboundCode" label="入仓码" min-width="160" />
|
||||
<el-table-column prop="errorMessage" label="失败原因" min-width="300" />
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</AppDialog>
|
||||
<SmartLoadingDialog v-model="smartLoadingOpen" />
|
||||
<AppDialog class="admin-density-dialog" v-model="batchApprovalOpen" title="批量审核订单" confirm-text="确认批量审核" confirm-type="success" width="min(1100px, calc(100vw - 28px))" :loading="batchApprovalLoading" :confirm-loading="batchApprovalSaving" :confirm-disabled="batchApprovalLoading || !batchApprovalSelectedRows.length" destroy-on-close @confirm="batchApproveOrders">
|
||||
<el-alert title="请逐票核对入仓码、客户、中文品名、渠道和目的仓;审核通过后订单进入待测量。" type="info" :closable="false" show-icon />
|
||||
@@ -3016,6 +3092,12 @@ onBeforeUnmount(() => {
|
||||
.hold-summary dt { flex: 0 0 auto; margin: 0; color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
|
||||
.hold-summary dd { margin: 0; color: var(--cc-text); font-size: var(--admin-font-body, 13px); overflow-wrap: anywhere; }
|
||||
.hold-actions { display: flex; flex: 0 0 auto; gap: var(--admin-space-3, 8px); }
|
||||
.legacy-import-content { display: grid; gap: var(--admin-space-5, 12px); }
|
||||
.legacy-import-facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 0; border: 1px solid var(--cc-border); }
|
||||
.legacy-import-facts > div { display: grid; grid-template-columns: 120px minmax(0, 1fr); align-items: center; padding-block: var(--admin-space-2, 6px); }
|
||||
.legacy-import-facts dt { padding: 8px 10px; color: var(--cc-text-muted); }
|
||||
.legacy-import-facts dd { margin: 0; padding: 8px 10px; font-weight: 600; }
|
||||
.legacy-import-result > p { margin: 0 0 var(--admin-space-3, 8px); }
|
||||
.hold-actions :deep(.el-button) { margin-left: 0; }
|
||||
.hold-block-reason { margin: var(--admin-space-5, 12px) 0 0; padding: 9px var(--admin-space-5, 12px); border: 1px solid var(--el-color-warning-light-5); border-radius: var(--admin-radius, 4px); background: var(--el-color-warning-light-9); color: var(--el-color-warning-dark-2); font-size: var(--admin-font-caption, 12px); }
|
||||
.hold-history-title { margin: var(--admin-space-6, 16px) 0 var(--admin-space-3, 8px); color: #344054; font-size: var(--admin-font-section, 14px); }
|
||||
|
||||
Reference in New Issue
Block a user