@@ -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 条 、 5 MB ; 入仓码保持不变 。 < / 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 , 12 px ) ; }
. hold - summary dd { margin : 0 ; color : var ( -- cc - text ) ; font - size : var ( -- admin - font - body , 13 px ) ; overflow - wrap : anywhere ; }
. hold - actions { display : flex ; flex : 0 0 auto ; gap : var ( -- admin - space - 3 , 8 px ) ; }
. legacy - import - content { display : grid ; gap : var ( -- admin - space - 5 , 12 px ) ; }
. legacy - import - facts { display : grid ; grid - template - columns : repeat ( 2 , minmax ( 0 , 1 fr ) ) ; margin : 0 ; border : 1 px solid var ( -- cc - border ) ; }
. legacy - import - facts > div { display : grid ; grid - template - columns : 120 px minmax ( 0 , 1 fr ) ; align - items : center ; padding - block : var ( -- admin - space - 2 , 6 px ) ; }
. legacy - import - facts dt { padding : 8 px 10 px ; color : var ( -- cc - text - muted ) ; }
. legacy - import - facts dd { margin : 0 ; padding : 8 px 10 px ; font - weight : 600 ; }
. legacy - import - result > p { margin : 0 0 var ( -- admin - space - 3 , 8 px ) ; }
. hold - actions : deep ( . el - button ) { margin - left : 0 ; }
. hold - block - reason { margin : var ( -- admin - space - 5 , 12 px ) 0 0 ; padding : 9 px var ( -- admin - space - 5 , 12 px ) ; border : 1 px solid var ( -- el - color - warning - light - 5 ) ; border - radius : var ( -- admin - radius , 4 px ) ; background : var ( -- el - color - warning - light - 9 ) ; color : var ( -- el - color - warning - dark - 2 ) ; font - size : var ( -- admin - font - caption , 12 px ) ; }
. hold - history - title { margin : var ( -- admin - space - 6 , 16 px ) 0 var ( -- admin - space - 3 , 8 px ) ; color : # 344054 ; font - size : var ( -- admin - font - section , 14 px ) ; }