@@ -19,6 +19,7 @@ import {
createIdempotencyKey ,
createMerchantOrderAddress ,
disableMerchantOrderAddress ,
downloadMerchantFirstLegBatchImportErrorReport ,
downloadMerchantFirstLegBatchImportTemplate ,
downloadMerchantOrderCartonLabelsBatch ,
downloadMerchantProductImage ,
@@ -101,7 +102,9 @@ const batchImportFile = ref<File | null>(null)
const batchImportIdempotencyKey = ref ( '' )
const batchImportSubmitting = ref ( false )
const batchTemplateDownloading = ref ( false )
const batchErrorReportDownloading = ref ( false )
const batchImportResult = ref < MerchantBatchImportResponse | null > ( null )
const batchImportError = ref ( '' )
const fixedOrderColumns : DataTableColumn [ ] = [
{ key : 'selection' , type : 'selection' , width : 48 , fixed : 'left' , reserveSelection : true } ,
{ key : 'actions' , label : '操作' , width : 216 , fixed : 'left' , align : 'center' , headerAlign : 'center' , slotName : 'actions' } ,
@@ -416,6 +419,9 @@ function quickOrderDraft() {
const form = reactive < FirstLegOrderDraft > ( quickOrderDraft ( ) )
const serializedDraft = computed ( ( ) => JSON . stringify ( form ) )
const editorDirty = computed ( ( ) => ( editorOpen . value || isCreatePage . value ) && serializedDraft . value !== editorBaseline . value )
const woodenCargoLines = computed ( ( ) => form . items
. map ( ( item , index ) => item . woodenProduct ? index + 1 : null )
. filter ( ( index ) : index is number => index != null ) )
const unsavedRouteKey = props . createPage ? 'first-leg-order-create' : 'first-leg-orders'
useUnsavedWorkRegistration ( {
@@ -608,6 +614,16 @@ function errorMessage(error: unknown) {
return error instanceof Error ? error . message : '服务暂时不可用'
}
function batchImportErrorMessage ( error : unknown ) {
if ( ! ( error instanceof ApiError ) ) return errorMessage ( error )
return [
error . message ,
error . code ? ` 错误码 ${ error . code } ` : '' ,
error . requestId ? ` requestId ${ error . requestId } ` : '' ,
error . traceId ? ` traceId ${ error . traceId } ` : '' ,
] . filter ( Boolean ) . join ( ' · ' )
}
function getOrderCargoSummary ( order : FirstLegOrder ) {
if ( order . items . length ) return ` ${ order . items [ 0 ] ? . cargoNameCn || '货物' } ${ order . items . length > 1 ? ` 等 ${ order . items . length } 项 ` : '' } `
if ( order . businessSummary . totalPackageCount != null ) return ` ${ order . businessSummary . totalPackageCount } 箱 `
@@ -1467,6 +1483,7 @@ function openBatchImport() {
batchImportFile . value = null
batchImportIdempotencyKey . value = ''
batchImportResult . value = null
batchImportError . value = ''
batchImportOpen . value = true
}
@@ -1476,52 +1493,83 @@ function selectBatchImportFile(event: Event) {
input . value = ''
if ( ! file ) return
if ( ! file . name . toLowerCase ( ) . endsWith ( '.xlsx' ) ) {
ElMessage . error ( '仅支持 .xlsx 格式的订单导入文件' )
batchImportError . value = '仅支持 .xlsx 格式的订单导入文件'
ElMessage . error ( batchImportError . value )
return
}
if ( file . size > 5 * 1024 * 1024 ) {
ElMessage . error ( '订单导入文件不能超过 5MB' )
batchImportError . value = '订单导入文件不能超过 5MB'
ElMessage . error ( batchImportError . value )
return
}
batchImportFile . value = file
batchImportIdempotencyKey . value = createIdempotencyKey ( 'first-leg-batch-import' )
batchImportResult . value = null
batchImportError . value = ''
}
async function downloadBatchTemplate ( ) {
try {
batchTemplateDownloading . value = true
batchImportError . value = ''
saveBlob ( await downloadMerchantFirstLegBatchImportTemplate ( ) , '头程订单批量导入模板.xlsx' )
} catch ( error ) {
ElMessage . error ( errorMessage ( error ) )
batchImportError . value = batchImportErrorMessage ( error )
ElMessage . error ( batchImportError . value )
} finally {
batchTemplateDownloading . value = false
}
}
async function downloadBatchErrorReport ( ) {
const result = batchImportResult . value
if ( ! result ? . errorReportFileId ) return
try {
batchErrorReportDownloading . value = true
batchImportError . value = ''
saveBlob (
await downloadMerchantFirstLegBatchImportErrorReport ( result . batchNo , result . errorReportFileId ) ,
` ${ result . batchNo } -错误报告.xlsx ` ,
)
} catch ( error ) {
batchImportError . value = batchImportErrorMessage ( error )
ElMessage . error ( batchImportError . value )
} finally {
batchErrorReportDownloading . value = false
}
}
async function submitBatchImport ( ) {
if ( ! batchImportFile . value ) {
ElMessage . warning ( '请先选择 .xlsx 订单导入文件' )
batchImportError . value = '请先选择 .xlsx 订单导入文件'
ElMessage . warning ( batchImportError . value )
return
}
try {
batchImportSubmitting . value = true
batchImportError . value = ''
batchImportResult . value = await importMerchantFirstLegOrders (
batchImportFile . value ,
batchImportMode . value ,
batchImportIdempotencyKey . value ,
)
const result = batchImportResult . value . result
if ( result . succeeded ) await store . load ( { includeDrafts : true } )
if ( result . succeeded && auth . hasPermission ( 'order:first-leg:list' ) ) await store . load ( { includeDrafts : true } )
if ( result . failed ) ElMessage . warning ( ` 导入完成:成功 ${ result . succeeded } 条,失败 ${ result . failed } 条 ` )
else ElMessage . success ( ` 已成功导入 ${ result . succeeded } 条订单草稿 ` )
} catch ( error ) {
ElMessage . error ( errorMessage ( error ) )
batchImportError . value = batchImportErrorMessage ( error )
ElMessage . error ( batchImportError . value )
} finally {
batchImportSubmitting . value = false
}
}
function viewImportedDrafts ( ) {
batchImportOpen . value = false
void router . push ( { name : 'first-leg-orders' } )
}
async function validateEditorForm ( ) {
try {
const policy = await getMerchantInboundCodePolicy ( )
@@ -1800,6 +1848,17 @@ async function searchOrders(model: QueryModel) {
< / div >
< / template >
< section v-if = "isCreatePage && !routeDraftId && canCreateOrder" class="create-batch-import-entry" aria-label="Excel 批量下单" >
< div >
< strong > 需要一次创建多条订单 ? < / strong >
< span > 下载正式 Excel 模板 , 填写后上传 ; 导入成功的数据将创建为订单草稿 , 不会直接提交审核 。 < / span >
< / div >
< div class = "create-batch-import-actions" >
< el-button :loading = "batchTemplateDownloading" @click ="downloadBatchTemplate" > < Download :size = "16" / > 下载 Excel 模板 < / el-button >
< el-button type = "primary" @click ="openBatchImport" > < Upload :size = "16" / > Excel 批量导入 < / el-button >
< / div >
< / section >
< div v-if = "isCreatePage && supportingDataErrors.length" class="supporting-data-error" role="alert" >
< AlertTriangle :size = "20" / >
< div > < strong > 下单配套数据加载失败 < / strong > < span v-for = "message in supportingDataErrors" :key="message" > {{ message }} < / span > < / div >
@@ -1841,7 +1900,7 @@ async function searchOrders(model: QueryModel) {
< / el -form -item >
< el-form-item label = "买单 / 退税" prop = "taxMode" required > < el-radio-group v-model = "form.taxMode"><el-radio value="BUY_DECLARATION">买单</el-radio><el-radio value="TAX_REFUND" > 退税 < / el -radio > < / el-radio-group > < / el-form-item >
< el-form-item label = "是否整柜" prop = "transportMode" required > < el-radio-group v-model = "form.transportMode"><el-radio value="FCL">整柜</el-radio><el-radio value="LCL" > 散货 < / el -radio > < / el-radio-group > < / el-form-item >
< el-form-item label = "是否商检" prop = "customsInspection" > < el-select v-model = "form.customsInspection" clearable placeholder="暂不选择"><el-option label="是" :value="true" /><el-option label="否" :value="false" /></el-select><small v-if="form.items.some((item) => item.woodenProduct) && form.customsInspection !== true" class="field-guidance field-guidance--error" > 货物包含木制品 , 提交前 必须选择“ 是 ” 。 < / small > < / el -form -item >
< el-form-item label = "是否商检" prop = "customsInspection" > < el-select v-model = "form.customsInspection" clearable placeholder="暂不选择"><el-option label="是" :value="true" /><el-option label="否" :value="false" /></el-select><small v-if="woodenCargoLines.length && form.customsInspection !== true" class="field-guidance field-guidance--error" > 第 {{ woodenCargoLines.join ( ' 、 ' ) }} 条货物已勾选木制品 , 提交前 “ 是否商检 ” 必须选择 “ 是 ” 。 < / small > < / el -form -item >
< el-form-item label = "期望出货日期" > < el-date-picker v-model = "form.expectedDepartureDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择期望出货日期" / > < / el-form-item >
< el-form-item label = "订单备注" > < el-input v-model = "form.customerRemark" type="textarea" :rows="3" resize="vertical" maxlength="300" show-word-limit placeholder="选填,例如:入仓前请电话联系" / > < / el-form-item >
< / div >
@@ -1874,16 +1933,16 @@ async function searchOrders(model: QueryModel) {
< section class = "order-form-section prototype-section cargo-section" >
< div class = "order-section-heading cargo-table-heading" > < div > < h3 > 货物明细 < / h3 > < / div > < / div >
< div class = "cargo-table-toolbar" >
< span class = "cargo-table-hint" > 可逐行维护货物明细 ; 批量订单请返回订单列表使用正式 Excel 导入 。 < / span >
< span class = "cargo-table-hint" > 可逐行维护货物明细 ; 一次创建多条订单草稿请使用页面上方 “ Excel 批量 导入” 。 < / span >
< div class = "cargo-table-actions" > < el-button @click ="addCargoItem" > < Plus :size = "16" / > 添加行 < / el-button > < el-button type = "danger" plain :disabled = "!selectedCargoIds.length" @click ="removeSelectedCargoItems" > < Trash2 :size = "15" / > 删除所选行 < / el-button > < / div >
< / div >
< div class = "cargo-table-wrap" >
< table class = "cargo-edit-table" >
< thead > < tr >
< th class = "cargo-check-cell" > < el-checkbox :model-value = "allCargoSelected" :indeterminate = "someCargoSelected" aria -label = " 全选货物 " @change ="toggleAllCargoItems" / > < / th > < th class = "cargo-index-cell" > # < / th >
< th > SKU NO . < small > SKU必填 < / small > < / th > < th > 中文品名 < / th > < th class = "cargo-image-column" > 图片 < / th > < th > 英文品名 < / th > < th > 报关 HS CODE < / th > < th > 清关 HS CODE < / th > < th > 包装类型 < / th >
< th > SKU NO . < small > SKU必填 < / small > < / th > < th > 中文品名 < / th > < th class = "cargo-image-column" > 图片 < / th > < th > 英文品名 < / th > < th > 报关 HS CODE < / th > < th > 清关 HS CODE < / th > < th > 包装类型 < / th > < th > 材质 < / th > < th class = "cargo-switch-column" > 危险品 < / th > < th class = "cargo-switch-column" > 木制品 < small > 勾选后须商检 < / small > < / th >
< th class = "cargo-number-column" > 箱规长 cm < / th > < th class = "cargo-number-column" > 箱规宽 cm < / th > < th class = "cargo-number-column" > 箱规高 cm < / th > < th class = "cargo-number-column" > 每箱数量 < / th > < th class = "cargo-number-column" > 箱数 < / th > < th class = "cargo-number-column" > 总毛重 < / th > < th class = "cargo-number-column" > 总体积 < / th > < th class = "cargo-number-column" > 总数量 < / th > < th class = "cargo-number-column" > 总净重 < / th >
< th > 清关申报单价 USD < / th > < th > 清关申报金额 USD < / th > < th > 材质 < / th > < th > 用途 < / th > < th > 型号 < / th > < th > 品牌 < / th > < th class = "cargo-dimensions-column" > 产品规格 长 × 宽 × 高 < / th > < th > 危险品 < / th > < th > 木制品 < / th > < th > 货件编号 < small > 货件编号必填 < / small > < / th > < th > 跟踪编号 < small > 跟踪编号必填 < / small > < / th > < th > UPC NO . < / th > < th > 备注 < / th > < th class = "cargo-operation-column" > 操作 < / th >
< th > 清关申报单价 USD < / th > < th > 清关申报金额 USD < / th > < th > 用途 < / th > < th > 型号 < / th > < th > 品牌 < / th > < th class = "cargo-dimensions-column" > 产品规格 长 × 宽 × 高 < / th > < th > 货件编号 < small > 货件编号必填 < / small > < / th > < th > 跟踪编号 < small > 跟踪编号必填 < / small > < / th > < th > UPC NO . < / th > < th > 备注 < / th > < th class = "cargo-operation-column" > 操作 < / th >
< / tr > < / thead >
< tbody > < tr v-for = "(item, index) in form.items" :key="item.id" >
< td class = "cargo-check-cell" > < el-checkbox :model-value = "selectedCargoIds.includes(item.id)" : aria -label = " ` 选择货物 $ { index + 1 } ` " @change ="toggleCargoItem(item.id, $event)" / > < / td > < td class = "cargo-index-cell" > { { index + 1 } } < / td >
@@ -1891,11 +1950,12 @@ async function searchOrders(model: QueryModel) {
< td > < el-form-item :prop = "`items.${index}.cargoNameCn`" required :rules = "requiredTextRules('请输入中文品名')" > < el-input v-model = "item.cargoNameCn" maxlength="128" placeholder="中文品名" / > < / el-form-item > < / td >
< td class = "cargo-image-column" > < div class = "cargo-table-image" > < img v-if = "item.productImagePreviewUrl" :src="item.productImagePreviewUrl" :alt="`${item.cargoNameCn || `货物 ${index + 1}`}产品图片`" /><el-upload v-else :show-file-list="false" accept=".jpg,.jpeg,.png,image/jpeg,image/png" :disabled="item.productImageUploading" :http-request="(options: UploadRequestOptions) => uploadCargoProductImage(item, options)"><el-button text :loading="item.productImageUploading" aria-label="上传图片"><ImagePlus :size="17" /></el-button></el-upload><el-button v-if="item.productImagePreviewUrl" class="cargo-image-remove" text type="danger" aria-label="移除图片" @click="removeCargoProductImage(item)"><Trash2 :size="12" / > < / el-button > < / div > < / td >
< td > < el-input v-model = "item.cargoNameEn" maxlength="128" placeholder="English Name" /></td><td><el-input v-model="item.hsCode" maxlength="20" placeholder="HS Code" /></td><td><el-input v-model="item.customsHsCode" maxlength="20" placeholder="HS Code" / > < / td >
< td > < el-form-item :prop = "`items.${index}.packageType`" required :rules = "requiredSelectRules('请选择包装类型')" > < el-select v-model = "item.packageType"><el-option label="纸箱" value="CARTON" /><el-option label="托盘" value="PALLET" /><el-option label="木箱" value="WOODEN_CASE" / > < / el-select > < / el-form-item > < / td >
< td > < el-form-item :prop = "`items.${index}.packageType`" required :rules = "requiredSelectRules('请选择包装类型')" > < el-select v-model = "item.packageType"><el-option label="纸箱" value="CARTON" /><el-option label="托盘" value="PALLET" /><el-option label="木箱" value="WOODEN_CASE" /></el-select></el-form-item></td><td><el-input v-model="item.material" maxlength="128" placeholder="材质" / > < / td >
< td class = "cargo-switch-column" > < el-switch v-model = "item.dangerousGoods" :aria-label="`第 ${index + 1} 条货物是否危险品`" /></td><td class="cargo-switch-column"><div class="cargo-switch-cell"><el-switch v-model="item.woodenProduct" :aria-label="`第 ${index + 1} 条货物是否木制品`" /><small v-if="item.woodenProduct && form.customsInspection !== true" > 第 {{ index + 1 }} 条须商检 < / small > < / div > < / td >
< td > < el-input-number v-model = "item.lengthCm" :min="0" :precision="2" :controls="false" /></td><td><el-input-number v-model="item.widthCm" :min="0" :precision="2" :controls="false" /></td><td><el-input-number v-model="item.heightCm" :min="0" :precision="2" :controls="false" / > < / td >
< td > < el-form-item :prop = "`items.${index}.unitsPerPackage`" required : rules = "positiveNumberRules('每箱数量必须大于 0', true)" > < el-input-number v-model = "item.unitsPerPackage" :min="1" :controls="false" /></el-form-item></td><td><el-form-item :prop="`items.${index}.packageCount`" required :rules="positiveNumberRules('箱数必须大于 0', true)"><el-input-number v-model="item.packageCount" :min="1" :controls="false" /></el-form-item></td><td><el-form-item :prop="`items.${index}.grossWeightKg`" required :rules="positiveNumberRules('总毛重必须大于 0')"><el-input-number v-model="item.grossWeightKg" :min="0" :precision="3" :controls="false" /></el-form-item></td><td><el-form-item :prop="`items.${index}.volumeCbm`" required :rules="positiveNumberRules('总体积必须大于 0')"><el-input-number v-model="item.volumeCbm" :min="0" :precision="4" :controls="false" /></el-form-item></td><td><el-form-item :prop="`items.${index}.pieces`" required :rules="positiveNumberRules('总数量必须大于 0', true)"><el-input-number v-model="item.pieces" :min="1" :controls="false" /></el-form-item></td><td><el-input-number v-model="item.netWeightKg" :min="0" :precision="3" :controls="false" / > < / td >
< td > < el-input-number v-model = "item.unitDeclaredValue" :min="0" :precision="2" :controls="false" /></td><td><el-input-number v-model="item.declaredValue" :min="0" :precision="2" :controls="false" /></td><td><el-input v-model="item.material" maxlength="128" placeholder="材质" /></td><td><el-input v-model="item.usage" maxlength="128" placeholder="用途" /></td><td><el-input v-model="item.model" maxlength="64" placeholder="型号" /></td><td><el-input v-model="item.brand" maxlength="64" placeholder="品牌" / > < / td >
< td class = "cargo-dimensions-column" > < div class = "cargo-table-dimensions" > < el-input-number v-model = "item.productLengthCm" :min="0" :precision="2" :controls="false" /><span>× </span><el-input-number v-model="item.productWidthCm" :min="0" :precision="2" :controls="false" /><span>× </span><el-input-number v-model="item.productHeightCm" :min="0" :precision="2" :controls="false" /></div></td><td class="cargo-danger-cell"><el-switch v-model="item.dangerousGoods" /></td><td class="cargo-danger-cell"><el-switch v-model="item.woodenProduct" / > < / td >
< td > < el-input-number v-model = "item.unitDeclaredValue" :min="0" :precision="2" :controls="false" /></td><td><el-input-number v-model="item.declaredValue" :min="0" :precision="2" :controls="false" /></td><td><el-input v-model="item.usage" maxlength="128" placeholder="用途" /></td><td><el-input v-model="item.model" maxlength="64" placeholder="型号" /></td><td><el-input v-model="item.brand" maxlength="64" placeholder="品牌" / > < / td >
< td class = "cargo-dimensions-column" > < div class = "cargo-table-dimensions" > < el-input-number v-model = "item.productLengthCm" :min="0" :precision="2" :controls="false" /><span>× </span><el-input-number v-model="item.productWidthCm" :min="0" :precision="2" :controls="false" /><span>× </span><el-input-number v-model="item.productHeightCm" :min="0" :precision="2" :controls="false" / > < / div > < / td >
< td > < el-form-item :prop = "`items.${index}.shipmentId`" : required = "form.destinationType === 'FBA'" : rules = "form.destinationType === 'FBA' ? requiredTextRules('FBA 货物请输入货件编号') : undefined" > < el-input v-model = "item.shipmentId" maxlength="64" placeholder="货件编号" /></el-form-item></td><td><el-form-item :prop="`items.${index}.referenceId`" :required="form.destinationType === 'FBA'" :rules="form.destinationType === 'FBA' ? requiredTextRules('FBA 货物请输入跟踪编号') : undefined"><el-input v-model="item.referenceId" maxlength="64" placeholder="跟踪编号" /></el-form-item></td><td><el-form-item :prop="`items.${index}.upc`"><el-input v-model="item.upc" maxlength="64" placeholder="UPC NO.(选填)" /></el-form-item></td><td><el-input v-model="item.remark" maxlength="500" placeholder="备注" /></td><td class="cargo-operation-column"><el-button link type="danger" :aria-label="`删除货物 ${index + 1}`" @click="removeCargoItem(index)"><Trash2 :size="15" / > 删除 < / el-button > < / td >
< / tr > < / tbody >
< / table >
@@ -1941,9 +2001,10 @@ async function searchOrders(model: QueryModel) {
< template # footer > < div class = "editor-actions" > < el-button :disabled = "saving" @click ="closeEditor" > 返回 < / el -button > < el-button v-if = "editingId ? canEditOrder : canCreateOrder" :loading="saving" @click="saveDraft">保存草稿</el-button><el-button v-if="auth.hasPermission('order:first-leg:submit')" type="primary" :loading="saving" @click="submitOrder"><Send :size="16" / > { { companySubmissionBlocked ? '提交前需完成资料审核' : '提交订单' } } < / el-button > < / div > < / template >
< / FirstLegOrderEditorShell >
< el-dialog v-model = "batchImportOpen" title="批量导入头程订单 " width="680px" destroy -on -close append -to -body >
< el-dialog v-model = "batchImportOpen" title="Excel 批量创建订单草稿 " width="680px" destroy -on -close append -to -body >
< div class = "batch-import-dialog" >
< el-alert title = "使用服务端正式模板导入 " description = "仅支持 .xlsx,文件不超过 5MB、最多 50 行。 导入结果按本企业权限和数据范围返回 。" type = "info" :closable = "false" show -icon / >
< el-alert title = "使用服务端正式模板创建订单草稿 " description = "仅支持 .xlsx,文件不超过 5MB、最多 50 行; 导入成功的数据只创建草稿,不会直接提交审核。模板包含 cargo.woodenProduct( true/false);任一货物填写 true 时,customsInspection 必须填写 true 。" type = "info" :closable = "false" show -icon / >
< el-alert v-if = "batchImportError" title="批量导入处理失败" :description="batchImportError" type="error" :closable="false" show -icon / >
< div class = "batch-import-actions" >
< input ref = "batchImportInput" class = "cargo-import-input" type = "file" accept = ".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" @change ="selectBatchImportFile" / >
< el-button :loading = "batchTemplateDownloading" @click ="downloadBatchTemplate" > < Download :size = "16" / > 下载正式模板 < / el-button >
@@ -1956,7 +2017,10 @@ async function searchOrders(model: QueryModel) {
< / el-radio-group >
< section v-if = "batchImportResult" class="batch-import-result" aria-live="polite" >
< strong > 批次 { { batchImportResult . batchNo } } < / strong >
< span > 共 { { batchImportResult . result . total } } 条 , 成功 { { batchImportResult . result . succeeded } } 条 , 失败 { { batchImportResult . result . failed } } 条 < / span >
< span > 共 { { batchImportResult . result . total } } 条 , 已创建草稿 { { batchImportResult . result . succeeded } } 条 , 失败 { { batchImportResult . result . failed } } 条 < / span >
< div v-if = "batchImportResult.errorReportFileId" class="batch-import-result-actions" >
< el -button type = "danger" plain :loading = "batchErrorReportDownloading" @click ="downloadBatchErrorReport" > < Download :size = "16" / > 下载 Excel 错误报告 < / el-button >
< / div >
< ul v-if = "batchImportResult.result.failed" >
< li v-for = "item in batchImportResult.result.items.filter((row) => !row.success)" :key="`${item.index}-${item.clientRowId}`" >
第 {{ item.index + 1 }} 行 ( {{ item.clientRowId | | ' 未填写行标识 ' }} ) : {{ item.errorMessage | | item.errorCode | | ' 导入失败 ' }}
@@ -1964,7 +2028,7 @@ async function searchOrders(model: QueryModel) {
< / ul >
< / section >
< / div >
< template # footer > < div class = "editor-actions" > < el-button :disabled = "batchImportSubmitting" @click ="batchImportOpen = false" > 关闭 < / el -button > < el-button type = "primary" :loading = "batchImportSubmitting" :disabled = "!batchImportFile" @click ="submitBatchImport" > 开始导入 < / el -button > < / div > < / template >
< template # footer > < div class = "editor-actions" > < el-button v-if = "batchImportResult?.result.succeeded && auth.hasPermission('order:first-leg:list')" :disabled="batchImportSubmitting" @click="viewImportedDrafts">查看订单草稿</el-button><el-button :disabled= "batchImportSubmitting" @click="batchImportOpen = false">关闭</el-button><el-button type= "primary" :loading= "batchImportSubmitting" :disabled= "!batchImportFile" @click="submitBatchImport"> 开始导入并创建草稿 < / el -button > < / div > < / template >
< / el-dialog >
< el-dialog v-model = "detailOpen" class="first-leg-detail-dialog" title="订单详情" width="94vw" top="3vh" destroy -on -close append -to -body >
@@ -2142,6 +2206,7 @@ async function searchOrders(model: QueryModel) {
. batch - import - dialog : deep ( . el - radio - group ) { display : flex ; align - items : flex - start ; flex - direction : column ; gap : 8 px ; }
. batch - import - result { display : flex ; flex - direction : column ; gap : 6 px ; padding : 14 px ; border : 1 px solid var ( -- border ) ; background : # f8fafc ; }
. batch - import - result > span , . batch - import - result li { color : var ( -- muted ) ; font - size : 12 px ; line - height : 1.55 ; }
. batch - import - result - actions { display : flex ; margin - top : 4 px ; }
. batch - import - result ul { max - height : 180 px ; margin : 4 px 0 0 ; padding - left : 20 px ; overflow - y : auto ; }
. column - settings - trigger { width : 40 px ; min - width : 40 px ; height : 36 px ; padding : 0 ; color : # 53627 b ; }
: global ( . order - column - popper . el - popover ) { padding : 0 ; border - color : # e3e8f0 ; border - radius : 4 px ; box - shadow : 0 8 px 24 px rgba ( 29 , 45 , 74 , .12 ) ; }
@@ -2196,6 +2261,12 @@ async function searchOrders(model: QueryModel) {
. is - create - route { gap : 0 ; }
. is - create - route . first - leg - editor - page { width : 100 % ; }
. is - create - route . supporting - data - error , . is - create - route > . el - alert { margin - bottom : 14 px ; }
. create - batch - import - entry { display : flex ; align - items : center ; justify - content : space - between ; gap : 18 px ; margin - bottom : 14 px ; padding : 14 px 18 px ; border : 1 px solid # d9e2ff ; border - radius : 8 px ; background : # f7f9ff ; }
. create - batch - import - entry > div : first - child { display : flex ; min - width : 0 ; flex - direction : column ; gap : 4 px ; }
. create - batch - import - entry strong { color : var ( -- navy ) ; font - size : 14 px ; }
. create - batch - import - entry span { color : var ( -- muted ) ; font - size : 12 px ; line - height : 1.5 ; }
. create - batch - import - actions { display : flex ; flex : 0 0 auto ; gap : 8 px ; }
. create - batch - import - actions : deep ( . el - button ) { min - height : 38 px ; margin - left : 0 ; }
. is - create - route . prototype - order - form { width : 100 % ; margin : 0 ; padding : 0 0 16 px ; }
. is - create - route . prototype - section : not ( . cargo - section ) : not ( . cost - estimate - section ) : not ( . destination - section ) { width : min ( 100 % , 900 px ) ; }
. order - form { width : min ( 100 % , 1000 px ) ; margin : 0 ; padding : 0 0 12 px ; }
@@ -2313,7 +2384,9 @@ async function searchOrders(model: QueryModel) {
. cargo - number - column { width : 92 px ! important ; }
. cargo - dimensions - column { width : 240 px ! important ; }
. cargo - operation - column { width : 76 px ! important ; text - align : center ! important ; }
. cargo - danger - cell { text - align : center ; vertical - align : middle ! important ; }
. cargo - switch - column { width : 112 px ! important ; text - align : center ! important ; vertical - align : middle ! important ; }
. cargo - switch - cell { display : flex ; align - items : center ; justify - content : center ; flex - direction : column ; gap : 4 px ; }
. cargo - switch - cell small { color : # b54708 ; font - size : 9 px ; line - height : 1.25 ; white - space : normal ; }
. cargo - table - image { position : relative ; display : flex ; width : 34 px ; height : 34 px ; align - items : center ; justify - content : center ; margin : 0 auto ; border : 1 px dashed # d6dce5 ; border - radius : 4 px ; color : # 788493 ; }
. cargo - table - image img { width : 100 % ; height : 100 % ; border - radius : 3 px ; object - fit : contain ; }
. cargo - table - image : deep ( . el - button ) { width : 30 px ; height : 30 px ; padding : 0 ; }
@@ -2421,6 +2494,9 @@ async function searchOrders(model: QueryModel) {
. product - image - field { grid - row : auto ; }
}
@ media ( max - width : 720 px ) {
. create - batch - import - entry { align - items : stretch ; flex - direction : column ; }
. create - batch - import - actions { flex - direction : column ; }
. create - batch - import - actions : deep ( . el - button ) { width : 100 % ; }
. first - leg - query - toolbar { padding : 12 px ; }
. first - leg - query - toolbar : deep ( . query - toolbar _ _filters ) { display : flex ; }
: global ( . client - main : has ( > . first - leg - page : not ( . is - create - route ) ) > . client - page - heading ) { margin - bottom : 10 px ; }