@@ -104,6 +104,40 @@
< / el-tag >
< / template >
< template # goodsInfo = "{ row }" >
< el-tooltip
v - if = "isTransportPlanPage"
: content = "formatTransportPlanGoodsInfo(row)"
placement = "top"
: disabled = "formatTransportPlanGoodsInfo(row).length <= 10"
>
< span > { { truncateTransportPlanText ( formatTransportPlanGoodsInfo ( row ) , 10 ) } } < / span >
< / el-tooltip >
< span v-else > {{ row.goodsInfo | | ' - ' }} < / span >
< / template >
< template # departureAddress = "{ row }" >
< el-tooltip
v - if = "isTransportPlanPage"
: content = "row.departureAddress || '-'"
placement = "top"
>
< span > { { formatTransportPlanDistrictAddress ( row . departureAddress ) } } < / span >
< / el-tooltip >
< span v-else > {{ row.departureAddress | | ' - ' }} < / span >
< / template >
< template # arrivalAddress = "{ row }" >
< el-tooltip
v - if = "isTransportPlanPage"
: content = "row.arrivalAddress || '-'"
placement = "top"
>
< span > { { formatTransportPlanDistrictAddress ( row . arrivalAddress ) } } < / span >
< / el-tooltip >
< span v-else > {{ row.arrivalAddress | | ' - ' }} < / span >
< / template >
< template # basicInfoTitle -form >
< div class = "dialog-section-title" > 基本信息 < / div >
< / template >
@@ -245,25 +279,36 @@
v - model = "form.departureName"
readonly
: placeholder = "transportStationNamePlaceholder"
: suffix - icon = "transportStationMode ? Search : undefined"
: disabled = "transportAddressSelectDisabled"
@ click = "openTransportPrimaryAddressPicker('departure')"
/ >
>
< template v-if = "transportStationMode" # suffix >
< el-icon class = "business-crud-page__map-suffix" @click.stop ="openTransportSecondaryAddressPicker('departure')" >
< Search / >
< / el-icon >
< / template >
< / el-input >
< el-input
v - model = "form.departureAddress"
readonly
: readonly = "transportStationMode || !config.editableRoadAddress"
: placeholder = "transportAddressDetailPlaceholder"
: suffix - icon = "transportStationMode ? undefined : Location"
: suffix - icon = "transportStationMode || config.editableRoadAddress ? undefined : Location"
: disabled = "transportAddressSelectDisabled"
@ click = "open TransportSecondaryAddressP icker ('departure')"
/ >
@ click = "handle TransportSecondaryAddressInputCl ick('departure')"
>
< template v-if = "!transportStationMode && config.editableRoadAddress" # suffix >
< el-icon class = "business-crud-page__map-suffix" @click.stop ="openTransportMapDialog('departure')" >
< Location / >
< / el-icon >
< / template >
< / el-input >
< el-tooltip content = "选择常用地址" placement = "top" >
< el-button
type = "primary"
: icon = "List"
link
: disabled = "transportAddressSelectDisabled"
@ click = "openTransportAddressDialog ('departure')"
@ click = "openTransportAddressPicker ('departure')"
/ >
< / el-tooltip >
< span class = "business-crud-page__route-label" > 联系人 < / span >
@@ -283,25 +328,36 @@
v - model = "form.arrivalName"
readonly
: placeholder = "transportStationNamePlaceholder"
: suffix - icon = "transportStationMode ? Search : undefined"
: disabled = "transportAddressSelectDisabled"
@ click = "openTransportPrimaryAddressPicker('arrival')"
/ >
>
< template v-if = "transportStationMode" # suffix >
< el-icon class = "business-crud-page__map-suffix" @click.stop ="openTransportSecondaryAddressPicker('arrival')" >
< Search / >
< / el-icon >
< / template >
< / el-input >
< el-input
v - model = "form.arrivalAddress"
readonly
: readonly = "transportStationMode || !config.editableRoadAddress"
: placeholder = "transportAddressDetailPlaceholder"
: suffix - icon = "transportStationMode ? undefined : Location"
: suffix - icon = "transportStationMode || config.editableRoadAddress ? undefined : Location"
: disabled = "transportAddressSelectDisabled"
@ click = "open TransportSecondaryAddressP icker ('arrival')"
/ >
@ click = "handle TransportSecondaryAddressInputCl ick('arrival')"
>
< template v-if = "!transportStationMode && config.editableRoadAddress" # suffix >
< el-icon class = "business-crud-page__map-suffix" @click.stop ="openTransportMapDialog('arrival')" >
< Location / >
< / el-icon >
< / template >
< / el-input >
< el-tooltip content = "选择常用地址" placement = "top" >
< el-button
type = "primary"
: icon = "List"
link
: disabled = "transportAddressSelectDisabled"
@ click = "openTransportAddressDialog ('arrival')"
@ click = "openTransportAddressPicker ('arrival')"
/ >
< / el-tooltip >
< span class = "business-crud-page__route-label" > 联系人 < / span >
@@ -1235,9 +1291,150 @@
< / template >
< / el-table-column >
< / el-table >
< div class = "business-crud-page__cargo-total" >
< div v-if = "!shippingTemplateFreightVisible" class=" business-crud-page__cargo-total" >
运输数量合计 : { { transportCargoQuantityTotal } }
< / div >
< section v-if = "shippingTemplateFreightVisible" class="business-crud-page__freight-section" >
< div class = "dialog-section-title" >
运费信息 { { transportMode === 'road' ? '(公路)' : '' } }
< / div >
< template v-if = "transportMode === 'road'" >
< el-form
: model = "shippingTemplateFreight"
label - position = "right"
label - width = "auto"
class = "business-crud-page__freight-form business-crud-page__freight-form--road"
>
< template v-for = "(row, index) in shippingTemplateFreight.freightItems" :key="index" >
< el-form-item : label = "`单价${index + 1}`" >
< el-input
v - model = "row.unitPrice"
placeholder = "请输入"
: disabled = "dialogReadonly"
@ input = "value => handleShippingTemplateFreightNumberInput(row, 'unitPrice', value)"
>
< template # append >
< el-select
v - model = "row.priceUnit"
class = "business-crud-page__append-select"
placeholder = "单位"
: disabled = "dialogReadonly"
@ visible - change = "visible => visible && loadTaskFeeUnitOptions()"
>
< el-option
v - for = "item in taskFeeUnitOptions"
: key = "item.value"
: label = "item.label"
: value = "item.value"
/ >
< / el-select >
< / template >
< / el-input >
< / el-form-item >
< el-form-item : label = "`数量合计${index + 1}`" >
< el-input :model-value = "shippingTemplateRoadFreightQuantity(index)" disabled >
< template # append >
< el-select
: model - value = "transportCargoRows[index]?.quantityUnit"
class = "business-crud-page__append-select"
placeholder = "单位"
disabled
>
< el-option
: label = "transportCargoRows[index]?.quantityUnit || '单位'"
: value = "transportCargoRows[index]?.quantityUnit || ''"
/ >
< / el-select >
< / template >
< / el-input >
< / el-form-item >
< el-form-item : label = "`运费${index + 1}`" >
< el-input : model -value = " shippingTemplateRoadFreightAmount ( row , index ) " disabled >
< template # suffix > { { shippingTemplateCurrencyLabel } } < / template >
< / el-input >
< / el-form-item >
< / template >
< el-form-item label = "运费合计" >
< el-input :model-value = "shippingTemplateRoadFreightTotal" disabled >
< template # suffix > { { shippingTemplateCurrencyLabel } } < / template >
< / el-input >
< / el-form-item >
< el-form-item label = "其他运费合计" >
< el-input
v - model = "shippingTemplateFreight.otherFreightAmount"
placeholder = "请输入"
: disabled = "dialogReadonly"
@ input = "value => handleShippingTemplateFreightNumberInput(shippingTemplateFreight, 'otherFreightAmount', value)"
>
< template # suffix > { { shippingTemplateCurrencyLabel } } < / template >
< / el-input >
< / el-form-item >
< el-form-item label = "币种" >
< el-select
v - model = "shippingTemplateFreight.currency"
placeholder = "请选择"
clearable
: disabled = "dialogReadonly"
@ visible - change = "visible => visible && loadShippingTemplateCurrencyOptions()"
>
< el-option
v - for = "item in shippingTemplateCurrencyOptions"
: key = "item.value"
: label = "item.label"
: value = "item.value"
/ >
< / el-select >
< / el-form-item >
< / el-form >
< / template >
< el-form
v - else
: model = "shippingTemplateFreight"
label - position = "right"
label - width = "auto"
class = "business-crud-page__freight-form business-crud-page__freight-form--non-road"
>
< el-form-item label = "运费" >
< el-input
v - model = "shippingTemplateFreight.freightAmount"
placeholder = "请输入"
: disabled = "dialogReadonly"
@ input = "value => handleShippingTemplateFreightNumberInput(shippingTemplateFreight, 'freightAmount', value)"
>
< template # suffix > { { shippingTemplateCurrencyLabel } } < / template >
< / el-input >
< / el-form-item >
< el-form-item label = "数量" >
< el-input :model-value = "transportCargoQuantityTotal" disabled / >
< / el-form-item >
< el-form-item label = "其他运费合计" >
< el-input
v - model = "shippingTemplateFreight.otherFreightAmount"
placeholder = "请输入"
: disabled = "dialogReadonly"
@ input = "value => handleShippingTemplateFreightNumberInput(shippingTemplateFreight, 'otherFreightAmount', value)"
>
< template # suffix > { { shippingTemplateCurrencyLabel } } < / template >
< / el-input >
< / el-form-item >
< el-form-item label = "币种" >
< el-select
v - model = "shippingTemplateFreight.currency"
placeholder = "请选择"
clearable
: disabled = "dialogReadonly"
@ visible - change = "visible => visible && loadShippingTemplateCurrencyOptions()"
>
< el-option
v - for = "item in shippingTemplateCurrencyOptions"
: key = "item.value"
: label = "item.label"
: value = "item.value"
/ >
< / el-select >
< / el-form-item >
< / el-form >
< / section >
< / div >
< / template >
@@ -1653,6 +1850,21 @@
< / template >
< template # menu -form -before >
< div
v - if = "config.enableShippingTemplateFreight && !dialogReadonly"
class = "business-crud-page__shipping-template-footer-summary"
>
< span >
运费合计 : < strong > ¥ { { formatShippingTemplateFooterFreightTotal } } < / strong >
< / span >
< el-link
type = "primary"
: disabled = "!form.projectId"
@ click = "openShippingTemplateProcessConfig"
>
查看过程配置
< / el-link >
< / div >
< el-button
v - if = "config.enableDraftSave && !dialogReadonly"
type = "primary"
@@ -3323,6 +3535,7 @@
clearable
filterable
placeholder = "请选择"
: disabled = "!!getTransportFixedAddressType()"
>
< el-option
v - for = "item in addressTypeOptions"
@@ -3405,7 +3618,7 @@
< / el-dialog >
< el-dialog
title = "选择站点 "
: title = "` 选择${transportStationTypeLabel || '站点'}` "
append - to - body
v - model = "transportStationBox"
width = "1180px"
@@ -3634,8 +3847,8 @@
< / el-dialog >
< el-dialog title = "导入货物" append -to -body v-model = "cargoImportBox" width="560px" >
< div class = "business-crud-page__cargo-import" >
< el-upload
drag
action = "#"
accept = ".xls,.xlsx"
: auto - upload = "false"
@@ -3643,19 +3856,50 @@
: disabled = "cargoImportLoading"
: on - change = "handleCargoImportFileChange"
>
< i class = "el-icon-upload " > < / i >
< div class = "el-upload__text" > 将 Excel 文件拖到此处 , 或点击上传 < / div >
< el-button type = "primary" :loading = "cargoImportLoading "> 上传 < / el-button >
< template # tip >
< div class = "el-upload__tip" >
按货物信息表头读取 : 货物名称 、 货物类型 、 数量 、 数量单位 、 包装 、 品牌 、 规格 、 型号 、 物料编码 、 设备编码 、 备注
< / div >
< div class = "el-upload__tip" > 支持 . xlsx 、 . xls的文件格式 , 单个文件大小限制50M < / div >
< / template >
< / el-upload >
< el-button type = "primary" @click ="handleCargoImportTemplate" > 点击下载模板 < / el -button >
< / div >
< template # footer >
< el-button @click ="cargoImportBox = false" > 关闭 < / el -button >
< / template >
< / el-dialog >
< el-dialog
v - model = "shippingTemplateProcessConfigBox"
title = "过程配置"
append - to - body
width = "980px"
class = "business-crud-page__process-config-dialog"
>
< div v-loading = "shippingTemplateProcessConfigLoading" >
< el-table :data = "shippingTemplateProcessConfigRows" border empty -text = " 当前项目暂无过程配置 " >
< el-table-column prop = "configCode" label = "配置编号" min -width = " 160 " / >
< el-table-column prop = "configName" label = "配置名称" min -width = " 180 " / >
< el-table-column prop = "projectNames" label = "项目" min -width = " 220 " / >
< el-table-column label = "过程节点" min -width = " 260 " >
< template # default = "{ row }" >
{ { formatShippingTemplateProcessNodes ( row . includedNodes ) } }
< / template >
< / el-table-column >
< el-table-column prop = "defaultFinishDays" label = "默认完成时限(天)" width = "160" / >
< el-table-column label = "状态" width = "100" align = "center" >
< template # default = "{ row }" >
< el-tag : type = "Number(row.status) === 1 ? 'success' : 'info'" >
{ { Number ( row . status ) === 1 ? '启用' : '停用' } }
< / el-tag >
< / template >
< / el-table-column >
< / el-table >
< / div >
< template # footer >
< el-button type = "primary" @click ="shippingTemplateProcessConfigBox = false" > 关闭 < / el -button >
< / template >
< / el-dialog >
< el-dialog :title = "`${config.title}导入`" append -to -body v-model = "excelBox" width="555px" >
< avue-form :option = "excelOptionConfig" v-model = "excelForm" >
< template # excelTemplate >
@@ -3669,7 +3913,7 @@
< / template >
< script >
import { exportBlob } from '@/api/common' ;
import { exportBlob , importBlob } from '@/api/common' ;
import { getList as getCommonAddressList } from '@/api/base/common-address' ;
import { getList as getAirportMasterList } from '@/api/base/airport-master' ;
import { getList as getCargoTypeList } from '@/api/base/cargo-type' ;
@@ -3682,9 +3926,11 @@ import { getList as getContractList } from '@/api/business/contract-manage';
import { getList as getProjectList } from '@/api/business/project-apply' ;
import { getList as getCommonRouteList } from '@/api/business/common-route' ;
import { getList as getTransportPlanList } from '@/api/business/transport-plan' ;
import { getList as getProcessConfigList } from '@/api/business/process-config' ;
import { getList as getCarrierCustomerList } from '@/api/vehicle/customer-archive' ;
import { getList as getDriverList } from '@/api/transportCapacity/driver' ;
import { getDictionary } from '@/api/system/dictbiz' ;
import { getDictionary as getSystemDictionary } from '@/api/system/dict' ;
import { getDeptTree } from '@/api/system/dept' ;
import { addressTypeOptions } from '@/option/base/common-address' ;
import { packageOptions } from '@/option/business/common' ;
@@ -3804,6 +4050,18 @@ const defaultTransportCargo = () => ({
remark : '' ,
} ) ;
const defaultShippingTemplateFreight = ( ) => ( {
currency : '' ,
otherFreightAmount : '' ,
freightAmount : '' ,
freightItems : [ ] ,
} ) ;
const defaultShippingTemplateFreightItem = ( ) => ( {
unitPrice : '' ,
priceUnit : '' ,
} ) ;
const defaultDispatchRow = ( ) => ( {
taskEntryMode : 'simple' ,
transportType : '' ,
@@ -3953,6 +4211,12 @@ export default {
attachmentRows : [ ] ,
contractFileRows : [ ] ,
transportCargoRows : [ ] ,
shippingTemplateFreight : defaultShippingTemplateFreight ( ) ,
shippingTemplateCurrencyOptions : [ ] ,
shippingTemplateCurrencyLoading : false ,
shippingTemplateProcessConfigBox : false ,
shippingTemplateProcessConfigLoading : false ,
shippingTemplateProcessConfigRows : [ ] ,
transportRouteBox : false ,
transportRouteLoading : false ,
transportRouteQuery : { } ,
@@ -4128,6 +4392,10 @@ export default {
this . loadTaskFeeUnitOptions ( ) ;
this . ensureBillingCargoTypeOptions ( ) ;
}
if ( this . config . enableShippingTemplateFreight ) {
this . loadShippingTemplateCurrencyOptions ( ) ;
this . loadTaskFeeUnitOptions ( ) ;
}
this . consumeTemplateCreatePayload ( ) ;
} ,
computed : {
@@ -4140,6 +4408,9 @@ export default {
detailSections ( ) {
return this . config . detailSections || [ ] ;
} ,
isTransportPlanPage ( ) {
return this . config . permission === 'transport_plan' ;
} ,
transportPlanImportDuplicateCount ( ) {
return this . transportPlanImportRows . filter ( row => row . _duplicate ) . length ;
} ,
@@ -4445,6 +4716,37 @@ export default {
} , 0 ) ;
return Number . isInteger ( total ) ? String ( total ) : total . toFixed ( 2 ) ;
} ,
shippingTemplateFreightVisible ( ) {
return (
this . config . enableShippingTemplateFreight === true && this . form . templateType === '运单'
) ;
} ,
shippingTemplateCurrencyLabel ( ) {
const currency = this . shippingTemplateFreight . currency ;
if ( ! currency ) return '' ;
const option = this . shippingTemplateCurrencyOptions . find ( item => item . value === currency ) ;
return option ? . label || currency ;
} ,
shippingTemplateRoadFreightTotal ( ) {
const total = this . shippingTemplateFreight . freightItems . reduce (
( sum , item , index ) => sum + Number ( this . shippingTemplateRoadFreightAmount ( item , index ) || 0 ) ,
0
) ;
return this . formatShippingTemplateFreightNumber ( total ) ;
} ,
shippingTemplateFooterFreightTotal ( ) {
const freightAmount =
this . transportMode === 'road'
? Number ( this . shippingTemplateRoadFreightTotal || 0 )
: Number ( this . shippingTemplateFreight . freightAmount || 0 ) ;
return freightAmount + Number ( this . shippingTemplateFreight . otherFreightAmount || 0 ) ;
} ,
formatShippingTemplateFooterFreightTotal ( ) {
return this . shippingTemplateFooterFreightTotal . toLocaleString ( 'zh-CN' , {
minimumFractionDigits : 2 ,
maximumFractionDigits : 2 ,
} ) ;
} ,
transportAddressSelectDisabled ( ) {
return this . dialogReadonly || this . isBillingFieldEmpty ( this . form . transportType ) ;
} ,
@@ -4480,6 +4782,7 @@ export default {
if ( ! this . shippingInfoFormEnabled || this . suppressTransportTypeClear ) return ;
if ( String ( value ? ? '' ) === String ( oldValue ? ? '' ) ) return ;
this . handleTransportTypeChange ( value ) ;
this . syncShippingTemplateFreightItems ( ) ;
} ,
} ,
methods : {
@@ -4490,6 +4793,31 @@ export default {
} ;
return applyTableMenuWidth ( nextOption , menuButtonCount ) ;
} ,
formatTransportPlanGoodsInfo ( row = { } ) {
if ( row . goodsInfo ) return String ( row . goodsInfo ) ;
const goodsRows = this . parseJsonArray ( row . goodsJson ) ;
return goodsRows
. map ( item => item . cargoType || item . goodsType || item . cargoName || item . goodsName || '' )
. filter ( Boolean )
. join ( '/' ) ;
} ,
truncateTransportPlanText ( value , length = 10 ) {
const text = String ( value || '' ) ;
return text . length > length ? ` ${ text . slice ( 0 , length ) } ... ` : text || '-' ;
} ,
formatTransportPlanDistrictAddress ( value ) {
const text = String ( value || '' ) . trim ( ) ;
if ( ! text ) return '-' ;
const cityIndex = text . indexOf ( '市' ) ;
const districtStart = cityIndex > - 1 ? cityIndex + 1 : 0 ;
const districtMatch = text
. slice ( districtStart )
. match ( /(?:自治县|自治旗|林区|矿区|新区|开发区|区|县|旗)/ ) ;
if ( districtMatch ) {
return text . slice ( 0 , districtStart + districtMatch . index + districtMatch [ 0 ] . length ) ;
}
return cityIndex > - 1 ? text . slice ( 0 , cityIndex + 1 ) : text ;
} ,
hasPermission ( code ) {
return this . isAdmin || this . validData ( this . permission && this . permission [ code ] , false ) ;
} ,
@@ -4901,6 +5229,10 @@ export default {
if ( this . config . enableTransportPlanForm ) {
if ( ! this . validateTransportPlanForm ( submitRow ) ) return false ;
submitRow . goodsJson = JSON . stringify ( this . transportCargoRows ) ;
if ( this . config . enableShippingTemplateFreight ) {
if ( ! this . validateShippingTemplateFreight ( ) ) return false ;
submitRow . freightJson = this . serializeShippingTemplateFreight ( ) ;
}
}
if ( this . taskInfoFormEnabled ) {
if ( ! this . validateTaskInfoForm ( submitRow ) ) return false ;
@@ -4980,6 +5312,142 @@ export default {
}
return true ;
} ,
loadShippingTemplateCurrencyOptions ( ) {
if ( this . shippingTemplateCurrencyOptions . length || this . shippingTemplateCurrencyLoading ) {
return Promise . resolve ( this . shippingTemplateCurrencyOptions ) ;
}
this . shippingTemplateCurrencyLoading = true ;
return getSystemDictionary ( { code : 'currency_type' } )
. then ( res => {
this . shippingTemplateCurrencyOptions = this . normalizeDictOptions ( res . data ? . data || [ ] ) ;
const cny = this . shippingTemplateCurrencyOptions . find ( item =>
[ 'CNY' , '人民币' , 'RMB' ] . includes ( String ( item . value ) . toUpperCase ( ) )
) ;
if ( ! this . shippingTemplateFreight . currency ) {
this . shippingTemplateFreight . currency = ( cny || this . shippingTemplateCurrencyOptions [ 0 ] ) ? . value || '' ;
}
return this . shippingTemplateCurrencyOptions ;
} )
. finally ( ( ) => {
this . shippingTemplateCurrencyLoading = false ;
} ) ;
} ,
initShippingTemplateFreight ( ) {
if ( ! this . config . enableShippingTemplateFreight ) return ;
const freight = this . parseJsonObject ( this . form . freightJson ) ;
this . shippingTemplateFreight = {
... defaultShippingTemplateFreight ( ) ,
... freight ,
freightItems : ( freight . freightItems || [ ] ) . map ( item => ( {
... defaultShippingTemplateFreightItem ( ) ,
... item ,
} ) ) ,
} ;
this . syncShippingTemplateFreightItems ( ) ;
this . loadShippingTemplateCurrencyOptions ( ) ;
} ,
syncShippingTemplateFreightItems ( ) {
if ( ! this . config . enableShippingTemplateFreight || this . transportMode !== 'road' ) return ;
const rows = this . shippingTemplateFreight . freightItems || [ ] ;
this . shippingTemplateFreight . freightItems = this . transportCargoRows . map ( ( _ , index ) => ( {
... defaultShippingTemplateFreightItem ( ) ,
... ( rows [ index ] || { } ) ,
} ) ) ;
} ,
handleShippingTemplateFreightNumberInput ( row , prop , value ) {
const text = String ( value || '' ) . replace ( /[^\d.]/g , '' ) ;
const parts = text . split ( '.' ) ;
row [ prop ] =
parts . length > 1 ? ` ${ parts [ 0 ] } . ${ parts . slice ( 1 ) . join ( '' ) . slice ( 0 , 2 ) } ` : parts [ 0 ] ;
} ,
shippingTemplateRoadFreightQuantity ( index ) {
return this . formatShippingTemplateFreightNumber ( this . transportCargoRows [ index ] ? . quantity || 0 ) ;
} ,
shippingTemplateRoadFreightAmount ( item , index ) {
const amount = Number ( item ? . unitPrice || 0 ) * Number ( this . transportCargoRows [ index ] ? . quantity || 0 ) ;
return this . formatShippingTemplateFreightNumber ( amount ) ;
} ,
formatShippingTemplateFreightNumber ( value ) {
const number = Number ( value || 0 ) ;
if ( ! Number . isFinite ( number ) ) return '' ;
return Number ( number . toFixed ( 2 ) ) . toString ( ) ;
} ,
serializeShippingTemplateFreight ( ) {
if ( this . form . templateType !== '运单' ) return '' ;
const freight = {
currency : this . shippingTemplateFreight . currency || '' ,
otherFreightAmount : this . shippingTemplateFreight . otherFreightAmount || '' ,
} ;
if ( this . transportMode === 'road' ) {
freight . totalFreightAmount = this . shippingTemplateRoadFreightTotal ;
freight . freightItems = this . shippingTemplateFreight . freightItems . map ( ( item , index ) => ( {
cargoIndex : index ,
unitPrice : item . unitPrice || '' ,
priceUnit : item . priceUnit || '' ,
quantity : this . shippingTemplateRoadFreightQuantity ( index ) ,
freightAmount : this . shippingTemplateRoadFreightAmount ( item , index ) ,
} ) ) ;
} else {
freight . quantity = this . transportCargoQuantityTotal ;
freight . freightAmount = this . shippingTemplateFreight . freightAmount || '' ;
}
return JSON . stringify ( freight ) ;
} ,
validateShippingTemplateFreight ( ) {
if ( ! this . shippingTemplateFreightVisible ) return true ;
if ( ! this . shippingTemplateFreight . currency ) {
this . $message . warning ( '请选择币种' ) ;
return false ;
}
if ( this . transportMode === 'road' ) {
const invalid = this . shippingTemplateFreight . freightItems . findIndex (
item => Number ( item . unitPrice || 0 ) < 0
) ;
if ( invalid > - 1 ) {
this . $message . warning ( ` 第 ${ invalid + 1 } 条货物单价不能小于0 ` ) ;
return false ;
}
const missingPriceUnit = this . shippingTemplateFreight . freightItems . findIndex (
item => ! item . priceUnit
) ;
if ( missingPriceUnit > - 1 ) {
this . $message . warning ( ` 请选择第 ${ missingPriceUnit + 1 } 条货物的计价单位 ` ) ;
return false ;
}
} else if ( Number ( this . shippingTemplateFreight . freightAmount || 0 ) < 0 ) {
this . $message . warning ( '运费不能小于0' ) ;
return false ;
}
if ( Number ( this . shippingTemplateFreight . otherFreightAmount || 0 ) < 0 ) {
this . $message . warning ( '其他运费合计不能小于0' ) ;
return false ;
}
return true ;
} ,
openShippingTemplateProcessConfig ( ) {
if ( ! this . form . projectId || this . shippingTemplateProcessConfigLoading ) return ;
this . shippingTemplateProcessConfigBox = true ;
this . shippingTemplateProcessConfigLoading = true ;
getProcessConfigList ( 1 , 100 , { projectIds : this . form . projectId } )
. then ( res => {
this . shippingTemplateProcessConfigRows = extractRecords ( res ) . filter ( row => {
const projectIds = String ( row . projectIds || '' )
. split ( ',' )
. map ( item => item . trim ( ) ) ;
return projectIds . includes ( String ( this . form . projectId ) ) ;
} ) ;
} )
. finally ( ( ) => {
this . shippingTemplateProcessConfigLoading = false ;
} ) ;
} ,
formatShippingTemplateProcessNodes ( nodes ) {
if ( Array . isArray ( nodes ) ) return nodes . join ( '、' ) ;
return String ( nodes || '' )
. split ( ',' )
. filter ( Boolean )
. join ( '、' ) ;
} ,
stopSubmitLoading ( loading ) {
if ( typeof loading === 'function' ) {
loading ( ) ;
@@ -5235,6 +5703,7 @@ export default {
this . clearShippingPlan ( ) ;
}
this . transportCargoRows = [ ] ;
this . shippingTemplateFreight = defaultShippingTemplateFreight ( ) ;
this . billingPlanRows = [ ] ;
this . settlementRuleForm = defaultSettlementRule ( ) ;
this . reconciliationForm = defaultReconciliation ( ) ;
@@ -5343,7 +5812,9 @@ export default {
this . projectLoading = true ;
getProjectList ( 1 , 9999 , this . config . projectQueryParams || { } )
. then ( res => {
this . projectOptions = extractRecords ( res ) ;
this . projectOptions = extractRecords ( res ) . filter (
item => ! this . config . excludeVoidedProjects || item . approvalStatus !== 'voided'
) ;
this . syncCurrentProjectOption ( ) ;
} )
. finally ( ( ) => {
@@ -5352,6 +5823,7 @@ export default {
} ,
syncCurrentProjectOption ( ) {
if ( ! this . form . projectId || ! this . form . projectName ) return ;
if ( this . config . excludeVoidedProjects ) return ;
const exists = this . projectOptions . some (
item => String ( item . id ) === String ( this . form . projectId )
) ;
@@ -6001,6 +6473,7 @@ export default {
this . transportCargoRows = this . parseJsonArray ( this . form . goodsJson ) . map ( row =>
this . normalizeTransportCargoRow ( row )
) ;
this . initShippingTemplateFreight ( ) ;
this . syncTransportCargoJson ( ) ;
this . syncCurrentContractOption ( ) ;
this . restoreTransportCargoTypePaths ( ) ;
@@ -6054,9 +6527,13 @@ export default {
} else {
this . transportCargoRows . push ( nextRow ) ;
}
this . syncShippingTemplateFreightItems ( ) ;
this . syncTransportCargoJson ( ) ;
} ,
removeTransportCargoRow ( index ) {
if ( this . config . enableShippingTemplateFreight && this . transportMode === 'road' ) {
this . shippingTemplateFreight . freightItems . splice ( index , 1 ) ;
}
this . transportCargoRows . splice ( index , 1 ) ;
if (
! this . transportCargoRows . length &&
@@ -6064,6 +6541,7 @@ export default {
) {
this . transportCargoRows . push ( this . normalizeTransportCargoRow ( ) ) ;
}
this . syncShippingTemplateFreightItems ( ) ;
this . syncTransportCargoJson ( ) ;
} ,
handleTransportCargoQuantityInput ( row , value ) {
@@ -6071,6 +6549,7 @@ export default {
const parts = text . split ( '.' ) ;
row . quantity =
parts . length > 1 ? ` ${ parts [ 0 ] } . ${ parts . slice ( 1 ) . join ( '' ) . slice ( 0 , 3 ) } ` : parts [ 0 ] ;
this . syncShippingTemplateFreightItems ( ) ;
this . syncTransportCargoJson ( ) ;
} ,
handleTransportCargoTypeChange ( row , value ) {
@@ -6207,6 +6686,11 @@ export default {
}
this . openTransportMapDialog ( target ) ;
} ,
handleTransportSecondaryAddressInputClick ( target ) {
if ( this . transportStationMode || ! this . config . editableRoadAddress ) {
this . openTransportSecondaryAddressPicker ( target ) ;
}
} ,
ensureTransportTypeBeforeAddress ( ) {
if ( ! this . shippingInfoFormEnabled || ! this . isBillingFieldEmpty ( this . form . transportType ) ) {
return true ;
@@ -6381,10 +6865,31 @@ export default {
if ( ! this . ensureTransportTypeBeforeAddress ( ) ) return ;
this . transportAddressTarget = target ;
this . transportAddressSelected = null ;
this . transportAddressQuery = { } ;
this . resetTransportAddressQuery ( ) ;
this . transportAddressPage . currentPage = 1 ;
this . transportAddressBox = true ;
} ,
openTransportAddressPicker ( target ) {
if ( this . transportStationMode ) {
this . openTransportStationDialog ( target ) ;
return ;
}
this . openTransportAddressDialog ( target ) ;
} ,
resetTransportAddressQuery ( ) {
const addressType = this . getTransportFixedAddressType ( ) ;
this . transportAddressQuery = addressType ? { addressType } : { } ;
} ,
getTransportFixedAddressType ( ) {
if ( ! this . config . fixedTransportAddressType ) return '' ;
const typeMap = {
road : '常规地址' ,
rail : '铁路车站' ,
water : '港口/码头' ,
air : '空港机场' ,
} ;
return typeMap [ this . transportMode ] || '' ;
} ,
loadTransportAddressList ( ) {
this . transportAddressLoading = true ;
getCommonAddressList (
@@ -6409,7 +6914,7 @@ export default {
this . loadTransportAddressList ( ) ;
} ,
handleTransportAddressReset ( ) {
this . transportAddressQuery = { } ;
this . resetTransportAddressQuery ( ) ;
this . transportAddressPage . currentPage = 1 ;
this . loadTransportAddressList ( ) ;
} ,
@@ -6812,44 +7317,26 @@ export default {
async handleCargoImportFileChange ( file ) {
const rawFile = file . raw ;
if ( ! rawFile ) return ;
if ( ! /\.(xls|xlsx)$/i . test ( rawFile . name || '' ) ) {
this . $message . warning ( '请上传 .xlsx、.xls 格式文件' ) ;
return ;
}
if ( rawFile . size > 50 * 1024 * 1024 ) {
this . $message . warning ( '单个文件大小不能超过50M' ) ;
return ;
}
this . cargoImportLoading = true ;
try {
const ExcelJSModule = await import ( 'exceljs' ) ;
const ExcelJS = ExcelJSModule . default || ExcelJSModule ;
const workbook = new ExcelJS . Workbook ( ) ;
await workbook . xlsx . load ( await rawFile . arrayBuffer ( ) ) ;
const worksheet = workbook . worksheets [ 0 ] ;
const headerRow = worksheet . getRow ( 1 ) ;
const headerMap = { } ;
headerRow . eachCell ( ( cell , colNumber ) => {
headerMap [ String ( cell . value || '' ) . trim ( ) ] = colNumber ;
} ) ;
const fieldMap = {
货物名称 : 'cargoName' ,
货物类型 : 'cargoType' ,
数量 : 'quantity' ,
数量单位 : 'quantityUnit' ,
包装 : 'packageType' ,
品牌 : 'brand' ,
规格 : 'specification' ,
型号 : 'model' ,
物料编码 : 'materialCode' ,
设备编码 : 'deviceCode' ,
备注 : 'remark' ,
} ;
const rows = [ ] ;
worksheet . eachRow ( ( row , rowNumber ) => {
if ( rowNumber === 1 ) return ;
const nextRow = defaultTransportCargo ( ) ;
Object . entries ( fieldMap ) . forEach ( ( [ label , prop ] ) => {
const columnIndex = headerMap [ label ] ;
if ( ! columnIndex ) return ;
nextRow [ prop ] = String ( row . getCell ( columnIndex ) . value || '' ) . trim ( ) ;
} ) ;
if ( Object . values ( nextRow ) . some ( value => String ( value || '' ) . trim ( ) ) ) {
rows . push ( nextRow ) ;
const res = await importBlob ( '/blade-transport/shipping-template/import-goods' , rawFile ) ;
const contentType = res . headers [ 'content-type' ] || res . data . type || '' ;
if ( contentType . includes ( 'application/vnd.ms-excel' ) ) {
downloadXls ( res . data , ` 发货模板货物导入失败明细 ${ this . $dayjs ( ) . format ( 'YYYY-MM-DD HH:mm:ss' ) } .xlsx ` ) ;
this . $message . warning ( '部分数据导入失败,已下载失败明细' ) ;
return ;
}
} ) ;
const result = JSON . parse ( await res . data . text ( ) ) ;
if ( result . code !== 200 ) throw new Error ( result . msg || '货物导入失败' ) ;
const rows = result . data || [ ] ;
if ( ! rows . length ) {
this . $message . warning ( '未读取到货物数据' ) ;
return ;
@@ -6864,6 +7351,11 @@ export default {
this . cargoImportLoading = false ;
}
} ,
handleCargoImportTemplate ( ) {
exportBlob ( '/blade-transport/shipping-template/export-goods-template' ) . then ( res => {
downloadXls ( res . data , '发货模板货物导入模板.xlsx' ) ;
} ) ;
} ,
loadOrganizationOptions ( ) {
if ( ! this . config . enableOrganizationSelect || this . organizationLoading ) return ;
this . organizationLoading = true ;
@@ -9043,6 +9535,15 @@ export default {
white - space : nowrap ;
}
& _ _map - suffix {
cursor : pointer ;
color : # 909399 ;
& : hover {
color : # 409 eff ;
}
}
& _ _cargo - wrap {
display : block ;
width : 100 % ;
@@ -9050,6 +9551,20 @@ export default {
overflow - x : auto ;
}
& _ _cargo - import {
display : flex ;
flex - direction : column ;
align - items : flex - start ;
gap : 56 px ;
padding : 12 px 0 8 px ;
: deep ( . el - upload _ _tip ) {
margin : 16 px 0 0 ;
color : # 606266 ;
font - size : 16 px ;
}
}
& _ _cargo - table {
width : 100 % ;
min - width : 1870 px ;
@@ -9118,6 +9633,87 @@ export default {
font - weight : 600 ;
}
& _ _freight - section {
margin - top : 20 px ;
}
& _ _freight - table {
width : 100 % ;
: deep ( . el - table _ _header th ) {
background : # f5f7fa ;
color : # 303133 ;
font - weight : 600 ;
}
: deep ( . el - table _ _cell ) {
border - color : # eff1f7 ;
}
: deep ( . el - input ) ,
: deep ( . el - select ) {
width : 100 % ;
}
}
& _ _freight - summary {
display : flex ;
justify - content : flex - end ;
margin - top : 16 px ;
}
& _ _freight - form {
display : grid ;
grid - template - columns : repeat ( 3 , minmax ( 220 px , 1 fr ) ) ;
gap : 16 px 24 px ;
width : min ( 100 % , 920 px ) ;
: deep ( . el - form - item ) {
margin - bottom : 0 ;
}
: deep ( . el - input ) ,
: deep ( . el - select ) {
width : 100 % ;
}
& -- non - road {
width : 100 % ;
grid - template - columns : repeat ( 4 , minmax ( 220 px , 1 fr ) ) ;
}
}
& _ _shipping - template - footer - summary {
position : absolute ;
left : 16 px ;
bottom : 10 px ;
display : inline - flex ;
align - items : center ;
gap : 24 px ;
height : 28 px ;
color : # 303133 ;
font - size : 13 px ;
strong {
margin - left : 4 px ;
font - size : 15 px ;
}
: deep ( . el - link ) {
font - size : 13 px ;
}
}
& _ _process - config - dialog {
: deep ( . el - table _ _cell ) {
border - color : # eff1f7 ;
}
: deep ( . el - table _ _row : nth - child ( even ) td ) {
background : # fafafa ;
}
}
& _ _dialog - search {
padding : 12 px 12 px 4 px ;
margin - bottom : 12 px ;
@@ -9162,6 +9758,16 @@ export default {
: deep ( . el - select ) {
width : 100 % ;
}
& -- road {
grid - template - columns : repeat ( 3 , minmax ( 0 , 1 fr ) ) ;
column - gap : 56 px ;
row - gap : 16 px ;
: deep ( . business - crud - page _ _append - select ) {
width : 128 px ;
}
}
}
& _ _dialog - pagination {