应付账单初始化
This commit is contained in:
@@ -0,0 +1,341 @@
|
||||
<template>
|
||||
<div v-if="panes.length" class="pay-detail">
|
||||
<section v-for="pane in panes" :key="pane.key" class="pay-product">
|
||||
<div class="pay-product-title">{{ pane.item.productName || '未命名产品' }}</div>
|
||||
<el-descriptions :column="3" border size="small" class="pay-summary">
|
||||
<el-descriptions-item label="价卡归属">{{ pane.item.supplierName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="生效日期">{{ pane.item.effectiveDate || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="国家/口岸">{{ pane.item.countryScope || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="费用类型">{{ pane.item.chargeType || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="每公斤加价">{{ pane.item.extraPerKg || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="服务类型">{{ pane.item.serviceType || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="分类">{{ pane.item.classification || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="币种">{{ pane.item.currency || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报价工作表">{{ pane.item.sheetName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="关联分区表">{{ pane.item.relatedSheet || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="识别结果">{{ statusText(pane.item.parseStatus) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="重量限制">{{ pane.item.weightLimit || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="更新内容" :span="2">{{ pane.item.updateContent || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="目录备注" :span="3">{{ pane.item.catalogRemark || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="pane.item.remark" label="备注" :span="3">{{ pane.item.remark }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-tabs v-model="pane.tab">
|
||||
<el-tab-pane :label="`重量段(${(pane.item.weightBands || []).length})`" name="band">
|
||||
<el-input v-model="pane.bandKeyword" clearable placeholder="筛选国家、分区、重量段、费用项" class="pay-filter" @input="pane.bandPageNo = 1" />
|
||||
<div v-if="editable" class="pay-edit-bar">
|
||||
<el-button size="small" type="primary" plain @click="addBand(pane)">添加重量段</el-button>
|
||||
</div>
|
||||
<el-table :data="pageBands(pane)" border size="small" max-height="360" empty-text="没有重量段或起重配置">
|
||||
<el-table-column prop="sectionName" label="区块" min-width="110" show-overflow-tooltip>
|
||||
<template #default="{ row }"><cell-text v-model="row.sectionName" :edit="editable" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="countryName" label="国家" min-width="120" show-overflow-tooltip>
|
||||
<template #default="{ row }"><cell-text v-model="row.countryName" :edit="editable" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="countryCode" label="代码" width="80" show-overflow-tooltip>
|
||||
<template #default="{ row }"><cell-text v-model="row.countryCode" :edit="editable" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="zoneName" label="分区" min-width="140" show-overflow-tooltip>
|
||||
<template #default="{ row }"><cell-text v-model="row.zoneName" :edit="editable" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="weightLabel" label="重量段" min-width="120" show-overflow-tooltip>
|
||||
<template #default="{ row }"><cell-text v-model="row.weightLabel" :edit="editable" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="下限KG" width="90" align="right">
|
||||
<template #default="{ row }">{{ money(row.weightMin) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上限KG" width="90" align="right">
|
||||
<template #default="{ row }">{{ money(row.weightMax) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="chargeName" label="费用项" min-width="120" show-overflow-tooltip>
|
||||
<template #default="{ row }"><cell-text v-model="row.chargeName" :edit="editable" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单价" width="110" align="right">
|
||||
<template #default="{ row }">
|
||||
<el-input v-if="editable" v-model="row.price" size="small" />
|
||||
<span v-else>{{ money(row.price) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="priceUnit" label="单位" width="110" show-overflow-tooltip>
|
||||
<template #default="{ row }"><cell-text v-model="row.priceUnit" :edit="editable" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="editable" label="操作" width="70" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="danger" @click="removeRow(pane.item.weightBands, row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
v-if="filteredBands(pane).length > pageSize"
|
||||
class="pay-page"
|
||||
layout="total, prev, pager, next"
|
||||
:total="filteredBands(pane).length"
|
||||
:page-size="pageSize"
|
||||
v-model:current-page="pane.bandPageNo"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane :label="`附加费(${(pane.item.surcharges || []).length})`" 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>
|
||||
</div>
|
||||
<el-table :data="pageFees(pane)" border size="small" max-height="360" empty-text="没有附加费">
|
||||
<el-table-column prop="feeName" label="附加费用" min-width="160" show-overflow-tooltip>
|
||||
<template #default="{ row }"><cell-text v-model="row.feeName" :edit="editable" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="zoneName" label="地区" min-width="160" show-overflow-tooltip>
|
||||
<template #default="{ row }"><cell-text v-model="row.zoneName" :edit="editable" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="chargeText" label="收费标准" min-width="180" show-overflow-tooltip>
|
||||
<template #default="{ row }"><cell-text v-model="row.chargeText" :edit="editable" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="feeDesc" label="费用解释" min-width="240" show-overflow-tooltip>
|
||||
<template #default="{ row }"><cell-text v-model="row.feeDesc" :edit="editable" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="calcRule" label="计费条件" width="140">
|
||||
<template #default="{ row }">
|
||||
<el-select v-if="editable" v-model="row.calcRule" size="small" placeholder="人工确认">
|
||||
<el-option v-for="item in calcRules" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<span v-else>{{ calcRuleLabel(row.calcRule) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="calcParam" label="条件参数" min-width="180" show-overflow-tooltip>
|
||||
<template #default="{ row }"><cell-text v-model="row.calcParam" :edit="editable" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="editable" label="操作" width="70" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="danger" @click="removeRow(pane.item.surcharges, row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
v-if="filteredFees(pane).length > pageSize"
|
||||
class="pay-page"
|
||||
layout="total, prev, pager, next"
|
||||
:total="filteredFees(pane).length"
|
||||
:page-size="pageSize"
|
||||
v-model:current-page="pane.feePageNo"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane v-if="postcodeLines(pane).length" :label="`邮编分区(${postcodeLines(pane).length})`" name="postcode">
|
||||
<p class="pay-hint">{{ postcodeHint(pane) }}</p>
|
||||
<el-input v-model="pane.postKeyword" clearable placeholder="筛选邮编、州、城市或分区" class="pay-filter" @input="pane.postPageNo = 1" />
|
||||
<el-table :data="pagePostcodes(pane)" border size="small" max-height="360" empty-text="没有邮编">
|
||||
<el-table-column v-for="(title, index) in postcodeTitles(pane)" :key="title" :label="title" min-width="110" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.parts[index] || '-' }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
v-if="filteredPostcodes(pane).length > pageSize"
|
||||
class="pay-page"
|
||||
layout="total, prev, pager, next"
|
||||
:total="filteredPostcodes(pane).length"
|
||||
:page-size="pageSize"
|
||||
v-model:current-page="pane.postPageNo"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 重量段和附加费。传入 products 时一次展示全部产品。
|
||||
import { h } from 'vue'
|
||||
import { ElInput } from 'element-plus'
|
||||
|
||||
const props = defineProps({
|
||||
product: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
products: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
editable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const CellText = {
|
||||
props: { modelValue: { default: '' }, edit: Boolean },
|
||||
emits: ['update:modelValue'],
|
||||
setup(props, { emit }) {
|
||||
return () => props.edit
|
||||
? h(ElInput, {
|
||||
modelValue: props.modelValue ?? '',
|
||||
size: 'small',
|
||||
'onUpdate:modelValue': value => emit('update:modelValue', value)
|
||||
})
|
||||
: h('span', props.modelValue || '-')
|
||||
}
|
||||
}
|
||||
|
||||
const calcRules = [
|
||||
{ value: 'ALWAYS', label: '每票都收' },
|
||||
{ value: 'POSTCODE', label: '命中邮编' },
|
||||
{ value: 'WEIGHT', label: '超重' },
|
||||
{ value: 'SIZE', label: '超尺寸' },
|
||||
{ value: 'RETURN', label: '退件' },
|
||||
{ value: 'MANUAL', label: '人工确认' }
|
||||
]
|
||||
|
||||
function calcRuleLabel(value) {
|
||||
return calcRules.find(item => item.value === value)?.label || '人工确认'
|
||||
}
|
||||
|
||||
const pageSize = 20
|
||||
const panes = ref([])
|
||||
|
||||
const source = computed(() => (props.products?.length ? props.products : (props.product ? [props.product] : [])))
|
||||
|
||||
watch(source, (list) => {
|
||||
panes.value = list.map((item, index) => ({
|
||||
key: item.productId || item.rowKey || `${item.sheetName || ''}-${item.productName || ''}-${index}`,
|
||||
item,
|
||||
tab: 'band',
|
||||
bandKeyword: '',
|
||||
feeKeyword: '',
|
||||
postKeyword: '',
|
||||
bandPageNo: 1,
|
||||
feePageNo: 1,
|
||||
postPageNo: 1
|
||||
}))
|
||||
}, { immediate: true })
|
||||
|
||||
function filteredBands(pane) {
|
||||
return filterRows(pane.item.weightBands || [], pane.bandKeyword, ['sectionName', 'countryName', 'countryCode', 'zoneName', 'weightLabel', 'chargeName', 'priceUnit'])
|
||||
}
|
||||
|
||||
function filteredFees(pane) {
|
||||
return filterRows(pane.item.surcharges || [], pane.feeKeyword, ['feeName', 'zoneName', 'chargeText', 'feeDesc', 'calcRule', 'calcParam'])
|
||||
}
|
||||
|
||||
function addBand(pane) {
|
||||
if (!pane.item.weightBands) pane.item.weightBands = []
|
||||
pane.item.weightBands.push({ sectionName: '', countryName: '', countryCode: '', zoneName: '', weightLabel: '', chargeName: '', price: '', priceUnit: '' })
|
||||
pane.bandPageNo = Math.ceil(pane.item.weightBands.length / pageSize) || 1
|
||||
}
|
||||
|
||||
function addFee(pane) {
|
||||
if (!pane.item.surcharges) pane.item.surcharges = []
|
||||
pane.item.surcharges.push({ feeName: '', zoneName: '', chargeText: '', feeDesc: '', calcRule: 'MANUAL', calcParam: '' })
|
||||
pane.feePageNo = Math.ceil(pane.item.surcharges.length / pageSize) || 1
|
||||
}
|
||||
|
||||
function removeRow(list, row) {
|
||||
const index = list.indexOf(row)
|
||||
if (index >= 0) list.splice(index, 1)
|
||||
}
|
||||
|
||||
function pageBands(pane) {
|
||||
return pageOf(filteredBands(pane), pane.bandPageNo)
|
||||
}
|
||||
|
||||
function pageFees(pane) {
|
||||
return pageOf(filteredFees(pane), pane.feePageNo)
|
||||
}
|
||||
|
||||
function postcodeLines(pane) {
|
||||
const related = pane.item.relatedSheet || ''
|
||||
if (!related.includes('邮编')) return []
|
||||
return (pane.item.extraLines || []).filter(line => line.lineType === '邮编')
|
||||
}
|
||||
|
||||
function postcodeTitles(pane) {
|
||||
const sheet = `${pane.item.sheetName || ''}${pane.item.productName || ''}`
|
||||
if (sheet.includes('DE-iMile')) return ['国家', '产品', '邮编', '状态']
|
||||
return ['州/地区', '城市', '地区代码', '邮编', '分区']
|
||||
}
|
||||
|
||||
function postcodeHint(pane) {
|
||||
const sheet = `${pane.item.sheetName || ''}${pane.item.productName || ''}`
|
||||
if (sheet.includes('DE-iMile')) {
|
||||
return '德国这块没有州和城市。状态为 Active 的邮编属于价卡里的德国邮编清单,走同一套重量价格。'
|
||||
}
|
||||
return '用邮编对应州和城市。分区已换成价卡上的列:city(dense) 为 City,province(no dense) 为 Province,rural(remote) 为 Rural。'
|
||||
}
|
||||
|
||||
function filteredPostcodes(pane) {
|
||||
const text = (pane.postKeyword || '').trim().toLowerCase()
|
||||
const rows = postcodeLines(pane).map(line => ({ parts: String(line.lineText || '').split(' | '), raw: line.lineText || '' }))
|
||||
if (!text) return rows
|
||||
return rows.filter(row => row.raw.toLowerCase().includes(text))
|
||||
}
|
||||
|
||||
function pagePostcodes(pane) {
|
||||
return pageOf(filteredPostcodes(pane), pane.postPageNo)
|
||||
}
|
||||
|
||||
function filterRows(rows, keyword, fields) {
|
||||
const text = (keyword || '').trim().toLowerCase()
|
||||
if (!text) return rows
|
||||
return rows.filter(row => fields.some(field => String(row?.[field] ?? '').toLowerCase().includes(text)))
|
||||
}
|
||||
|
||||
function pageOf(rows, pageNo) {
|
||||
const start = (pageNo - 1) * pageSize
|
||||
return rows.slice(start, start + pageSize)
|
||||
}
|
||||
|
||||
function money(value) {
|
||||
if (value === null || value === undefined || value === '') return '-'
|
||||
const num = Number(value)
|
||||
return Number.isNaN(num) ? value : String(num)
|
||||
}
|
||||
|
||||
function periodLabel(row) {
|
||||
if (row.effectiveStart || row.effectiveEnd) {
|
||||
return `${row.effectiveStart || '…'} 至 ${row.effectiveEnd || '…'}`
|
||||
}
|
||||
return row.periodText || row.chargeTiming || '-'
|
||||
}
|
||||
|
||||
function statusText(status) {
|
||||
return { OK: '已识别', EMPTY: '未识别到配置', MISSING: '缺少工作表' }[status] || status || '-'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pay-product + .pay-product {
|
||||
margin-top: 18px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
.pay-product-title {
|
||||
margin-bottom: 8px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.pay-summary {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.pay-filter {
|
||||
width: 320px;
|
||||
}
|
||||
.pay-edit-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.pay-hint {
|
||||
margin: 0 0 8px;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.pay-page {
|
||||
margin-top: 8px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.limit-tag {
|
||||
margin-right: 6px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,286 @@
|
||||
<template>
|
||||
<el-dialog v-model="visible" title="导入应付价卡" width="1080px" append-to-body destroy-on-close @closed="reset">
|
||||
<el-form label-width="88px" class="import-form">
|
||||
<el-form-item label="支付类型" required>
|
||||
<el-select v-model="productType" filterable placeholder="请选择支付类型" style="width: 420px">
|
||||
<el-option v-for="item in bms_pay_type" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品">
|
||||
<el-select v-model="dictValue" clearable filterable placeholder="不选则按工作表名称匹配全部字典产品" style="width: 420px">
|
||||
<el-option v-for="item in payment_product_type" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-upload
|
||||
:key="uploadKey"
|
||||
drag
|
||||
accept=".xlsx"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:disabled="recognizing"
|
||||
@change="onFile"
|
||||
>
|
||||
<el-icon class="upload-icon"><UploadFilled /></el-icon>
|
||||
<div class="el-upload__text">将 xlsx 价卡拖到此处,或<em>点击选择</em></div>
|
||||
<template #tip>
|
||||
<div class="upload-tip">选一个产品就只读对应工作表。不选时,一个文件里有多张表,只读取名称能对上字典的工作表。</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
|
||||
<div v-if="preview" v-loading="recognizing" class="preview-box">
|
||||
<el-alert
|
||||
:title="`${preview.sourceFile || ''} 归属 ${preview.supplierName || '-'} 生效 ${preview.effectiveDate || '-'} 共 ${products.length} 个产品`"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
<div class="preview-bar">
|
||||
<el-switch v-model="overwrite" active-text="已存在时另存为新版本" />
|
||||
<span class="preview-hint">未开启时,已存在的产品不能勾选,保存时会跳过。开启后产品编号不变,旧价格留在历史里,新价格从今天起生效。</span>
|
||||
</div>
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
:data="products"
|
||||
border
|
||||
size="small"
|
||||
max-height="280"
|
||||
row-key="rowKey"
|
||||
@selection-change="onSelect"
|
||||
>
|
||||
<el-table-column type="selection" width="48" :selectable="canSelect" />
|
||||
<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 />
|
||||
<el-table-column prop="relatedSheet" label="关联分区表" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="生效日期" width="110">
|
||||
<template #default="{ row }">{{ row.effectiveDate || preview.effectiveDate || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="weightCount" label="重量段" width="80" align="right" />
|
||||
<el-table-column prop="surchargeCount" label="附加费" width="80" align="right" />
|
||||
<el-table-column label="识别" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="statusType(row.parseStatus)" size="small">{{ statusText(row.parseStatus) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="库中" width="90">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.duplicated" type="warning" size="small">已存在</el-tag>
|
||||
<span v-else>新记录</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div v-if="skippedText" class="upload-tip">未匹配字典、已跳过:{{ skippedText }}</div>
|
||||
<div class="sheet-scroll">
|
||||
<button
|
||||
v-for="item in products"
|
||||
:key="item.rowKey"
|
||||
type="button"
|
||||
class="sheet-tab"
|
||||
:class="{ active: activeKey === item.rowKey }"
|
||||
@click="activeKey = item.rowKey"
|
||||
>{{ item.sheetName || item.productName }}</button>
|
||||
</div>
|
||||
<div class="preview-detail">
|
||||
<pay-config-detail v-if="activeProduct" :product="activeProduct" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" :disabled="!selected.length" :loading="saving" @click="save">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import { UploadFilled } from '@element-plus/icons-vue'
|
||||
import { previewPay, savePay } from '@/api/price/pay'
|
||||
import { useDict } from '@/utils/dict'
|
||||
import PayConfigDetail from './PayConfigDetail.vue'
|
||||
|
||||
const emit = defineEmits(['saved'])
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { payment_product_type, bms_pay_type } = useDict('payment_product_type', 'bms_pay_type')
|
||||
|
||||
const visible = ref(false)
|
||||
const recognizing = ref(false)
|
||||
const saving = ref(false)
|
||||
const overwrite = ref(false)
|
||||
const dictValue = ref('')
|
||||
const productType = ref('AP')
|
||||
const preview = ref(null)
|
||||
const selected = ref([])
|
||||
const tableRef = ref()
|
||||
const activeKey = ref('')
|
||||
const uploadKey = ref(0)
|
||||
|
||||
const products = computed(() => preview.value?.products || [])
|
||||
const activeProduct = computed(() => products.value.find(item => item.rowKey === activeKey.value) || products.value[0])
|
||||
const skippedText = computed(() => (preview.value?.skippedSheets || []).filter(Boolean).join('、'))
|
||||
|
||||
function open() {
|
||||
reset()
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
function reset() {
|
||||
preview.value = null
|
||||
selected.value = []
|
||||
overwrite.value = false
|
||||
dictValue.value = ''
|
||||
productType.value = 'AP'
|
||||
activeKey.value = ''
|
||||
recognizing.value = false
|
||||
saving.value = false
|
||||
uploadKey.value += 1
|
||||
}
|
||||
|
||||
async function onFile(uploadFile) {
|
||||
const raw = uploadFile?.raw
|
||||
if (!raw) return
|
||||
if (!raw.name.toLowerCase().endsWith('.xlsx')) {
|
||||
proxy.$modal.msgError('请上传xlsx价卡')
|
||||
return
|
||||
}
|
||||
recognizing.value = true
|
||||
try {
|
||||
const res = await previewPay(raw, dictValue.value)
|
||||
const data = res.data || {}
|
||||
data.products = (data.products || []).map((item, index) => ({
|
||||
...item,
|
||||
rowKey: `${item.sheetName || ''}-${item.productName || ''}-${index}`
|
||||
}))
|
||||
preview.value = data
|
||||
activeKey.value = data.products[0]?.rowKey || ''
|
||||
const picked = data.products.filter(row => canSelect(row))
|
||||
selected.value = picked
|
||||
await nextTick()
|
||||
tableRef.value?.clearSelection()
|
||||
picked.forEach(row => tableRef.value?.toggleRowSelection(row, true))
|
||||
} finally {
|
||||
recognizing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function canSelect(row) {
|
||||
if (row.parseStatus === 'MISSING') return false
|
||||
return overwrite.value || !row.duplicated
|
||||
}
|
||||
|
||||
watch(overwrite, async () => {
|
||||
if (!visible.value || !preview.value) return
|
||||
const picked = products.value.filter(row => canSelect(row))
|
||||
selected.value = picked
|
||||
await nextTick()
|
||||
tableRef.value?.clearSelection()
|
||||
picked.forEach(row => tableRef.value?.toggleRowSelection(row, true))
|
||||
})
|
||||
|
||||
function onSelect(rows) {
|
||||
if (!visible.value) return
|
||||
selected.value = rows || []
|
||||
}
|
||||
|
||||
async function save() {
|
||||
if (!productType.value) {
|
||||
proxy.$modal.msgError('请选择支付类型')
|
||||
return
|
||||
}
|
||||
if (!selected.value.length) {
|
||||
proxy.$modal.msgError('请勾选要保存的产品')
|
||||
return
|
||||
}
|
||||
const duplicated = selected.value.filter(row => row.duplicated).length
|
||||
const tip = overwrite.value && duplicated
|
||||
? `将保存 ${selected.value.length} 个产品,其中 ${duplicated} 个会保留旧版本并生成新版本。`
|
||||
: `将保存 ${selected.value.length} 个产品。`
|
||||
try {
|
||||
await proxy.$modal.confirm(tip)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
saving.value = true
|
||||
try {
|
||||
const products = selected.value.map(row => ({ ...row, productType: productType.value }))
|
||||
const res = await savePay({ overwrite: overwrite.value, products })
|
||||
const message = res.msg || '保存成功'
|
||||
reset()
|
||||
visible.value = false
|
||||
ElMessageBox.close()
|
||||
proxy.$modal.msgSuccess(message)
|
||||
emit('saved')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function statusText(status) {
|
||||
return { OK: '已识别', EMPTY: '未识别到配置', MISSING: '缺少工作表' }[status] || status || '-'
|
||||
}
|
||||
|
||||
function statusType(status) {
|
||||
if (status === 'OK') return 'success'
|
||||
if (status === 'EMPTY') return 'warning'
|
||||
if (status === 'MISSING') return 'danger'
|
||||
return 'info'
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.upload-icon {
|
||||
font-size: 42px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
.upload-tip {
|
||||
margin-top: 6px;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
}
|
||||
.preview-box {
|
||||
margin-top: 14px;
|
||||
}
|
||||
.preview-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.preview-hint {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
}
|
||||
.preview-detail {
|
||||
max-height: 460px;
|
||||
margin-top: 14px;
|
||||
overflow: auto;
|
||||
}
|
||||
.import-form {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.sheet-scroll {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
overflow-x: auto;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
.sheet-tab {
|
||||
flex: 0 0 auto;
|
||||
border: 1px solid var(--el-border-color);
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.sheet-tab.active {
|
||||
color: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary);
|
||||
background: var(--el-color-primary-light-9);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,396 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<basic-table
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:search-columns="searchColumns"
|
||||
:query="queryList"
|
||||
:search-per-row="4"
|
||||
search-action-align="right"
|
||||
row-key="productId"
|
||||
>
|
||||
<template #toolbar="{ selection, ids }">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Upload" @click="importRef.open()" v-hasPermi="['price:pay:import']">导入价卡</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="!selection.length" @click="handleDelete(ids)" v-hasPermi="['price:pay:remove']">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['price:pay:export']">导出列表</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExportDetail" v-hasPermi="['price:pay:export']">导出明细</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<template #col-parseStatus="{ row }">
|
||||
<el-tag :type="statusType(row.parseStatus)" size="small">{{ statusText(row.parseStatus) }}</el-tag>
|
||||
</template>
|
||||
|
||||
<template #col-termRange="{ row }">
|
||||
<span>{{ termText(row) }}</span>
|
||||
</template>
|
||||
|
||||
<template #col-termStatus="{ row }">
|
||||
<el-switch
|
||||
v-if="row.termId"
|
||||
:model-value="row.termStatus === '1' ? '1' : '0'"
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
inline-prompt
|
||||
active-text="生效"
|
||||
inactive-text="失效"
|
||||
@change="(val) => changeStatus(row, 'termStatus', val)"
|
||||
v-hasPermi="['price:pay:edit']"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #col-action="{ row }">
|
||||
<el-button link type="primary" icon="View" @click="openDetail(row)" v-hasPermi="['price:pay:query']">查看</el-button>
|
||||
<el-button link type="primary" icon="Calendar" @click="openTermDialog(row)" v-hasPermi="['price:pay:edit']">有效期</el-button>
|
||||
<el-button link type="primary" icon="Clock" @click="openHistory(row)" v-hasPermi="['price:pay:query']">历史</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(row.productId)" v-hasPermi="['price:pay:remove']">删除</el-button>
|
||||
</template>
|
||||
</basic-table>
|
||||
|
||||
<pay-import-dialog ref="importRef" @saved="tableRef.refresh()" />
|
||||
|
||||
<el-drawer v-model="historyVisible" :title="historyTitle" size="860px" destroy-on-close>
|
||||
<div class="history-bar">
|
||||
<el-button type="primary" plain :disabled="historyPicked.length !== 2" @click="openCompare">比对所选版本</el-button>
|
||||
<span>勾选两个版本后比对。历史订单按生效时间取价,不看当前开关。</span>
|
||||
</div>
|
||||
<el-table v-loading="historyLoading" :data="historyRows" border size="small" empty-text="还没有价格版本" @selection-change="onHistoryPick">
|
||||
<el-table-column type="selection" width="42" align="center" />
|
||||
<el-table-column prop="versionNo" label="版本" width="70" align="center" />
|
||||
<el-table-column prop="sourceFile" label="来源文件" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="weightCount" label="重量段" width="80" align="center" />
|
||||
<el-table-column prop="surchargeCount" label="附加费" width="80" align="center" />
|
||||
<el-table-column label="生效开始" width="110" align="center">
|
||||
<template #default="{ row }">{{ dayText(row.effectiveStart) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="生效结束" width="110" align="center">
|
||||
<template #default="{ row }">{{ dayText(row.effectiveEnd) || '不限' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-switch
|
||||
v-if="row.termId"
|
||||
:model-value="row.status === '1' ? '1' : '0'"
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
inline-prompt
|
||||
active-text="生效"
|
||||
inactive-text="失效"
|
||||
@change="(val) => changeStatus(row, 'status', val)"
|
||||
v-hasPermi="['price:pay:edit']"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="openTermDialog(row)" v-hasPermi="['price:pay:edit']">修改有效期</el-button>
|
||||
<el-button link type="primary" @click="openDetail(row)" v-hasPermi="['price:pay:query']">查看</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-drawer>
|
||||
|
||||
<el-dialog v-model="termVisible" title="修改有效期" width="460px" append-to-body>
|
||||
<el-form label-width="90px">
|
||||
<el-form-item label="生效开始" required>
|
||||
<el-date-picker v-model="termForm.effectiveStart" type="date" value-format="YYYY-MM-DD" placeholder="生效开始" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="生效结束">
|
||||
<el-date-picker v-model="termForm.effectiveEnd" type="date" value-format="YYYY-MM-DD" placeholder="不填表示不限" style="width: 100%" clearable />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="termVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="termSaving" @click="saveTerm">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-drawer v-model="compareVisible" :title="compareTitle" size="88%" append-to-body destroy-on-close>
|
||||
<el-table v-loading="compareLoading" :data="compareRows" border size="small" max-height="640" empty-text="没有差异">
|
||||
<el-table-column prop="kind" label="类型" width="90" align="center" />
|
||||
<el-table-column prop="change" label="变化" width="80" align="center" />
|
||||
<el-table-column prop="name" label="项目" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="zoneName" label="地区" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="oldValue" label="旧版" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="newValue" label="新版" min-width="160" show-overflow-tooltip />
|
||||
</el-table>
|
||||
</el-drawer>
|
||||
|
||||
<el-drawer v-model="detailVisible" :title="detailTitle" size="78%" destroy-on-close>
|
||||
<pay-config-detail v-loading="detailLoading" :product="detail" :editable="editing" />
|
||||
<template #footer>
|
||||
<el-button v-if="!editing" type="primary" @click="editing = true" v-hasPermi="['price:pay:import']">编辑</el-button>
|
||||
<el-button v-else type="primary" :loading="saving" @click="saveDetail">保存</el-button>
|
||||
<el-button @click="detailVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="PricePay">
|
||||
import { listPay, getPay, delPay, updatePay, listPayTerm, updatePayTerm, changePayTermStatus, comparePay } from '@/api/price/pay'
|
||||
import PayImportDialog from './components/PayImportDialog.vue'
|
||||
import PayConfigDetail from './components/PayConfigDetail.vue'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const tableRef = ref()
|
||||
const importRef = ref()
|
||||
const detailVisible = ref(false)
|
||||
const detailLoading = ref(false)
|
||||
const editing = ref(false)
|
||||
const saving = ref(false)
|
||||
const detail = ref(null)
|
||||
const historyVisible = ref(false)
|
||||
const historyLoading = ref(false)
|
||||
const historyRows = ref([])
|
||||
const historyTitle = ref('价格版本')
|
||||
const historyProductId = ref(null)
|
||||
const historyPicked = ref([])
|
||||
const compareVisible = ref(false)
|
||||
const compareLoading = ref(false)
|
||||
const compareTitle = ref('版本比对')
|
||||
const compareRows = ref([])
|
||||
const detailTitle = computed(() => {
|
||||
if (!detail.value) return '应付配置'
|
||||
const no = detail.value.versionNo ? `第 ${detail.value.versionNo} 版` : ''
|
||||
return `${detail.value.productName || '应付配置'} ${no}`.trim()
|
||||
})
|
||||
const termVisible = ref(false)
|
||||
const termSaving = ref(false)
|
||||
const termForm = reactive({ termId: null, productId: null, effectiveStart: '', effectiveEnd: '' })
|
||||
|
||||
const statusOptions = [
|
||||
{ label: '已识别', value: 'OK' },
|
||||
{ label: '未识别到配置', value: 'EMPTY' },
|
||||
{ label: '缺少工作表', value: 'MISSING' }
|
||||
]
|
||||
|
||||
const columns = [
|
||||
{ label: '产品编号', prop: 'productId', align: 'center', width: 90 },
|
||||
{ label: '产品名称', prop: 'productName', align: 'center', minWidth: 150, showOverflowTooltip: true },
|
||||
{ label: '价卡归属', prop: 'supplierName', align: 'center', width: 100 },
|
||||
{ label: '国家/口岸', prop: 'countryScope', align: 'center', minWidth: 120, showOverflowTooltip: true },
|
||||
{ label: '费用类型', prop: 'chargeType', align: 'center', width: 110 },
|
||||
{ label: '每公斤加价', prop: 'extraPerKg', align: 'center', width: 120 },
|
||||
{ label: '报价工作表', prop: 'sheetName', align: 'center', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '关联分区表', prop: 'relatedSheet', align: 'center', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '版本', prop: 'versionNo', align: 'center', width: 70 },
|
||||
{ label: '有效期', prop: 'termRange', align: 'center', minWidth: 180, showOverflowTooltip: true },
|
||||
{ label: '状态', prop: 'termStatus', align: 'center', width: 100 },
|
||||
{ label: '币种', prop: 'currency', align: 'center', width: 80 },
|
||||
{ label: '重量段', prop: 'weightCount', align: 'center', width: 80 },
|
||||
{ label: '附加费', prop: 'surchargeCount', align: 'center', width: 80 },
|
||||
{ label: '识别结果', prop: 'parseStatus', align: 'center', width: 120 },
|
||||
{ label: '来源文件', prop: 'sourceFile', align: 'center', minWidth: 180, showOverflowTooltip: true },
|
||||
{ label: '创建时间', prop: 'createTime', align: 'center', width: 160, parseTime: true },
|
||||
{ label: '操作', prop: 'action', align: 'center', width: 280, fixed: 'right', showInToolbar: false }
|
||||
]
|
||||
|
||||
const searchColumns = [
|
||||
{ label: '产品名称', prop: 'productName', component: 'input', placeholder: '产品名称', width: '180px' },
|
||||
{ label: '价卡归属', prop: 'supplierName', component: 'input', placeholder: '威狮、易德、GWE', width: '160px' },
|
||||
{ label: '来源文件', prop: 'sourceFile', component: 'input', placeholder: '文件名', width: '180px' },
|
||||
{ label: '报价工作表', prop: 'sheetName', component: 'input', placeholder: '工作表', width: '160px' },
|
||||
{ label: '国家/口岸', prop: 'countryScope', component: 'input', placeholder: '国家或口岸', width: '160px' },
|
||||
{ label: '服务类型', prop: 'serviceType', component: 'input', placeholder: '服务类型', width: '150px' },
|
||||
{ label: '重量段', prop: 'weightKeyword', component: 'input', placeholder: '重量段、分区或费用项', width: '180px' },
|
||||
{ label: '附加费', prop: 'surchargeName', component: 'input', placeholder: '名称、说明或时效', width: '180px' },
|
||||
{ label: '生效日期', prop: 'effectiveRange', component: 'daterange', width: '280px' },
|
||||
{ label: '识别结果', prop: 'parseStatus', component: 'select', options: statusOptions, placeholder: '全部', width: '150px' }
|
||||
]
|
||||
|
||||
function queryList(params) {
|
||||
return listPay({ ...toQuery(params), templateOnly: true, currentOnly: true })
|
||||
}
|
||||
|
||||
function toQuery(params) {
|
||||
const query = { ...params }
|
||||
if (query.effectiveRange?.length === 2) {
|
||||
query.effectiveBegin = query.effectiveRange[0]
|
||||
query.effectiveEnd = query.effectiveRange[1]
|
||||
}
|
||||
delete query.effectiveRange
|
||||
return query
|
||||
}
|
||||
|
||||
function currentQuery() {
|
||||
const query = toQuery(tableRef.value?.getQueryParams() || {})
|
||||
delete query.pageNum
|
||||
delete query.pageSize
|
||||
query.templateOnly = true
|
||||
query.currentOnly = true
|
||||
return query
|
||||
}
|
||||
|
||||
function handleExport() {
|
||||
proxy.download('/price/pay/export', currentQuery(), `应付配置_${Date.now()}.xlsx`)
|
||||
}
|
||||
|
||||
function handleExportDetail() {
|
||||
proxy.download('/price/pay/exportDetail', currentQuery(), `应付配置明细_${Date.now()}.xlsx`)
|
||||
}
|
||||
|
||||
function handleDelete(ids) {
|
||||
const idText = Array.isArray(ids) ? ids.join(',') : ids
|
||||
if (!idText) return
|
||||
proxy.$modal.confirm('确认删除所选应付产品?重量段和附加费会一起删除。').then(() => {
|
||||
return delPay(idText)
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
tableRef.value.refresh()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function openDetail(row) {
|
||||
detailVisible.value = true
|
||||
editing.value = false
|
||||
detailLoading.value = true
|
||||
detail.value = null
|
||||
getPay(row.versionProductId || row.productId).then(res => {
|
||||
detail.value = res.data
|
||||
}).finally(() => {
|
||||
detailLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function saveDetail() {
|
||||
cleanMoney(detail.value)
|
||||
saving.value = true
|
||||
updatePay(detail.value).then(() => {
|
||||
proxy.$modal.msgSuccess('已保存这一版。产品编号不变,绑定该产品的渠道按有效期取价')
|
||||
editing.value = false
|
||||
tableRef.value.refresh()
|
||||
}).finally(() => {
|
||||
saving.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function cleanMoney(product) {
|
||||
;(product?.weightBands || []).forEach(row => {
|
||||
if (row.price === '') row.price = null
|
||||
})
|
||||
}
|
||||
|
||||
function dayText(value) {
|
||||
if (!value) return ''
|
||||
return typeof value === 'string' ? value.slice(0, 10) : proxy.parseTime(value, '{y}-{m}-{d}')
|
||||
}
|
||||
|
||||
function termText(row) {
|
||||
const start = dayText(row.termStart || row.effectiveDate)
|
||||
const end = dayText(row.termEnd)
|
||||
if (!start && !end) return '-'
|
||||
return `${start || '不限'} 至 ${end || '不限'}`
|
||||
}
|
||||
|
||||
function openHistory(row) {
|
||||
historyProductId.value = row.productId
|
||||
historyTitle.value = `${row.productName || '产品'}(编号 ${row.productId})的价格版本`
|
||||
historyPicked.value = []
|
||||
historyVisible.value = true
|
||||
loadHistory()
|
||||
}
|
||||
|
||||
function onHistoryPick(rows) {
|
||||
historyPicked.value = rows || []
|
||||
}
|
||||
|
||||
function openCompare() {
|
||||
if (historyPicked.value.length !== 2) return
|
||||
const [first, second] = historyPicked.value
|
||||
compareVisible.value = true
|
||||
compareLoading.value = true
|
||||
compareRows.value = []
|
||||
comparePay(first.productId, second.productId).then(res => {
|
||||
const data = res.data || {}
|
||||
const older = data.older?.versionNo || '-'
|
||||
const newer = data.newer?.versionNo || '-'
|
||||
compareTitle.value = `第 ${older} 版 对比 第 ${newer} 版`
|
||||
const weights = (data.weightDiffs || []).map(row => ({ ...row, kind: '重量段' }))
|
||||
const fees = (data.surchargeDiffs || []).map(row => ({ ...row, kind: '附加费' }))
|
||||
compareRows.value = weights.concat(fees).filter(row => row.change !== '相同')
|
||||
}).finally(() => {
|
||||
compareLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function loadHistory() {
|
||||
if (!historyProductId.value) return
|
||||
historyLoading.value = true
|
||||
listPayTerm(historyProductId.value).then(res => {
|
||||
historyRows.value = res.data || []
|
||||
}).finally(() => {
|
||||
historyLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function openTermDialog(row) {
|
||||
termForm.termId = row.termId
|
||||
termForm.productId = row.productId
|
||||
termForm.effectiveStart = dayText(row.termStart || row.effectiveStart || row.effectiveDate)
|
||||
termForm.effectiveEnd = dayText(row.termEnd || row.effectiveEnd)
|
||||
termVisible.value = true
|
||||
}
|
||||
|
||||
function saveTerm() {
|
||||
if (!termForm.effectiveStart) {
|
||||
proxy.$modal.msgError('请填写生效开始')
|
||||
return
|
||||
}
|
||||
termSaving.value = true
|
||||
updatePayTerm({
|
||||
termId: termForm.termId,
|
||||
productId: termForm.productId,
|
||||
effectiveStart: termForm.effectiveStart,
|
||||
effectiveEnd: termForm.effectiveEnd || null
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess('有效期已保存')
|
||||
termVisible.value = false
|
||||
tableRef.value.refresh()
|
||||
if (historyVisible.value) loadHistory()
|
||||
}).finally(() => {
|
||||
termSaving.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function changeStatus(row, field, status) {
|
||||
if (!row?.termId || (status !== '0' && status !== '1') || row[field] === status) return
|
||||
const previous = row[field]
|
||||
row[field] = status
|
||||
changePayTermStatus({ termId: row.termId, productId: row.productId, status }).then(() => {
|
||||
proxy.$modal.msgSuccess(status === '0' ? '已设为生效' : '已设为失效')
|
||||
tableRef.value.refresh()
|
||||
if (historyVisible.value) loadHistory()
|
||||
}).catch(() => {
|
||||
row[field] = previous
|
||||
})
|
||||
}
|
||||
|
||||
function statusText(status) {
|
||||
return statusOptions.find(item => item.value === status)?.label || status || '-'
|
||||
}
|
||||
|
||||
function statusType(status) {
|
||||
if (status === 'OK') return 'success'
|
||||
if (status === 'EMPTY') return 'warning'
|
||||
if (status === 'MISSING') return 'danger'
|
||||
return 'info'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.history-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user