新增附加费计费标识,优化附加费管理逻辑
This commit is contained in:
@@ -75,12 +75,22 @@
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane :label="`附加费(${(pane.item.surcharges || []).length})`" name="fee">
|
||||
<el-tab-pane :label="feeTabLabel(pane)" name="fee">
|
||||
<div class="pay-edit-bar">
|
||||
<el-input v-model="pane.feeKeyword" clearable placeholder="筛选附加费用、地区、价格、货币或费用解释" class="pay-filter" @input="pane.feePageNo = 1" />
|
||||
<el-button v-if="editable" size="small" type="primary" plain @click="addFee(pane)">添加附加费</el-button>
|
||||
<el-button v-if="editable" size="small" type="primary" plain @click="openFeePicker(pane)">添加附加费</el-button>
|
||||
<template v-if="billSelect">
|
||||
<el-button size="small" @click="setFeesBill(pane, true)">全选计费</el-button>
|
||||
<el-button size="small" @click="setFeesBill(pane, false)">取消全选</el-button>
|
||||
<span class="pay-bill-tip">勾选后才会写入 pay_surcharge;默认不勾选</span>
|
||||
</template>
|
||||
</div>
|
||||
<el-table :data="pageFees(pane)" border size="small" empty-text="没有附加费">
|
||||
<el-table-column v-if="billSelect" label="是否计费" width="88" align="center" fixed="left">
|
||||
<template #default="{ row }">
|
||||
<el-checkbox :model-value="row.needBill === true" @update:model-value="v => { row.needBill = !!v }" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="feeName" label="附加费用" min-width="180" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
@@ -227,6 +237,12 @@
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</section>
|
||||
|
||||
<pay-surcharge-picker-dialog
|
||||
v-model="feePickerVisible"
|
||||
:exclude-sc-ids="feePickerExcludeIds"
|
||||
@confirm="onFeePickerConfirm"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -235,6 +251,7 @@
|
||||
import { h, onMounted, watch } from 'vue'
|
||||
import { ElInput } from 'element-plus'
|
||||
import { listSurchargeOptions } from '@/api/price/surcharge'
|
||||
import PaySurchargePickerDialog from './PaySurchargePickerDialog.vue'
|
||||
|
||||
const props = defineProps({
|
||||
product: {
|
||||
@@ -249,6 +266,11 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/** 打单套餐:附加费表显示「是否计费」勾选,仅勾选行入库 */
|
||||
billSelect: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
savingRowKey: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
@@ -517,24 +539,93 @@ function filteredFees(pane) {
|
||||
return filterRows(pane.item.surcharges || [], pane.feeKeyword, ['feeName', 'zoneName', 'chargeText', 'amount', 'currency', 'priceUnit', 'feeDesc', 'calcRule', 'calcParam', 'postcodeParam'])
|
||||
}
|
||||
|
||||
function addFee(pane) {
|
||||
if (!pane.item.surcharges) pane.item.surcharges = []
|
||||
const currency = String(pane.item.currency || 'EUR').trim().toUpperCase() || 'EUR'
|
||||
const row = {
|
||||
feeName: '',
|
||||
scId: undefined,
|
||||
zoneName: '',
|
||||
chargeText: '',
|
||||
amount: '',
|
||||
priceUnit: currency + '/ITEM',
|
||||
currency,
|
||||
feeDesc: '',
|
||||
calcRule: 'MANUAL',
|
||||
calcParam: ''
|
||||
function feeTabLabel(pane) {
|
||||
const fees = pane.item.surcharges || []
|
||||
if (!props.billSelect) return '附加费(' + fees.length + ')'
|
||||
const n = fees.filter(f => f && f.needBill === true).length
|
||||
return '附加费(已选 ' + n + '/' + fees.length + ')'
|
||||
}
|
||||
|
||||
function setFeesBill(pane, value) {
|
||||
const fees = pane.item.surcharges || []
|
||||
for (const fee of fees) {
|
||||
if (fee) fee.needBill = !!value
|
||||
}
|
||||
}
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const feePickerVisible = ref(false)
|
||||
const feePickerPane = ref(null)
|
||||
|
||||
const feePickerExcludeIds = computed(() => {
|
||||
const fees = feePickerPane.value?.item?.surcharges || []
|
||||
return fees.map(fee => fee?.scId).filter(id => id != null)
|
||||
})
|
||||
|
||||
function openFeePicker(pane) {
|
||||
feePickerPane.value = pane
|
||||
feePickerVisible.value = true
|
||||
}
|
||||
|
||||
function toCalcRule(chargeType) {
|
||||
const t = String(chargeType || '').trim().toUpperCase()
|
||||
if (!t || t === 'RATE') return 'MANUAL'
|
||||
if (t === 'FIXED') return 'ALWAYS'
|
||||
const allowed = ['ALWAYS', 'POSTCODE', 'ZONE', 'EXTRA_KG', 'WEIGHT', 'SIZE', 'RETURN', 'MANUAL']
|
||||
return allowed.includes(t) ? t : 'MANUAL'
|
||||
}
|
||||
|
||||
function configToFeeRow(cfg, product) {
|
||||
const currency = String(cfg?.currency || product?.currency || 'EUR').trim().toUpperCase() || 'EUR'
|
||||
const amount = cfg?.chargeValue
|
||||
const isPercent = String(cfg?.chargeType || '').toUpperCase() === 'RATE'
|
||||
return {
|
||||
feeName: String(cfg?.scName || '').trim(),
|
||||
scId: cfg?.scId,
|
||||
zoneName: String(cfg?.blockTitle || cfg?.countryCode || '').trim(),
|
||||
chargeText: '',
|
||||
amount: amount == null ? '' : amount,
|
||||
currency,
|
||||
priceUnit: isPercent ? '%' : (currency + '/ITEM'),
|
||||
feeDesc: String(cfg?.scNote || '').trim(),
|
||||
calcRule: toCalcRule(cfg?.chargeType),
|
||||
calcParam: '',
|
||||
// 从配置添加默认要计费
|
||||
needBill: true
|
||||
}
|
||||
}
|
||||
|
||||
function onFeePickerConfirm(list) {
|
||||
const pane = feePickerPane.value
|
||||
if (!pane?.item) return
|
||||
if (!pane.item.surcharges) pane.item.surcharges = []
|
||||
const existed = new Set(
|
||||
(pane.item.surcharges || []).map(fee => fee?.scId).filter(id => id != null).map(id => String(id))
|
||||
)
|
||||
let added = 0
|
||||
let skipped = 0
|
||||
for (const cfg of list || []) {
|
||||
if (cfg?.scId != null && existed.has(String(cfg.scId))) {
|
||||
skipped++
|
||||
continue
|
||||
}
|
||||
const row = configToFeeRow(cfg, pane.item)
|
||||
if (!row.feeName) continue
|
||||
pane.item.surcharges.push(row)
|
||||
if (row.scId != null) existed.add(String(row.scId))
|
||||
added++
|
||||
}
|
||||
if (added > 0) {
|
||||
pane.feePageNo = Math.ceil(pane.item.surcharges.length / pageSize) || 1
|
||||
pane.tab = 'fee'
|
||||
}
|
||||
if (added && skipped) {
|
||||
proxy.$modal.msgSuccess(`已添加 ${added} 项,跳过重复 ${skipped} 项`)
|
||||
} else if (added) {
|
||||
proxy.$modal.msgSuccess(`已添加 ${added} 项附加费`)
|
||||
} else {
|
||||
proxy.$modal.msgWarning('没有可添加的附加费(可能已全部存在)')
|
||||
}
|
||||
pane.item.surcharges.push(row)
|
||||
startEdit(row)
|
||||
pane.feePageNo = Math.ceil(pane.item.surcharges.length / pageSize) || 1
|
||||
}
|
||||
|
||||
function removeRow(list, row) {
|
||||
@@ -643,9 +734,14 @@ function statusText(status) {
|
||||
.pay-edit-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.pay-bill-tip {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
}
|
||||
.pay-hint {
|
||||
margin: 0 0 8px;
|
||||
color: var(--el-text-color-secondary);
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="选择附加费配置"
|
||||
width="920px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
@opened="onOpened"
|
||||
>
|
||||
<div class="picker-bar">
|
||||
<el-input
|
||||
v-model="keyword"
|
||||
clearable
|
||||
placeholder="搜索附加费名称、计费方式、币种、国家或备注"
|
||||
class="picker-search"
|
||||
/>
|
||||
<span class="picker-meta">共 {{ filteredRows.length }} 项,已选 {{ picked.length }} 项</span>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
v-loading="loading"
|
||||
:data="filteredRows"
|
||||
row-key="scId"
|
||||
border
|
||||
size="small"
|
||||
max-height="420"
|
||||
empty-text="暂无附加费配置"
|
||||
@selection-change="onSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="46" :selectable="canSelect" />
|
||||
<el-table-column prop="scName" label="附加费名称" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="计费方式" width="120" align="center">
|
||||
<template #default="{ row }">{{ chargeTypeLabel(row.chargeType) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="计费数值" width="130" align="right">
|
||||
<template #default="{ row }">{{ formatValue(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="currency" label="币种" width="80" align="center" />
|
||||
<el-table-column label="国家/分区" min-width="140" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ scopeText(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="scNote" label="备注" min-width="160" show-overflow-tooltip />
|
||||
</el-table>
|
||||
|
||||
<div class="picker-hint">勾选后点确定写入当前产品附加费表;已存在相同配置的不会重复添加。</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取 消</el-button>
|
||||
<el-button type="primary" :disabled="!picked.length" @click="submit">确 定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { listSurchargeOptions } from '@/api/price/surcharge'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: Boolean, default: false },
|
||||
/** 已在表中的 scId,用于禁用重复勾选 */
|
||||
excludeScIds: { type: Array, default: () => [] }
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue', 'confirm'])
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const tableRef = ref()
|
||||
const loading = ref(false)
|
||||
const keyword = ref('')
|
||||
const rows = ref([])
|
||||
const pickedIds = ref(new Set())
|
||||
const syncing = ref(false)
|
||||
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: v => emit('update:modelValue', v)
|
||||
})
|
||||
|
||||
const excludeSet = computed(() => new Set((props.excludeScIds || []).map(id => String(id))))
|
||||
|
||||
const chargeTypeLabelMap = {
|
||||
MANUAL: '暂不计费',
|
||||
ALWAYS: '每票都收',
|
||||
POSTCODE: '命中邮编',
|
||||
ZONE: '命中分区',
|
||||
EXTRA_KG: '超重续收',
|
||||
WEIGHT: '超重',
|
||||
SIZE: '超尺寸',
|
||||
RETURN: '退件',
|
||||
FIXED: '每票都收',
|
||||
RATE: '暂不计费'
|
||||
}
|
||||
|
||||
function chargeTypeLabel(val) {
|
||||
if (!val) return chargeTypeLabelMap.MANUAL
|
||||
const key = String(val).toUpperCase()
|
||||
return chargeTypeLabelMap[key] || val
|
||||
}
|
||||
|
||||
function formatValue(row) {
|
||||
if (row == null || row.chargeValue == null || row.chargeValue === '') return '-'
|
||||
const v = Number(row.chargeValue)
|
||||
if (!Number.isFinite(v)) return row.chargeValue
|
||||
if (String(row.chargeType || '').toUpperCase() === 'RATE') return (v * 100).toFixed(2) + '%'
|
||||
return String(v)
|
||||
}
|
||||
|
||||
function scopeText(row) {
|
||||
const parts = []
|
||||
if (row.countryCode) parts.push(row.countryCode)
|
||||
if (row.blockTitle) parts.push(row.blockTitle)
|
||||
return parts.join(' / ') || '全部'
|
||||
}
|
||||
|
||||
function canSelect(row) {
|
||||
if (row?.scId == null) return false
|
||||
return !excludeSet.value.has(String(row.scId))
|
||||
}
|
||||
|
||||
const filteredRows = computed(() => {
|
||||
const text = String(keyword.value || '').trim().toLowerCase()
|
||||
const list = rows.value || []
|
||||
if (!text) return list
|
||||
return list.filter(row => {
|
||||
const bag = [
|
||||
row.scName,
|
||||
chargeTypeLabel(row.chargeType),
|
||||
row.chargeType,
|
||||
row.currency,
|
||||
row.countryCode,
|
||||
row.blockTitle,
|
||||
row.scNote,
|
||||
row.chargeValue
|
||||
].map(v => String(v ?? '').toLowerCase())
|
||||
return bag.some(item => item.includes(text))
|
||||
})
|
||||
})
|
||||
|
||||
const picked = computed(() => {
|
||||
const ids = pickedIds.value
|
||||
return (rows.value || []).filter(row => row?.scId != null && ids.has(String(row.scId)) && canSelect(row))
|
||||
})
|
||||
|
||||
function onSelectionChange(selection) {
|
||||
if (syncing.value) return
|
||||
const visibleIds = new Set((filteredRows.value || []).map(row => String(row.scId)))
|
||||
const next = new Set(pickedIds.value)
|
||||
for (const id of visibleIds) {
|
||||
next.delete(id)
|
||||
}
|
||||
for (const row of selection || []) {
|
||||
if (row?.scId != null && canSelect(row)) next.add(String(row.scId))
|
||||
}
|
||||
pickedIds.value = next
|
||||
}
|
||||
|
||||
function restoreSelection() {
|
||||
const table = tableRef.value
|
||||
if (!table) return
|
||||
syncing.value = true
|
||||
table.clearSelection()
|
||||
for (const row of filteredRows.value || []) {
|
||||
if (row?.scId != null && pickedIds.value.has(String(row.scId))) {
|
||||
table.toggleRowSelection(row, true)
|
||||
}
|
||||
}
|
||||
nextTick(() => {
|
||||
syncing.value = false
|
||||
})
|
||||
}
|
||||
|
||||
async function loadRows() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await listSurchargeOptions({ ssCode: 'on' })
|
||||
rows.value = res.data || []
|
||||
} catch (e) {
|
||||
rows.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
nextTick(() => restoreSelection())
|
||||
}
|
||||
}
|
||||
|
||||
function onOpened() {
|
||||
keyword.value = ''
|
||||
pickedIds.value = new Set()
|
||||
nextTick(() => {
|
||||
tableRef.value?.clearSelection?.()
|
||||
})
|
||||
if (!rows.value.length) {
|
||||
loadRows()
|
||||
}
|
||||
}
|
||||
|
||||
function submit() {
|
||||
const list = picked.value
|
||||
if (!list.length) {
|
||||
proxy.$modal.msgWarning('请先勾选要添加的附加费')
|
||||
return
|
||||
}
|
||||
emit('confirm', list)
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
watch(keyword, () => {
|
||||
nextTick(() => restoreSelection())
|
||||
})
|
||||
|
||||
watch(() => props.modelValue, v => {
|
||||
if (v && !rows.value.length) loadRows()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.picker-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.picker-search {
|
||||
width: 360px;
|
||||
}
|
||||
.picker-meta {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
}
|
||||
.picker-hint {
|
||||
margin-top: 10px;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
@@ -87,7 +87,7 @@
|
||||
</div>
|
||||
|
||||
<el-drawer v-model="detailVisible" :title="detailTitle" size="78%" destroy-on-close append-to-body>
|
||||
<pay-config-detail :product="detailProduct" :editable="false" />
|
||||
<pay-config-detail :product="detailProduct" :editable="false" :bill-select="true" />
|
||||
<template #footer>
|
||||
<el-button @click="detailVisible = false">关闭</el-button>
|
||||
</template>
|
||||
@@ -187,7 +187,9 @@ async function onFile(uploadFile) {
|
||||
const data = res.data || {}
|
||||
data.products = (data.products || []).map((item, index) => ({
|
||||
...item,
|
||||
rowKey: `${item.sheetName || ''}-${item.productName || ''}-${index}`
|
||||
rowKey: `${item.sheetName || ''}-${item.productName || ''}-${index}`,
|
||||
// 解析后默认不计费,勾选后才入 pay_surcharge
|
||||
surcharges: (item.surcharges || []).map(fee => ({ ...fee, needBill: fee?.needBill === true }))
|
||||
}))
|
||||
preview.value = data
|
||||
const picked = data.products.filter(row => canSelect(row))
|
||||
@@ -310,12 +312,17 @@ async function submit(payload = {}) {
|
||||
const productsPayload = (payload.products && payload.products.length)
|
||||
? payload.products
|
||||
: selected.value.map(row => ({ ...row, productType: 'AR' }))
|
||||
const filteredProducts = productsPayload.map(product => ({
|
||||
...product,
|
||||
productType: product.productType || 'AR',
|
||||
surcharges: (product.surcharges || []).filter(fee => fee && fee.needBill === true)
|
||||
}))
|
||||
const res = await saveLabelImport({
|
||||
priceSetId: payload.priceSetId || null,
|
||||
priceSetName: String(payload.priceSetName || '').trim(),
|
||||
priceSetNote: payload.priceSetNote || '',
|
||||
overwrite: payload.overwrite != null ? payload.overwrite : overwrite.value,
|
||||
products: productsPayload
|
||||
products: filteredProducts
|
||||
})
|
||||
const data = res.data || {}
|
||||
return {
|
||||
|
||||
@@ -21,9 +21,18 @@
|
||||
<span>{{ cardTitle }}</span>
|
||||
<span class="header-tip">{{ headerTip }}</span>
|
||||
</div>
|
||||
<el-button type="primary" plain icon="Upload" @click="openImport" v-hasPermi="['price:label:import', 'price:label:add']">
|
||||
导入价卡
|
||||
</el-button>
|
||||
<div class="header-actions">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="!selectedProducts.length"
|
||||
@click="removeSelectedProducts"
|
||||
>批量删除</el-button>
|
||||
<el-button type="primary" plain icon="Upload" @click="openImport" v-hasPermi="['price:label:import', 'price:label:add']">
|
||||
导入价卡
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -36,7 +45,18 @@
|
||||
class="meta-alert"
|
||||
/>
|
||||
|
||||
<el-table v-loading="loadingProducts" :data="productRows" border size="small" max-height="420" empty-text="请点击右上角「导入价卡」">
|
||||
<el-table
|
||||
ref="productTableRef"
|
||||
v-loading="loadingProducts"
|
||||
:data="productRows"
|
||||
border
|
||||
size="small"
|
||||
max-height="420"
|
||||
row-key="rowKey"
|
||||
empty-text="请点击右上角「导入价卡」"
|
||||
@selection-change="onProductSelect"
|
||||
>
|
||||
<el-table-column type="selection" width="48" align="center" />
|
||||
<el-table-column prop="productName" label="产品" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="sheetName" label="报价工作表" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="countryScope" label="国家/口岸" min-width="110" show-overflow-tooltip />
|
||||
@@ -54,9 +74,10 @@
|
||||
<span v-else>新记录</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="90" align="center" fixed="right">
|
||||
<el-table-column label="操作" width="140" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" icon="View" @click="openDetail(row)">查看</el-button>
|
||||
<el-button link type="primary" icon="Edit" @click="openDetail(row)">编辑</el-button>
|
||||
<el-button link type="danger" icon="Delete" @click="removeProduct(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -79,6 +100,7 @@
|
||||
<pay-config-detail
|
||||
:product="detailProduct"
|
||||
:editable="true"
|
||||
:bill-select="true"
|
||||
:saving-row-key="savingRowKey"
|
||||
@save-row="saveDetailRow"
|
||||
/>
|
||||
@@ -104,6 +126,8 @@ const form = reactive({
|
||||
basic: createEmptyBasicInfo()
|
||||
})
|
||||
const productRows = ref([])
|
||||
const productTableRef = ref()
|
||||
const selectedProducts = ref([])
|
||||
const importMeta = reactive({
|
||||
sourceFile: '',
|
||||
supplierName: '',
|
||||
@@ -141,7 +165,7 @@ const pageBanner = computed(() => {
|
||||
const headerTip = computed(() => {
|
||||
if (editVersionMode.value) return '可重新导入或修改明细;完成后覆盖本版本,不新增版本号'
|
||||
if (newVersionMode.value) return '导入后生成待生效新版本;生效时间请回详情页设置'
|
||||
return '打单价卡与应付独立;解析后回填到此表,查看明细后可在行内保存修改'
|
||||
return '打单价卡与应付独立;解析后回填到此表,可删除不需要的产品,编辑明细后点完成保存'
|
||||
})
|
||||
|
||||
function goBack() {
|
||||
@@ -196,7 +220,9 @@ async function loadEditProducts() {
|
||||
}
|
||||
}
|
||||
}))
|
||||
productRows.value = details
|
||||
// 库中已落库的附加费默认勾选(保留原计费项)
|
||||
productRows.value = markSurchargeBillFlags(details, true)
|
||||
selectedProducts.value = []
|
||||
importMeta.sourceFile = details[0]?.sourceFile || ''
|
||||
importMeta.supplierName = details[0]?.supplierName || ''
|
||||
importMeta.overwrite = true
|
||||
@@ -209,8 +235,71 @@ async function openImport() {
|
||||
importRef.value?.open()
|
||||
}
|
||||
|
||||
function markSurchargeBillFlags(products, defaultNeedBill) {
|
||||
return (products || []).map(product => ({
|
||||
...product,
|
||||
surcharges: (product.surcharges || []).map(fee => ({
|
||||
...fee,
|
||||
needBill: fee?.needBill === true ? true : (fee?.needBill === false ? false : !!defaultNeedBill)
|
||||
}))
|
||||
}))
|
||||
}
|
||||
|
||||
function withBillableSurcharges(products) {
|
||||
return (products || []).map(product => ({
|
||||
...product,
|
||||
surcharges: (product.surcharges || []).filter(fee => fee && fee.needBill === true)
|
||||
}))
|
||||
}
|
||||
|
||||
function onProductSelect(rows) {
|
||||
selectedProducts.value = rows || []
|
||||
}
|
||||
|
||||
function productKey(row) {
|
||||
return row?.rowKey || `${row?.sheetName || ''}-${row?.productName || ''}`
|
||||
}
|
||||
|
||||
async function removeProduct(row) {
|
||||
if (!row) return
|
||||
try {
|
||||
await proxy.$modal.confirm(`确认从本次导入中移除产品「${row.productName || row.sheetName || ''}」?`)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
const key = productKey(row)
|
||||
productRows.value = productRows.value.filter(item => productKey(item) !== key)
|
||||
selectedProducts.value = selectedProducts.value.filter(item => productKey(item) !== key)
|
||||
if (detailRowKey.value === key) {
|
||||
detailVisible.value = false
|
||||
}
|
||||
proxy.$modal.msgSuccess('已删除')
|
||||
}
|
||||
|
||||
async function removeSelectedProducts() {
|
||||
const rows = selectedProducts.value || []
|
||||
if (!rows.length) {
|
||||
proxy.$modal.msgWarning('请先勾选要删除的产品')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await proxy.$modal.confirm(`确认删除所选 ${rows.length} 个产品?`)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
const keys = new Set(rows.map(productKey))
|
||||
productRows.value = productRows.value.filter(item => !keys.has(productKey(item)))
|
||||
selectedProducts.value = []
|
||||
if (keys.has(detailRowKey.value)) {
|
||||
detailVisible.value = false
|
||||
}
|
||||
proxy.$modal.msgSuccess('已批量删除')
|
||||
}
|
||||
|
||||
function onImportConfirm(payload) {
|
||||
productRows.value = payload?.products || []
|
||||
// 价卡解析行默认不计费,需手动勾选
|
||||
productRows.value = markSurchargeBillFlags(payload?.products || [], false)
|
||||
selectedProducts.value = []
|
||||
importMeta.sourceFile = payload?.sourceFile || ''
|
||||
importMeta.supplierName = payload?.supplierName || ''
|
||||
importMeta.effectiveDate = payload?.effectiveDate || ''
|
||||
@@ -226,6 +315,10 @@ function openDetail(row) {
|
||||
}
|
||||
|
||||
function onDetailClosed() {
|
||||
// 关闭抽屉时回写勾选结果(不依赖行内「保存」)
|
||||
if (detailProduct.value && detailRowKey.value) {
|
||||
applyEditedProduct()
|
||||
}
|
||||
detailProduct.value = null
|
||||
detailRowKey.value = ''
|
||||
savingRowKey.value = ''
|
||||
@@ -303,6 +396,10 @@ async function finish() {
|
||||
proxy.$modal.msgWarning('请先导入并确认价卡产品')
|
||||
return
|
||||
}
|
||||
// 抽屉未关闭时先回写勾选,避免丢失
|
||||
if (detailVisible.value && detailProduct.value && detailRowKey.value) {
|
||||
applyEditedProduct()
|
||||
}
|
||||
const tip = editVersionMode.value
|
||||
? `将覆盖版本 V${versionNo.value} 的 ${productRows.value.length} 个产品,确认继续?`
|
||||
: newVersionMode.value
|
||||
@@ -317,7 +414,7 @@ async function finish() {
|
||||
try {
|
||||
const payload = {
|
||||
overwrite: editVersionMode.value ? true : importMeta.overwrite,
|
||||
products: productRows.value
|
||||
products: withBillableSurcharges(productRows.value)
|
||||
}
|
||||
if (editVersionMode.value) {
|
||||
payload.priceSetId = priceSetId.value
|
||||
@@ -367,6 +464,11 @@ watch([priceSetId, versionNo, () => route.query.editVersion, () => route.query.n
|
||||
color: #909399;
|
||||
font-weight: normal;
|
||||
}
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.meta-alert {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@
|
||||
<el-table-column :label="colExp" min-width="170">
|
||||
<template #default="{ row }">{{ row.expireTime || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="colStatus" width="100" align="center">
|
||||
<!-- <el-table-column :label="colStatus" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="isDeletedVersion(row) ? 'danger' : versionStatusTag(row.status)" size="small">{{ statusText(row) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column> -->
|
||||
<el-table-column :label="colCreate" min-width="170" align="center">
|
||||
<template #default="{ row }">{{ row.createTime || '-' }}</template>
|
||||
</el-table-column>
|
||||
|
||||
Reference in New Issue
Block a user