@@ -32,13 +32,13 @@ const columns = [
{ key : 'customerName' , prop : 'customerName' , label : '客户' , minWidth : 180 } ,
{ key : 'customerId' , prop : 'customerId' , label : '客户ID' , minWidth : 180 } ,
{ key : 'currency' , prop : 'currency' , label : '币种' , width : 90 } ,
{ key : 'storage' , slotName : 'storage' , label : '仓租' , minWidth : 260 } ,
{ key : 'returnStorage' , slotName : 'returnStorage' , label : '退货仓储费' , minWidth : 260 } ,
{ key : 'storage' , slotName : 'storage' , label : '仓租( CBM/天) ' , minWidth : 300 } ,
{ key : 'returnStorage' , slotName : 'returnStorage' , label : '退货仓储费(托/天) ' , minWidth : 300 } ,
]
function clone ( tiers : OverseasStorageFeeTier [ ] ) { return tiers . map ( ( tier ) => ( { ... tier } ) ) }
function label ( tier : OverseasStorageFeeTier ) { return tier . endDay === null ? ` ${ tier . startDay } 天以上 ` : ` ${ tier . startDay } - ${ tier . endDay } 天 ` }
function summary ( row : OverseasStorageFeeConfig , feeType : OverseasStorageFeeTier [ 'feeType' ] ) { return row . tiers . filter ( ( tier ) => tier . feeType === feeType ) . map ( ( tier ) => ` ${ label ( tier ) } ${ tier . unitPrice } ` ) . join ( ' / ' ) }
function tiersFor ( row : OverseasStorageFeeConfig , feeType : OverseasStorageFeeTier [ 'feeType' ] ) { return row . tiers . filter ( ( tier ) => tier . feeType === feeType ) . sort ( ( a , b ) => a . sortNo - b . sortNo ) }
async function load ( ) {
loading . value = true ; error . value = null
try { const result = await listOverseasStorageFees ( page . value , size . value , keyword . value . trim ( ) || undefined ) ; rows . value = result . records ; total . value = Number ( result . total ) }
@@ -62,7 +62,12 @@ onMounted(() => { void load() })
< section v-if = "error" class="surface-panel error-state" role="alert"><div><strong>{{ error.message }}</strong><ApiErrorMeta :details="error" /></div><el-button type="primary" @click="load" > 重新加载 < / el -button > < / section >
< QueryToolbar v-else v-model = "filters" class="surface-panel" :fields="queryFields" ariaLabel="海外仓阶梯费用筛选" preference-key="overseas-storage-fees-query" @search="search" @reset="() => { filters.keyword = ''; search() }" / >
< section v-if = "!error" class="surface-panel table-panel" >
< DataTable v -model :page = "page" v -model :page-size = "size" :data = "rows" :columns = "columns" :loading = "loading" :total = "total" preference -key = " overseas -storage -fees -table " row -key = " id " empty -text = " 暂无海外仓阶梯费用配置 " @ update :page = "load" @ update :page-size = "load" > < template # toolbar -start > < div class = "table-heading" > < span > 共 { { total } } 个客户 < / span > < / div > < / template > < template # actions = "{ row }" > < el-button v-if = "access.can('platform:customer:edit')" link type="primary" @click="openEdit(row as OverseasStorageFeeConfig)"><Edit :size="15" />编辑</el-button></template><template #storage="{ row }">{{ summary(row as OverseasStorageFeeConfig, 'STORAGE') }}</template><template #returnStorage="{ row }" > {{ summary ( row as OverseasStorageFeeConfig , ' RETURN_STORAGE ' ) }} < / template > < / DataTable >
< DataTable v -model :page = "page" v -model :page-size = "size" :data = "rows" :columns = "columns" :loading = "loading" :total = "total" preference -key = " overseas -storage -fees -table " row -key = " id " empty -text = " 暂无海外仓阶梯费用配置 " @ update :page = "load" @ update :page-size = "load" >
< template # toolbar -start > < div class = "table-heading" > < span > 共 { { total } } 个客户 < / span > < / div > < / template >
< template # actions = "{ row }" > < el-button v-if = "access.can('platform:customer:edit')" link type="primary" @click="openEdit(row as OverseasStorageFeeConfig)"><Edit :size="15" / > 编辑 < / el-button > < / template >
< template # storage = "{ row }" > < div class = "tier-list" > < div v-for = "tier in tiersFor(row as OverseasStorageFeeConfig, 'STORAGE')" :key="tier.id" class="tier-item" > < span > { { label ( tier ) } } < / span > < strong > { { tier . unitPrice } } { { ( row as OverseasStorageFeeConfig ) . currency } } < / strong > < / div > < / div > < / template >
< template # returnStorage = "{ row }" > < div class = "tier-list" > < div v-for = "tier in tiersFor(row as OverseasStorageFeeConfig, 'RETURN_STORAGE')" :key="tier.id" class="tier-item" > < span > { { label ( tier ) } } < / span > < strong > { { tier . unitPrice } } { { ( row as OverseasStorageFeeConfig ) . currency } } < / strong > < / div > < / div > < / template >
< / DataTable >
< / section >
< AppDialog v-model = "dialogOpen" title="编辑海外仓阶梯费用" width="min(1120px, calc(100vw - 28px))" :confirm-loading="saving" @confirm="save" >
< el -form label -position = " top " > < el-form-item label = "币种" > < el-select v-model = "form.currency" style="width:180px"><el-option label="USD" value="USD" /><el-option label="CNY" value="CNY" /><el-option label="EUR" value="EUR" /></el-select></el-form-item><div class="groups"><div v-for="(group, index) in [storageTiers, returnTiers]" :key="index"><h3>{{ index === 0 ? '仓租(CBM/天)' : '退货仓储费(托/天)' }}</h3><el-table :data="group" size="small"><el-table-column label="开始天数"><template #default="{ row }"><el-input-number v-model="row.startDay" :min="1" :controls="false" /></template></el-table-column><el-table-column label="结束天数"><template #default="{ row }"><el-input-number v-if="row.endDay !== null" v-model="row.endDay" :min="1" :controls="false" /><span v-else>以上</span></template></el-table-column><el-table-column label="价格"><template #default="{ row }"><el-input-number v-model="row.unitPrice" :min="0" :precision="6" :controls="false" / > < / template > < / el-table-column > < / el-table > < / div > < / div > < / el-form >
@@ -71,5 +76,12 @@ onMounted(() => { void load() })
< / template >
< style scoped >
. page - header , . toolbar , . pagination { display : flex ; align - items : center ; justify - content : space - between ; gap : 12 px ; } . page - header { margin - bottom : 16 px ; } . page - header h1 { margin : 0 ; font - size : 22 px ; } . page - header p { margin : 6 px 0 0 ; color : var ( -- cc - muted ) ; font - size : 13 px ; } . toolbar { justify - content : flex - start ; padding : 16 px ; border - bottom : 1 px solid var ( -- cc - border ) ; } . toolbar : deep ( . el - input ) { width : min ( 320 px , 100 % ) ; } . pagination { justify - content : flex - end ; padding : 16 px ; } . error - state { display : flex ; justify - content : space - between ; padding : 20 px ; color : # b42318 ; } . groups { display : grid ; grid - template - columns : 1 fr 1 fr ; gap : 20 px ; } . groups h3 { margin : 0 0 10 px ; font - size : 15 px ; } @ media ( max - width : 720 px ) { . groups { grid - template - columns : 1 fr ; } . page - header { align - items : flex - start ; flex - direction : column ; } }
. error - state { display : flex ; justify - content : space - between ; padding : 20 px ; color : # b42318 ; }
. tier - list { display : grid ; grid - template - columns : repeat ( 2 , minmax ( 130 px , 1 fr ) ) ; gap : 4 px 8 px ; padding : 2 px 0 ; }
. tier - item { display : flex ; align - items : center ; justify - content : space - between ; gap : 6 px ; min - height : 24 px ; padding : 2 px 6 px ; border - left : 2 px solid # 409 eff ; background : # f6f9fd ; }
. tier - item span { color : var ( -- cc - muted ) ; font - size : 12 px ; white - space : nowrap ; }
. tier - item strong { color : var ( -- cc - text ) ; font - size : 12 px ; font - weight : 650 ; white - space : nowrap ; }
. groups { display : grid ; grid - template - columns : 1 fr 1 fr ; gap : 20 px ; }
. groups h3 { margin : 0 0 10 px ; font - size : 15 px ; }
@ media ( max - width : 720 px ) { . groups , . tier - list { grid - template - columns : 1 fr ; } }
< / style >