@@ -15,7 +15,7 @@ import LoadErrorState from '@/shared/components/LoadErrorState.vue'
import OperationErrorToast from '@/shared/components/OperationErrorToast.vue'
import QueryToolbar , { type QueryField , type QueryModel } from '@/shared/components/QueryToolbar.vue'
import RowActions , { type RowActionItem } from '@/shared/components/RowActions.vue'
import { changeContainerReceivingWarehouse , convertLastMileContainerToInboundPlans , createManualInboundPlan , downloadLastMileOrders , getContainerInboundPlanPreview , getContainerPlanProgress , getLastMileOrderManualNodes , listContainerPlanProgress , listLastMileOrders , reviewDirectRelabelOrder , reviewReturnOrder , transferLastMileOrders , updateLastMileOrderHold , updateLastMileOrderManualNodes , type ContainerInboundPlanPreviewDto , type ContainerInboundPlanPreviewGroupDto , type ContainerInboundPlanPreviewOrderDto , type ContainerPlanProgressDetailDto , type ContainerPlanProgressSummaryDto , type ManualNodeCode } from '../api/lastMileOrders'
import { changeContainerReceivingWarehouse , convertLastMileContainerToInboundPlans , createManualInboundPlan , downloadLastMileOrders , getContainerInboundPlanPreview , getContainerPlanProgress , getLastMileOrderManualNodes , listContainerPlanProgress , listLastMileOrders , reviewDirectRelabelOrder , reviewReturnOrder , transferLastMileOrders , updateLastMileOrderHold , updateLastMileOrderManualNodes , type ContainerInboundPlanPreviewDto , type ContainerInboundPlanPreviewGroupDto , type ContainerInboundPlanPreviewOrderDto , type ContainerPlanProgressDetailDto , type ContainerPlanProgressSummaryDto , type ManualNodeCode , type LastMileOrderQuery } from '../api/lastMileOrders'
import OrderReferenceText from '../components/OrderReferenceText.vue'
import { reviewCargoDiscard } from '../api/lastMileOrders'
import WarehouseTransferReviewDialog from '../components/WarehouseTransferReviewDialog.vue'
@@ -71,7 +71,7 @@ async function submitReturnPackingPlan() {
}
const actionLoading = ref ( false )
const exportLoading = ref ( false )
async function exportOrders ( ) { exportLoading . value = true ; try { const { createdAt : range , ... rest } = filters . value ; saveCsv ( await downloadLastMileOrders ( { ... rest , createdFrom : Array . isArray ( range ) && range [ 0 ] ? ` ${ String ( range [ 0 ] ) . slice ( 0 , 10 ) } T00:00:00 ` : undefined , createdTo : Array . isArray ( range ) && range [ 1 ] ? ` ${ String ( range [ 1 ] ) . slice ( 0 , 10 ) } T23:59:59 ` : undefined } ) as Blob , '尾程订单.csv' ) } catch ( cause ) { ElMessage . error ( getApiErrorDetails ( cause , '尾程订单导出失败,请重试' ) . message ) } finally { exportLoading . value = false } }
async function exportOrders ( ) { exportLoading . value = true ; try { saveCsv ( await downloadLastMileOrders ( orderQuery ( ) ) as Blob , '尾程订单.csv' ) } catch ( cause ) { ElMessage . error ( getApiErrorDetails ( cause , '尾程订单导出失败,请重试' ) . message ) } finally { exportLoading . value = false } }
const actionError = ref < ApiErrorDetails | null > ( null )
const actionTarget = ref < LastMileOrderListItem | null > ( null )
const inboundPlanOpen = ref ( false )
@@ -150,8 +150,8 @@ const receivingWarehouseTarget = ref<ContainerPlanProgressSummaryDto | null>(nul
const receivingWarehouseForm = reactive ( { warehouseId : '' , remark : '' } )
let receivingWarehouseIdempotencyKey = ''
let containerOptionRequest = 0
const statusTab = ref < '全部' | '待分配 ' | '柜子' > ( '全部' )
const statusTabs = [ '全部' , '待分配 ' ] as const
const statusTab = ref < '全部' | '开船前' | '开船后 ' | '柜子' > ( '全部' )
const statusTabs = [ '全部' , '开船前' , '开船后 '] as const
const showAdvancedFilters = ref ( false )
const filters = ref < QueryModel > ( { orderNo : '' , relabelNo : '' , orderType : '' , warehouse : '' , inboundCode : '' , lxCode : '' , sourceOrderNo : '' , containerNo : '' , status : '' , transferWarehouseCode : '' , abnormal : '' , detained : '' , createdAt : [ ] } )
const allFields : QueryField [ ] = [
@@ -184,10 +184,11 @@ const allFields: QueryField[] = [
{ key : 'detained' , type : 'select' , label : '是否暂扣' , width : 140 , placeholder : '请选择是否暂扣' , options : [ { label : '是' , value : 'true' } , { label : '否' , value : 'false' } ] } ,
{ key : 'createdAt' , type : 'daterange' , label : '创建时间' , width : 280 } ,
]
const availableFields = computed ( ( ) => allFields . filter ( ( field ) => statusTab . value === '全部' || field . key !== 'orderType' ) )
const fields = computed < QueryField [ ] > ( ( ) => statusTab . value === '柜子'
? allFields . filter ( ( field ) => [ 'containerNo' , 'createdAt' ] . includes ( field . key ) )
: showAdvancedFilters . value
? allFields
? availableFields . value
: allFields . filter ( ( field ) => [ 'orderNo' , 'inboundCode' , 'warehouse' ] . includes ( field . key ) ) )
const selectedContainer = computed ( ( ) => containerOptions . value . find ( ( row ) => row . sourceContainerBookingId === selectedContainerBookingId . value ) )
const selectedReceivingWarehouse = computed ( ( ) => receivingWarehouseOptions . value . find ( ( row ) => (
@@ -333,6 +334,29 @@ function changeContainerPageSize(value: number) {
page . value = 1
void load ( )
}
function orderQuery ( ) : LastMileOrderQuery {
const dateRange = Array . isArray ( filters . value . createdAt ) ? filters . value . createdAt . map ( String ) : [ ]
const booleanValue = ( value : unknown ) => value === 'true' ? true : value === 'false' ? false : undefined
return {
page : page . value ,
size : pageSize . value ,
orderNo : String ( filters . value . orderNo || '' ) . trim ( ) || undefined ,
relabelNo : String ( filters . value . relabelNo || '' ) . trim ( ) || undefined ,
orderType : statusTab . value === '全部' ? String ( filters . value . orderType || '' ) . trim ( ) || undefined : 'FIRST_LEG' ,
sailingStage : statusTab . value === '开船前' ? 'BEFORE_DEPARTURE' : statusTab . value === '开船后' ? 'AFTER_DEPARTURE' : undefined ,
warehouse : String ( filters . value . warehouse || '' ) . trim ( ) || undefined ,
inboundCode : String ( filters . value . inboundCode || '' ) . trim ( ) || undefined ,
lxCode : String ( filters . value . lxCode || '' ) . trim ( ) || undefined ,
sourceOrderNo : String ( filters . value . sourceOrderNo || '' ) . trim ( ) || undefined ,
containerNo : String ( filters . value . containerNo || '' ) . trim ( ) || undefined ,
currentStatus : String ( filters . value . status || '' ) . trim ( ) || undefined ,
transferWarehouseCode : String ( filters . value . transferWarehouseCode || '' ) . trim ( ) || undefined ,
abnormal : booleanValue ( filters . value . abnormal ) ,
detained : booleanValue ( filters . value . detained ) ,
createdFrom : dateRange [ 0 ] ? ` ${ dateRange [ 0 ] } T00:00:00 ` : undefined ,
createdTo : dateRange [ 1 ] ? ` ${ dateRange [ 1 ] } T23:59:59 ` : undefined ,
}
}
async function load ( model : QueryModel = filters . value ) {
Object . assign ( filters . value , model )
loading . value = true
@@ -359,26 +383,7 @@ async function load(model: QueryModel = filters.value) {
return
}
try {
const dateRange = Array . isArray ( filters . value . createdAt ) ? filters . value . createdAt . map ( String ) : [ ]
const booleanValue = ( value : unknown ) => value === 'true' ? true : value === 'false' ? false : undefined
const result = await listLastMileOrders ( {
page : page . value ,
size : pageSize . value ,
orderNo : String ( filters . value . orderNo || '' ) . trim ( ) || undefined ,
relabelNo : String ( filters . value . relabelNo || '' ) . trim ( ) || undefined ,
orderType : String ( filters . value . orderType || '' ) . trim ( ) || undefined ,
warehouse : String ( filters . value . warehouse || '' ) . trim ( ) || undefined ,
inboundCode : String ( filters . value . inboundCode || '' ) . trim ( ) || undefined ,
lxCode : String ( filters . value . lxCode || '' ) . trim ( ) || undefined ,
sourceOrderNo : String ( filters . value . sourceOrderNo || '' ) . trim ( ) || undefined ,
containerNo : String ( filters . value . containerNo || '' ) . trim ( ) || undefined ,
currentStatus : statusTab . value === '待分配' ? 'PENDING_ASSIGNMENT' : String ( filters . value . status || '' ) . trim ( ) || undefined ,
transferWarehouseCode : String ( filters . value . transferWarehouseCode || '' ) . trim ( ) || undefined ,
abnormal : booleanValue ( filters . value . abnormal ) ,
detained : booleanValue ( filters . value . detained ) ,
createdFrom : dateRange [ 0 ] ? ` ${ dateRange [ 0 ] } T00:00:00 ` : undefined ,
createdTo : dateRange [ 1 ] ? ` ${ dateRange [ 1 ] } T23:59:59 ` : undefined ,
} )
const result = await listLastMileOrders ( orderQuery ( ) )
rows . value = result . records . map ( toLastMileOrderListItem )
total . value = result . total
} catch ( error ) {
@@ -1034,7 +1039,7 @@ onMounted(() => { void load() })
< ! - - 柜子工作台入口暂时隐藏 , 保留现有实现便于后续恢复 。 - - >
<!-- < el-radio-button value = "柜子" > 柜子工作台 ( { { containerTotal } } ) < / el-radio-button > -- >
< / div >
< QueryToolbar id = "last-mile-orders-advanced-filters" v-model = "filters" :fields="fields" :available-fields="all Fields" :loading="loading" ariaLabel="尾程订单筛选" preference-key="overseas-last-mile-orders-query" :search-on-reset="false" @search="search" @reset="reset" >
< QueryToolbar id = "last-mile-orders-advanced-filters" v-model = "filters" :fields="fields" :available-fields="available Fields" :loading="loading" ariaLabel="尾程订单筛选" preference-key="overseas-last-mile-orders-query" :search-on-reset="false" @search="search" @reset="reset" >
< template v-if = "statusTab !== '柜子'" # actions >
< el-button :aria-expanded = "showAdvancedFilters" aria -controls = " last -mile -orders -advanced -filters " @click ="showAdvancedFilters = !showAdvancedFilters" >
< SlidersHorizontal :size = "16" / > { { showAdvancedFilters ? '收起筛选' : '高级筛选' } }
@@ -1088,7 +1093,7 @@ onMounted(() => { void load() })
< / div >
< / section > < / template >
< template v-if = "statusTab !== '柜子'" #toolbar-actions><el-button v-if="selectedReturnRows.length" type="warning" @click="openReturnPackingPlan"><PackagePlus :size="16" />创建退货合箱计划({{ selectedReturnRows.length }}) </el-button><el-button v-if="access.can(permission.lastMileOrderExport)" plain :loading="exportLoading" @click="exportOrders"><Download :size="16" />导出订单</el-button><el-button v-if="access.can(permission.lastMileOrderTransfer)" plain :disabled="!selectedRows.length || hasSelectedRejectedReturn() || actionLoading" @click="openTransfer">转仓</el-button><el-button v-if="access.can(permission.inboundOrderCreate) && selectedRows.length === 1 && canGenerateInboundPlan(selectedRows[0])" type="primary" plain :disabled="hasSelectedInboundPlan() || actionLoading" @click="openInboundPlan()">生成入库计划</el-button><el-button v-if="statusTab === '待分配' && access.can(permission.inboundOrderCreate)" type="primary" :disabled="actionLoading" @click="openContainerConversion" > 按柜批量生成 < / el -button > < / template >
< template v-if = "statusTab !== '柜子'" #toolbar-actions><el-button v-if="selectedReturnRows.length" type="warning" @click="openReturnPackingPlan"><PackagePlus :size="16" />创建退货合箱计划({{ selectedReturnRows.length }}) </el-button><el-button v-if="access.can(permission.lastMileOrderExport)" plain :loading="exportLoading" @click="exportOrders"><Download :size="16" />导出订单</el-button><el-button v-if="access.can(permission.lastMileOrderTransfer)" plain :disabled="!selectedRows.length || hasSelectedRejectedReturn() || actionLoading" @click="openTransfer">转仓</el-button><el-button v-if="access.can(permission.inboundOrderCreate) && selectedRows.length === 1 && canGenerateInboundPlan(selectedRows[0])" type="primary" plain :disabled="hasSelectedInboundPlan() || actionLoading" @click="openInboundPlan()">生成入库计划</el-button><el-button v-if="access.can(permission.inboundOrderCreate)" type="primary" :disabled="actionLoading" @click="openContainerConversion" > 按柜批量生成 < / el -button > < / template >
< template # relabelOrderNo = "{ row }" > < div v-for = "application in row.relabelApplications" :key="application.relabelNo" class="relabel-suborder">{{ application.relabelNo }}</div><span v-if="!row.relabelApplications?.length" > {{ row.relabelOrderNo }} < / span > < / template >
< template # relabelStatus = "{ row }" > < div v-for = "application in row.relabelApplications" :key="application.relabelNo" class="relabel-suborder">{{ application.statusLabel }} · {{ application.quantity }} 件</div><span v-if="!row.relabelApplications?.length" > {{ row.relabelStatus }} < / span > < / template >
< template # actions = "{ row }" > < RowActions text -actions : actions = "lastMileRowActions(row as LastMileOrderListItem)" : more -label = " ` 更多尾程订单操作 · $ { ( row as LastMileOrderListItem ) .orderNo | | ' 当前记录 ' } ` " @action ="handleLastMileRowAction($event, row as LastMileOrderListItem)" / > < / template >