@@ -21,7 +21,6 @@ import {
performOceanBookingAction ,
releaseOceanBooking ,
requestOceanBookingChange ,
updateOceanBooking ,
listWarehouseLogisticsChannels ,
type OceanLogisticsChannel ,
type OceanBookingAction ,
@@ -47,28 +46,24 @@ const containerKindOptions = [
{ value : 'LCL' , label : '拼箱 LCL' } ,
]
const actionLabels : Record < OceanBookingAction , string > = {
submit : '提交' , confirm : '确认订舱' , release : '确认放舱' , fail : '标记失败 ', cancel : '取消订舱' ,
release : '确认放舱 ', cancel : '取消订舱' ,
'change-request' : '申请改船' , 'change-confirm' : '确认改船' , 'change-fail' : '改船失败' ,
}
const actionPermissions : Record < OceanBookingAction , string > = {
submit : 'platform:firstleg:ocean-booking:submit' , confirm : 'platform:firstleg:ocean-booking:confirm ',
release : 'platform:firstleg:ocean-booking:release' , fail : 'platform:firstleg:ocean-booking:confirm' ,
release : 'platform:firstleg:ocean-booking:release ' ,
cancel : 'platform:firstleg:ocean-booking:cancel' , 'change-request' : 'platform:firstleg:ocean-booking:change' ,
'change-confirm' : 'platform:firstleg:ocean-booking:change' , 'change-fail' : 'platform:firstleg:ocean-booking:change' ,
}
const actionsByStatus : Partial < Record < OceanBookingStatus , OceanBookingAction [ ] > > = {
DRAFT : [ 'submit' , 'cancel' ] ,
SUBMITTED : [ 'confirm' , 'fail' , 'cancel' ] ,
CONFIRMED : [ 'release' , 'change-request' , 'cancel' ] ,
CHANGE _PENDING : [ 'change-confirm' , 'change-fail' , 'cancel' ] ,
FAILED : [ 'cancel' ] ,
}
const route = useRoute ( )
const router = useRouter ( )
const access = useAccessPreviewStore ( )
const id = computed ( ( ) => String ( route . params . bookingId || '' ) )
const isNew = computed ( ( ) => id . value === 'new' )
const isNew = computed ( ( ) => route . name === 'ocean-booking-create' || id . value === 'new' )
const detail = ref < OceanBookingDetail | null > ( null )
const loading = ref ( false )
const saving = ref ( false )
@@ -99,6 +94,7 @@ function blankParties(): OceanBookingParty[] {
function blankData ( ) : OceanBookingDraftData {
return {
customerId : null , customerName : null , channelCode : '' , warehouseId : '' , warehouseName : '' , serviceTerm : null , freightTerm : null , parties : blankParties ( ) ,
billOfLadingNo : '' , vesselName : '' , voyageNo : '' , estimatedSailingAt : null , estimatedArrivalAt : null ,
containerRequirements : [ createBlankContainerRequirement ( ) ] ,
}
}
@@ -106,20 +102,25 @@ const form = reactive<OceanBookingSaveRequest>({ expectedVersion: 0, data: blank
const rules : FormRules = {
warehouseId : [ { required : true , message : '请选择装柜仓库' , trigger : 'change' } ] ,
channelCode : [ { required : true , message : '请选择物流渠道' , trigger : 'change' } ] ,
billOfLadingNo : [ { required : true , message : '请输入提单号' , trigger : 'blur' } ] ,
vesselName : [ { required : true , message : '请输入船名' , trigger : 'blur' } ] ,
voyageNo : [ { required : true , message : '请输入航次' , trigger : 'blur' } ] ,
estimatedSailingAt : [ { required : true , message : '请选择预计开船时间' , trigger : 'change' } ] ,
estimatedArrivalAt : [ { required : true , message : '请选择预计到港时间' , trigger : 'change' } ] ,
serviceTerm : [ { required : true , message : '请输入主要运输条款 Service Term' , trigger : 'blur' } ] ,
freightTerm : [ { required : true , message : '请输入运费条款 Freight Term' , trigger : 'blur' } ] ,
}
const editable = computed ( ( ) => isNew . value || detail . value ? . status === 'DRAFT' )
const editable = computed ( ( ) => isNew . value )
const canSave = computed ( ( ) => isNew . value
? access . can ( 'platform:firstleg:ocean-booking:create' )
: editable . value && access . can ( 'platform:firstleg:ocean-booking:edit' ) )
const availableActions = computed ( ( ) => ( actionsByStatus [ detail . value ? . status || 'DRAFT ' ] || [ ] )
const availableActions = computed ( ( ) => ( actionsByStatus [ detail . value ? . status || 'CONFIRMED ' ] || [ ] )
. filter ( ( action ) => access . can ( actionPermissions [ action ] ) ) )
const changeDialogOpen = ref ( false )
const changeForm = reactive ( { vesselName : '' , voyageNo : '' , estimatedSailingAt : null as string | null , estimatedArrivalAt : null as string | null , reason : '' } )
const releaseDialogOpen = ref ( false )
const releaseForm = reactive ( { billOfLadingNo : '' , vesselName : '' , voyageNo : '' , estimatedSailingAt : null as string | null , estimatedArrivalAt : null as string | null , reason : '' } )
const releaseReason = ref ( '' )
function statusLabel ( status : OceanBookingStatus | string | null | undefined ) {
return oceanBookingStatusLabel ( status )
@@ -153,6 +154,9 @@ function normalizeDraftData() {
form . data . customerName = form . data . customerId ? form . data . customerName : null
form . data . serviceTerm = form . data . serviceTerm ? . trim ( ) || null
form . data . freightTerm = form . data . freightTerm ? . trim ( ) || null
form . data . billOfLadingNo = form . data . billOfLadingNo . trim ( )
form . data . vesselName = form . data . vesselName . trim ( )
form . data . voyageNo = form . data . voyageNo . trim ( )
}
function selectWarehouse ( value : unknown ) {
const warehouseId = String ( value || '' )
@@ -197,6 +201,8 @@ function applyDetail(value: OceanBookingDetail) {
form . expectedVersion = value . version
form . data = {
customerId : value . customerId ? ? null , customerName : value . customerName ? ? null , channelCode : value . channelCode || '' , warehouseId : value . warehouseId || '' , warehouseName : value . warehouseName || '' ,
billOfLadingNo : value . billOfLadingNo || '' , vesselName : value . vesselName || '' , voyageNo : value . voyageNo || '' ,
estimatedSailingAt : value . estimatedSailingAt || null , estimatedArrivalAt : value . estimatedArrivalAt || null ,
shippingCompanyId : value . shippingCompanyId , shippingCompanyName : value . shippingCompanyName ,
bookingAgentId : value . bookingAgentId , bookingAgentName : value . bookingAgentName ,
departurePortCode : value . departurePortCode , departurePortName : value . departurePortName ,
@@ -223,7 +229,11 @@ async function save() {
actionError . value = null
normalizeDraftData ( )
if ( ! await formRef . value ? . validate ( ) . catch ( ( ) => false ) ) {
actionError . value = { message : '请完善装柜仓库、物流渠道和运输条款等必填信息' }
actionError . value = { message : '请完善装柜仓库、物流渠道、真实运输资料 和运输条款等必填信息' }
return
}
if ( form . data . estimatedArrivalAt ! < form . data . estimatedSailingAt ! ) {
actionError . value = { message : '预计到港时间不能早于预计开船时间' }
return
}
if ( form . data . parties . some ( ( party ) => ! party . partyName . trim ( ) ) ) {
@@ -237,18 +247,15 @@ async function save() {
const payload = { ... form . data }
if ( isNew . value ) {
const created = await createOceanBooking ( { expectedVersion : 0 , data : payload } )
ElMessage . success ( '订舱已创建 ' )
ElMessage . success ( '线下订舱资料已登记并确认 ' )
await router . replace ( ` /ocean-bookings/ ${ created . id } ` )
applyDetail ( created )
} else {
applyDetail ( await updateOceanBooking ( id . value , { expectedVersion : form . expectedVersion , data : payload } ) )
ElMessage . success ( '订舱草稿已保存' )
}
} catch ( cause ) { actionError . value = getApiErrorDetails ( cause ) }
finally { saving . value = false }
}
async function requiredReason ( action : OceanBookingAction ) {
if ( ! [ 'fail' , ' cancel', 'change-fail' ] . includes ( action ) ) return null
if ( ! [ 'cancel' , 'change-fail' ] . includes ( action ) ) return null
const result = await ElMessageBox . prompt ( ` 请填写“ ${ actionLabels [ action ] } ”原因,服务端将写入状态历史。 ` , actionLabels [ action ] , {
inputValidator : ( value ) => Boolean ( value ? . trim ( ) ) || '原因不能为空' ,
confirmButtonText : '确认' , cancelButtonText : '取消' , type : 'warning' ,
@@ -264,31 +271,28 @@ function openChangeDialog() {
changeDialogOpen . value = true
}
function openReleaseDialog ( ) {
releaseForm . billOfLadingNo = detail . value ? . billOfLadingNo || ''
releaseForm . vesselName = detail . value ? . vesselName || ''
releaseForm . voyageNo = detail . value ? . voyageNo || ''
releaseForm . estimatedSailingAt = detail . value ? . estimatedSailingAt || null
releaseForm . estimatedArrivalAt = detail . value ? . estimatedArrivalAt || null
releaseForm . reason = ''
releaseReason . value = ''
releaseDialogOpen . value = true
}
async function submitRelease ( ) {
if ( ! detail . value ) return
const billOfLadingNo = releaseForm . billOfLadingNo . trim ( )
const vesselName = releaseForm . vesselName . trim ( )
const voyageNo = releaseForm . voyageNo . trim ( )
if ( ! billOfLadingNo || ! vesselName || ! voyageNo || ! releaseForm . estimatedSailingAt || ! releaseForm . estimatedArrivalAt ) {
actionError . value = getApiErrorDetails ( new Error ( '请完整填写提单号、船名、航次、预计开船和预计到港时间' ) )
const billOfLadingNo = detail . value . billOfLadingNo ? . trim ( )
const vesselName = detail . value . vesselName ? . trim ( )
const voyageNo = detail . value . voyageNo ? . trim ( )
const estimatedSailingAt = detail . value . estimatedSailingAt
const estimatedArrivalAt = detail . value . estimatedArrivalAt
if ( ! billOfLadingNo || ! vesselName || ! voyageNo || ! estimatedSailingAt || ! estimatedArrivalAt ) {
actionError . value = getApiErrorDetails ( new Error ( '当前订舱缺少已确认运输资料,无法放舱' ) )
return
}
if ( releaseForm . estimatedArrivalAt < releaseForm . estimatedSailingAt ) {
if ( estimatedArrivalAt < estimatedSailingAt ) {
actionError . value = getApiErrorDetails ( new Error ( '预计到港时间不能早于预计开船时间' ) )
return
}
actionSaving . value = true
actionError . value = null
try {
applyDetail ( await releaseOceanBooking ( id . value , { expectedVersion : detail . value . version , billOfLadingNo , vesselName , voyageNo , estimatedSailingAt : releaseForm . estimatedSailingAt , estimatedArrivalAt : releaseForm . estimatedArrivalAt , reason : releaseForm . reason . trim ( ) || null } ) )
applyDetail ( await releaseOceanBooking ( id . value , { expectedVersion : detail . value . version , billOfLadingNo , vesselName , voyageNo , estimatedSailingAt , estimatedArrivalAt , reason : releaseReason . value . trim ( ) || null } ) )
releaseDialogOpen . value = false
ElMessage . success ( '确认放舱成功' )
} catch ( cause ) { actionError . value = getApiErrorDetails ( cause ) }
@@ -344,7 +348,7 @@ onMounted(() => { void load(); void loadOptions() })
< template # header >
< div class = "detail-heading" >
< el-button @click ="router.push('/ocean-bookings')" > < ArrowLeft :size = "16" / > 返回列表 < / el-button >
< PageToolbar class = "booking-page-toolbar" as = "section" : title = "detail?.bookingNo || '订舱资料'" : description = "detail ? `${detail.customerName || '未关联客户'} · ${detail.pol || '—'} → ${detail.pod || '—'}` : '填写并保存 订舱资料'" >
< PageToolbar class = "booking-page-toolbar" as = "section" : title = "detail?.bookingNo || '订舱资料'" : description = "detail ? `${detail.customerName || '未关联客户'} · ${detail.pol || '—'} → ${detail.pod || '—'}` : '登记线下已确认的真实 订舱资料'" >
< template v-if = "detail" #actions><div class="heading-status"><StatusBadge :status="statusLabel(detail.status)" / > < / div > < / template >
< / PageToolbar >
< / div >
@@ -355,10 +359,10 @@ onMounted(() => { void load(); void loadOptions() })
< el -alert v-if = "actionError" type="error" :closable="false" show-icon :title="actionError.message"><ApiErrorMeta :details="actionError" / > < / el-alert >
< section class = "surface-panel" >
< div class = "section-title" >
< div > < h2 > 订舱信息 < / h2 > < p v-if = "detail && !editable" > 当前状态 仅可查看, 草稿状态才允许编辑 。 < / p > < / div >
< div > < h2 > 订舱信息 < / h2 > < p v-if = "detail && !editable" > 已确认订舱资料 仅可查看, 船期变更请使用 “ 申请改船 ” 。 < / p > < / div >
< div class = "actions" >
< el-button v-for = "item in availableActions" :key="item" :loading="actionSaving" @click="runAction(item)" > {{ actionLabels [ item ] }} < / el -button >
< el-button v-if = "canSave" type="primary" :loading="saving" @click="save"><Save :size="16" / > 保存 < / el-button >
< el-button v-if = "canSave" type="primary" :loading="saving" @click="save"><Save :size="16" / > { { isNew ? '登记订舱' : '保存' } } < / el-button >
< / div >
< / div >
< el-alert v-if = "optionsError" type="warning" :closable="false" show-icon :title="optionsError"><el-button link type="warning" :loading="optionsLoading" @click="loadOptions" > 重新加载选项 < / el -button > < / el-alert >
@@ -367,25 +371,25 @@ onMounted(() => { void load(); void loadOptions() })
< h3 > 基本信息 < / h3 > < div class = "booking-group__body booking-group__body--fields" >
< el-form-item label = "订舱号" > < el-input : model -value = " detail ? .bookingNo | | ' 保存后生成 ' " disabled / > < / el-form-item >
< el-form-item label = "客户(可选)" > < el-select :model-value = "form.data.customerId" filterable clearable placeholder = "请选择客户(可选)" no -data -text = " 暂无可选客户 " :loading = "optionsLoading" style = "width:100%" @ update :model-value = "selectCustomer" > < el-option v-if = "form.data.customerId && !selectableCustomers.some((customer) => String(customer.id) === String(form.data.customerId))" :label="form.data.customerName || '客户名称缺失'" :value="String(form.data.customerId)" /><el-option v-for="customer in selectableCustomers" :key="String(customer.id)" :label="customer.tenantName" :value="String(customer.id)" / > < / el-select > < / el-form-item >
< el-form-item label = "状态" > < el-input : model -value = " detail ? statusLabel ( detail.status ) : ' 待创建 ' " disabled / > < / el-form-item >
< el-form-item label = "状态" > < el-input : model -value = " detail ? statusLabel ( detail.status ) : ' 登记后直接确认 ' " disabled / > < / el-form-item >
< el-form-item label = "装柜仓库" prop = "warehouseId" > < el-select :model-value = "form.data.warehouseId" filterable placeholder = "请选择装柜仓库" :loading = "optionsLoading" style = "width:100%" @ update :model-value = "selectWarehouse" > < el-option v-for = "warehouse in warehouses" :key="String(warehouse.id)" :label="warehouse.warehouseName" :value="String(warehouse.id)" / > < / el-select > < / el-form-item >
< el-form-item label = "物流渠道" prop = "channelCode" > < el-select :model-value = "form.data.channelCode" filterable : disabled = "!form.data.warehouseId || channelsLoading || !!channelsError || channelUnavailable" :loading = "channelsLoading" : placeholder = "form.data.warehouseId ? '请选择物流渠道' : '请先选择装柜仓库'" no -data -text = " 该仓库暂无有效物流渠道 " style = "width:100%" @ update : model -value = " selectChannel ( String ( $ event | | ' ' ) ) " > < el-option v-if = "form.data.channelCode && !channels.some((channel) => channel.channelCode === form.data.channelCode)" :label="selectedChannelName || form.data.channelCode" :value="form.data.channelCode" /><el-option v-for="channel in channels" :key="channel.channelCode" :label="channel.channelName" :value="channel.channelCode"><span>{{ channel.channelName }}</span><small class="channel-code">{{ channel.channelCode }}</small></el-option></el-select><div v-if="channelsError" class="field-error"><span>{{ channelsError }}</span><el-button link type="primary" :loading="channelsLoading" @click="loadChannels(form.data.warehouseId)">重新加载</el-button></div><div v-else-if="channelUnavailable" class="field-hint" > 该仓库暂无有效物流渠道 , 请更换仓库或联系管理员 。 < / div > < / el -form -item >
< el-form-item label = "提单号" > < el-input : model -value = " detail ? .billOfLadingNo | | ' ' " disabled placeholder = "确认放舱时录入" / > < / el-form-item >
< el-form-item label = "提单号" prop = "billOfLadingNo" > < el-input v-model = "form.data.billOfLadingNo" maxlength="128" placeholder="请输入提单号" / > < / el-form-item >
< el-form-item label = "船公司" > < el-select :model-value = "form.data.shippingCompanyId" filterable clearable placeholder = "请选择船公司" :loading = "optionsLoading" style = "width:100%" @ update : model -value = " selectPartner ( ' shippingCompany ' , String ( $ event | | ' ' ) ) " > < el-option v-if = "form.data.shippingCompanyId && !shippingCompanies.some((partner) => String(partner.id) === String(form.data.shippingCompanyId))" :label="form.data.shippingCompanyName || '船公司名称缺失'" :value="String(form.data.shippingCompanyId)" /><el-option v-for="partner in shippingCompanies" :key="String(partner.id)" :label="partner.partnerName" :value="String(partner.id)" / > < / el-select > < / el-form-item >
< el-form-item label = "订舱代理" > < el-select :model-value = "form.data.bookingAgentId" filterable clearable placeholder = "请选择订舱代理" :loading = "optionsLoading" style = "width:100%" @ update : model -value = " selectPartner ( ' bookingAgent ' , String ( $ event | | ' ' ) ) " > < el-option v-if = "form.data.bookingAgentId && !bookingAgents.some((partner) => String(partner.id) === String(form.data.bookingAgentId))" :label="form.data.bookingAgentName || '订舱代理名称缺失'" :value="String(form.data.bookingAgentId)" /><el-option v-for="partner in bookingAgents" :key="String(partner.id)" :label="partner.partnerName" :value="String(partner.id)" / > < / el-select > < / el-form-item >
< / div >
< / section >
< section class = "booking-group" aria -label = " 航线船期 " >
< h3 > 航线船期 < / h3 > < div class = "booking-group__body booking-group__body--fields" >
< el-form-item label = "船名" > < el-input : model -value = " detail ? .vesselName | | ' ' " disabled placeholder = "确认放舱时录入" / > < / el-form-item >
< el-form-item label = "航次" > < el-input : model -value = " detail ? .voyageNo | | ' ' " disabled placeholder = "确认放舱时录入" / > < / el-form-item >
< el-form-item label = "船名" prop = "vesselName" > < el-input v-model = "form.data.vesselName" maxlength="128" placeholder="请输入船名" / > < / el-form-item >
< el-form-item label = "航次" prop = "voyageNo" > < el-input v-model = "form.data.voyageNo" maxlength="64" placeholder="请输入航次" / > < / el-form-item >
< el-form-item label = "起运港代码" > < el-input v-model = "form.data.departurePortCode" placeholder="例如:CNNGB" / > < / el-form-item >
< el-form-item label = "起运港" > < el-input v-model = "form.data.departurePortName" placeholder="例如:宁波港" / > < / el-form-item >
< el-form-item label = "目的港代码" > < el-input v-model = "form.data.destinationPortCode" placeholder="例如:USLAX" / > < / el-form-item >
< el-form-item label = "目的港" > < el-input v-model = "form.data.destinationPortName" placeholder="例如:洛杉矶港" / > < / el-form-item >
< el-form-item class = "booking-field--wide" label = "交货地点" > < el-input v-model = "form.data.placeOfDelivery" placeholder="请输入最终交货地点" / > < / el-form-item >
< el-form-item label = "预计开船" > < el-date-picker : model -value = " detail ? .estimatedSailingAt | | null " type = "datetime" value -format = " YYYY -MM -DDTHH : mm : ss " disabled placeholder = "确认放舱时录入" style = "width:100%" / > < / el-form-item >
< el-form-item label = "预计到港" > < el-date-picker : model -value = " detail ? .estimatedArrivalAt | | null " type = "datetime" value -format = " YYYY -MM -DDTHH : mm : ss " disabled placeholder = "确认放舱时录入" style = "width:100%" / > < / el-form-item >
< el-form-item label = "预计开船" prop = "estimatedSailingAt" > < el-date-picker v-model = "form.data.estimatedSailingAt" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择预计开船时间" style= "width:100%" / > < / el-form-item >
< el-form-item label = "预计到港" prop = "estimatedArrivalAt" > < el-date-picker v-model = "form.data.estimatedArrivalAt" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择预计到港时间" style= "width:100%" / > < / el-form-item >
< / div >
< / section >
< section class = "booking-group" aria -label = " 条款与柜量 " >
@@ -459,14 +463,17 @@ onMounted(() => { void load(); void loadOptions() })
< / el-form >
< LoadErrorState v-if = "actionError" compact title="提交失败" :message="actionError.message" :show-retry="false"><template #details><ApiErrorMeta :details="actionError" / > < / template > < / LoadErrorState >
< / AppDialog >
< AppDialog v-model = "releaseDialogOpen" title="确认放舱" width="min(560px, 94vw)" :confirm-loading="actionSaving" :confirm-disabled="!releaseForm.billOfLadingNo.trim() || !releaseForm.vesselName.trim() || !releaseForm.voyageNo.trim() || !releaseForm.estimatedSailingAt || !releaseForm.estimatedArrivalAt" confirm-text="确认放舱" cancel-text="取消" @confirm="submitRelease" >
< AppDialog v-model = "releaseDialogOpen" title="确认放舱" width="min(560px, 94vw)" :confirm-loading="actionSaving" confirm-text="确认放舱" cancel-text="取消" @confirm="submitRelease" >
< el -form label -position = " top " >
< el-form-item label = "提单号" required > < el-input v-model = "releaseForm.billOfLadingNo" maxlength="128" placeholder="请输入提单号" / > < / el-form-item >
< el-form-item label = "船名" required > < el-input v-model = "releaseForm.vesselName" maxlength="128" placeholder="请输入船名" / > < / el-form-item >
< el-form-item label = "航次" required > < el-input v-model = "releaseForm.voyageNo" maxlength="64" placeholder="请输入航次" / > < / el-form-item >
< el-form-item label = "预计开船" required > < el-date-picker v-model = "releaseForm.estimatedSailingAt" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择预计开船时间" style="width:100%" / > < / el-form-item >
< el-form-item label = "预计到港" required > < el-date-picker v-model = "releaseForm.estimatedArrivalAt" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择预计到港时间" style="width:100%" / > < / el-form-item >
< el-form-item label = "备注 " > < el-input v-model = "releaseForm.reason" type="textarea" maxlength="1000" placeholder="请输入放舱备注(可选)" / > < / el-form-item >
< el-alert title = "请复核已登记的真实订舱资料。放舱不会重新采集或修改这些信息。" type = "info" :closable = "false" show -icon / >
< el-descriptions class = "release-summary" :column = "2" border >
< el-descriptions-item label = "提单号" > { { detail ? . billOfLadingNo || '—' } } < / el-descriptions-item >
< el-descriptions-item label = "船名" > { { detail ? . vesselName || '—' } } < / el-descriptions-item >
< el-descriptions-item label = "航次" > { { detail ? . voyageNo || '—' } } < / el-descriptions-item >
< el-descriptions-item label = "预计开船 " > { { detail ? . estimatedSailingAt || '—' } } < / el-descriptions-item >
< el-descriptions-item label = "预计到港" > { { detail ? . estimatedArrivalAt || '—' } } < / el-descriptions-item >
< / el-descriptions >
< el-form-item label = "备注" > < el-input v-model = "releaseReason" type="textarea" maxlength="1000" placeholder="请输入放舱备注(可选)" / > < / el-form-item >
< / el-form >
< LoadErrorState v-if = "actionError" compact title="放舱失败" :message="actionError.message" :show-retry="false"><template #details><ApiErrorMeta :details="actionError" / > < / template > < / LoadErrorState >
< / AppDialog >