Refactor surcharge management by replacing PrcPriceSetVersionSurcharge with PrcSurchargeRel, introducing price type handling (PRODUCT, SALES, REDLINE) and updating related services, mappers, and front-end components. Enhance validation and error messaging for price type and detail ID requirements. Update database schema to support new relationships and constraints.

This commit is contained in:
wuweihua
2026-10-09 17:20:10 +08:00
parent 70e9c18182
commit 380577f691
19 changed files with 647 additions and 530 deletions
@@ -5,6 +5,9 @@
</div>
<el-table v-loading="loading" :data="list" border max-height="calc(100vh - 160px)" empty-text="该产品暂无关联附加费">
<el-table-column label="附加费名称" prop="scName" min-width="140" show-overflow-tooltip />
<el-table-column label="价格类型" width="100" align="center">
<template #default="{ row }">{{ priceTypeLabel(row.priceType) }}</template>
</el-table-column>
<el-table-column label="计费方式" width="110" align="center">
<template #default="{ row }">{{ chargeTypeLabel(row.chargeType) }}</template>
</el-table-column>
@@ -98,6 +101,12 @@ function handleRelDelete(relId) {
}).catch(() => {})
}
function priceTypeLabel(val) {
if (val === 'REDLINE') return '红线价'
if (val === 'SALES') return '销售价'
return '产品价'
}
function chargeTypeLabel(val) {
const found = bms_charge_type.value.find(c => c.value === val)
return found ? found.label : (val || '-')
@@ -6,6 +6,22 @@
<el-option v-for="s in surchargePool" :key="s.scId" :label="s.scName" :value="s.scId" />
</el-select>
</el-form-item>
<el-form-item label="价格类型" prop="priceType">
<el-select v-model="form.priceType" placeholder="请选择价格类型" style="width: 100%">
<el-option label="产品价" value="PRODUCT" />
<el-option label="红线价" value="REDLINE" />
</el-select>
</el-form-item>
<el-form-item label="适用国家" prop="countryCode">
<el-select v-model="form.countryCode" clearable filterable placeholder="空=全部国家" style="width: 100%">
<el-option
v-for="r in regionOptions"
:key="r.countryCode2"
:label="(r.countryNameCn || r.countryNameEn || r.countryCode2) + ' (' + r.countryCode2 + ')'"
:value="r.countryCode2"
/>
</el-select>
</el-form-item>
<el-form-item label="作用范围" prop="scope">
<el-radio-group v-model="form.scope">
<el-radio value="PRODUCT">产品级(全部明细)</el-radio>
@@ -30,6 +46,7 @@
<script setup>
import { listProductCountries } from '@/api/price/product'
import { listSurcharge, addSurchargeRel } from '@/api/price/surcharge'
import { listRegionOptions } from '@/api/data/region'
const emit = defineEmits(['success'])
const { proxy } = getCurrentInstance()
@@ -40,9 +57,11 @@ const title = ref('新增附加费关联')
const product = ref(null)
const surchargePool = ref([])
const countryList = ref([])
const regionOptions = ref([])
const form = ref({})
const rules = {
scId: [{ required: true, message: '请选择附加费', trigger: 'change' }]
scId: [{ required: true, message: '请选择附加费', trigger: 'change' }],
priceType: [{ required: true, message: '请选择价格类型', trigger: 'change' }]
}
function detailLabel(d) {
@@ -53,6 +72,8 @@ function detailLabel(d) {
function reset() {
form.value = {
scId: undefined,
priceType: 'PRODUCT',
countryCode: undefined,
scope: 'PRODUCT',
detailId: undefined
}
@@ -66,6 +87,9 @@ function open(row) {
listSurcharge({ ssCode: 'on', pageNum: 1, pageSize: 1000 }).then(res => {
surchargePool.value = res.rows || []
})
listRegionOptions().then(res => {
regionOptions.value = res.data || []
})
if (row && row.productCode) {
listProductCountries(row.productCode, undefined, row.productId).then(res => {
countryList.value = res.data || []
@@ -84,7 +108,9 @@ function submit() {
scId: form.value.scId,
productId: product.value ? product.value.productId : undefined,
productCode: product.value ? product.value.productCode : undefined,
detailId: form.value.scope === 'DETAIL' ? form.value.detailId : null
detailId: form.value.scope === 'DETAIL' ? form.value.detailId : null,
priceType: form.value.priceType || 'PRODUCT',
countryCode: form.value.countryCode || null
}
addSurchargeRel(payload).then(() => {
proxy.$modal.msgSuccess('关联成功')
@@ -314,7 +314,7 @@ export function useDetailCascade(refData) {
const row = toEditableDetailRow(d)
if (!row.productCode) return row
// Do NOT prefetch /surcharge/byProduct here: applySetData hydrates every detail row
// on step-1 load; use version snapshot row.surcharges instead. Fetch on demand in picker.
// on step-1 load; use SALES rels on row.surcharges instead. Fetch on demand in picker.
if (setType !== 'FIXED') return row
// Bulk load from DB already has productCountryId + weight bounds: normalize locally,
// do not fire /countries per detail row (was stampeding thousands of identical requests).
@@ -301,8 +301,7 @@ export function surchargeScopeLabel(s) {
/**
* 产品关联附加费(prc_surcharge_rel)是否作用于该明细行
* 与后端 snapshotVersionSurcharges 的过滤口径保持一致:
* 国家三字码一致 + 产品级(detailId 空)直接命中,明细级需 productCountryId 相同
* 产品关联:国家 + productCountryId;SALES:detailId=psdId
*/
export function surchargeAppliesToRow(rel, row) {
if (!rel || !row) return false
@@ -310,6 +309,14 @@ export function surchargeAppliesToRow(rel, row) {
&& String(rel.countryCode).toUpperCase() !== String(row.countryCode).toUpperCase()) {
return false
}
// SALES rel: detail_id = psd_id
if (rel.priceType === 'SALES') {
if (rel.detailId != null && row.psdId != null) {
return Number(rel.detailId) === Number(row.psdId)
}
return true
}
// PRODUCT / REDLINE: product_country detail or product-level
if (rel.detailId == null) return true
return row.productCountryId != null && Number(rel.detailId) === Number(row.productCountryId)
}