Refactor billing options retrieval to include data permissions and add effective date handling for product imports

This commit is contained in:
wuweihua
2026-10-09 09:27:28 +08:00
parent fea34d1d10
commit c95a9861cc
42 changed files with 984 additions and 219 deletions
@@ -63,13 +63,13 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="生效日期" prop="effectiveDate">
<el-date-picker v-model="editForm.effectiveDate" type="date" value-format="YYYY-MM-DD" placeholder="选择日期" style="width: 100%" />
<el-form-item label="生效时间" prop="effectiveDate">
<el-date-picker v-model="editForm.effectiveDate" type="datetime" format="YYYY-MM-DD HH:00" value-format="YYYY-MM-DD HH:00:00" placeholder="选择时间" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="失效日期" prop="expireDate">
<el-date-picker v-model="editForm.expireDate" type="date" value-format="YYYY-MM-DD" placeholder="留空为长期" style="width: 100%" />
<el-form-item label="失效时间" prop="expireDate">
<el-date-picker v-model="editForm.expireDate" type="datetime" format="YYYY-MM-DD HH:00" value-format="YYYY-MM-DD HH:00:00" placeholder="留空为长期" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="24">
@@ -112,8 +112,8 @@ const columns = [
{ prop: 'sizeLimit', label: '尺寸限制', minWidth: 140 },
{ prop: 'lastMile', label: '尾程', minWidth: 110 },
{ prop: 'leadTime', label: '时效', width: 100, showOverflowTooltip: true },
{ prop: 'effectiveDate', label: '生效日期', width: 110, align: 'center' },
{ prop: 'expireDate', label: '失效日期', width: 110, align: 'center', formatter: (row) => row.expireDate || '长期' },
{ prop: 'effectiveDate', label: '生效时间', width: 160, align: 'center' },
{ prop: 'expireDate', label: '失效时间', width: 110, align: 'center', formatter: (row) => row.expireDate || '长期' },
{ prop: 'status', label: '状态', width: 90, align: 'center', dict: 'bms_product_effect_status' },
// { prop: 'action', label: '操作', width: 70, fixed: 'right', slot: 'action' }
]
@@ -176,6 +176,15 @@ const editTitle = ref('编辑国家明细')
const editFormRef = ref()
const saving = ref(false)
const editRow = ref({})
function normalizeDrawerHour(val) {
if (val == null || val === '') return ''
const s = String(val).trim().replace('T', ' ')
const m = s.match(/^(\d{4}-\d{2}-\d{2})[ ](\d{2})/)
if (m) return m[1] + ' ' + m[2] + ':00:00'
if (/^\d{4}-\d{2}-\d{2}$/.test(s)) return s + ' 00:00:00'
return s
}
const editForm = reactive({
detailId: null,
weightMin: null,
@@ -202,7 +211,7 @@ const editRules = {
trigger: 'blur'
}],
freightPrice: [{ required: true, message: '运费不能为空', trigger: 'blur' }],
effectiveDate: [{ required: true, message: '生效日期不能为空', trigger: 'change' }]
effectiveDate: [{ required: true, message: '生效时间不能为空', trigger: 'change' }]
}
function openEdit(row) {
@@ -217,8 +226,8 @@ function openEdit(row) {
sizeLimit: row.sizeLimit || '',
lastMile: row.lastMile || '',
leadTime: row.leadTime || '',
effectiveDate: row.effectiveDate ? String(row.effectiveDate).slice(0, 10) : '',
expireDate: row.expireDate ? String(row.expireDate).slice(0, 10) : ''
effectiveDate: row.effectiveDate ? normalizeDrawerHour(row.effectiveDate) : '',
expireDate: row.expireDate ? normalizeDrawerHour(row.expireDate) : '',
})
editVisible.value = true
nextTick(() => editFormRef.value?.clearValidate())
@@ -80,19 +80,19 @@ const schemas = computed(() => [
// rules: [{ required: true, message: '请选择税模式', trigger: 'change' }]
// },
{
label: '生效日期',
label: '生效时间',
prop: 'effectiveDate',
component: 'date',
placeholder: '选择日期',
component: 'datetime',
placeholder: '选择时间',
span: 12,
width: '100%',
rules: [{ required: true, message: '生效日期不能为空', trigger: 'change' }]
rules: [{ required: true, message: '生效时间不能为空', trigger: 'change' }]
},
{
label: '失效日期',
label: '失效时间',
prop: 'expireDate',
component: 'date',
placeholder: '选择日期',
component: 'datetime',
placeholder: '选择时间',
span: 12,
width: '100%'
},
@@ -5,7 +5,7 @@
:closable="false"
show-icon
class="mb16"
title="多国家价表用「全部导入」。单国专线成本价用「按国家导入」:先选目的国家,目录里的产品都记到该国。相同产品代码+生效日期不覆盖主数据。两种导入均可在弹窗内下载对应模板。"
title="导入前须先选择生效时间;产品与国家明细均按此时间落库,上一版产品/明细失效时间设为该时间。多国家价表用「全部导入」,单国专线成本价用「按国家导入」。相同产品代码+生效日期不覆盖主数据。"
/>
<basic-table
@@ -65,9 +65,27 @@
template-url="/templates/product/product-import-all-template.xlsx"
template-file-name="产品多国家价表导入模板"
:import-desc="allImportDesc"
:before-submit="validateImportEffectiveDate"
:submit-handler="runImportAllAsync"
@success="() => tableRef.refresh()"
/>
>
<template #extra>
<el-form label-width="90px">
<el-form-item label="生效时间" required>
<el-date-picker
v-model="importEffectiveDate"
type="datetime"
format="YYYY-MM-DD HH:00"
value-format="YYYY-MM-DD HH:00:00"
popper-class="product-import-hour-picker"
placeholder="请选择生效时间"
style="width: 100%"
@change="onImportEffectiveTimeChange"
/>
</el-form-item>
</el-form>
</template>
</excel-import-dialog>
<excel-import-dialog
ref="countryImportRef"
title="按国家导入"
@@ -82,6 +100,18 @@
>
<template #extra>
<el-form label-width="90px">
<el-form-item label="生效时间" required>
<el-date-picker
v-model="importEffectiveDate"
type="datetime"
format="YYYY-MM-DD HH:00"
value-format="YYYY-MM-DD HH:00:00"
popper-class="product-import-hour-picker"
placeholder="请选择生效时间"
style="width: 100%"
@change="onImportEffectiveTimeChange"
/>
</el-form-item>
<el-form-item label="目的国家" required>
<el-select v-model="importCountryCode" filterable clearable placeholder="请选择国家" style="width: 100%">
<el-option
@@ -133,6 +163,31 @@ const expiredRef = ref()
const importRef = ref()
const countryImportRef = ref()
const importCountryCode = ref('')
const importEffectiveDate = ref('')
function pad2(n) {
return String(n).padStart(2, '0')
}
function formatImportHour(d) {
if (!d || Number.isNaN(d.getTime())) return undefined
return d.getFullYear() + '-' + pad2(d.getMonth() + 1) + '-' + pad2(d.getDate())
+ ' ' + pad2(d.getHours()) + ':00:00'
}
function normalizeImportToHour(val) {
if (val == null || val === '') return val
if (val instanceof Date) return formatImportHour(val)
const s = String(val).trim().replace('T', ' ')
const m = s.match(/^(\d{4}-\d{2}-\d{2})[ ](\d{2})/)
if (m) return m[1] + ' ' + m[2] + ':00:00'
if (/^\d{4}-\d{2}-\d{2}$/.test(s)) return s + ' 00:00:00'
return formatImportHour(new Date(s.replace(/-/g, '/'))) || val
}
function onImportEffectiveTimeChange(val) {
importEffectiveDate.value = normalizeImportToHour(val) || ''
}
const regionOptions = ref([])
const importProgressVisible = ref(false)
const importProgressPercent = ref(0)
@@ -140,12 +195,12 @@ const importProgressMessage = ref('')
const importProgressStatus = ref(undefined)
const allImportDesc = [
'适用多国家价表(B2C 小包价表等),按目录 sheet 匹配国家导入。',
'生效日期必填。产品+国家有新生效日时,先失效该国家下全部旧重量段,再插入 Excel 重量段。',
'必选生效时间:产品与 product_country 明细均按此时间计费;上一版产品/明细失效时间设为该时间。',
'请在弹窗底部下载模板,按模板格式整理后上传。相同产品代码+生效日期不覆盖主数据。'
]
const countryImportDesc = [
'选择目的国家后上传专线成本价表,目录中的产品都会记到该国家。',
'生效日期必填。产品+国家有新生效日时,先失效该国家下全部旧重量段,再插入 Excel 重量段。',
'必选生效时间:产品与 product_country 明细均按此时间计费;上一版产品/明细失效时间设为该时间。',
'邮编、赔偿标准等 sheet 不导入。可在弹窗底部下载模板。'
]
@@ -155,8 +210,8 @@ const columns = [
{ prop: 'serviceLevel', label: '时效等级', width: 90, align: 'center' },
{ prop: 'cargoAttr', label: '走货属性', minWidth: 120, slot: 'cargoAttr' },
{ prop: 'destRegion', label: '目的区域', width: 90, align: 'center' },
{ prop: 'effectiveDate', label: '生效日期', width: 120, align: 'center' },
{ prop: 'expireDate', label: '失效日期', width: 120, align: 'center' },
{ prop: 'effectiveDate', label: '生效时间', width: 160, align: 'center' },
{ prop: 'expireDate', label: '失效时间', width: 160, align: 'center' },
{ prop: 'countryCount', label: '国家数', width: 80, align: 'center' },
{ prop: 'surchargeCount', label: '附加费数', width: 90, align: 'center' },
{ prop: 'status', label: '状态', width: 90, align: 'center', slot: 'status' },
@@ -190,6 +245,7 @@ function handleImportCommand(command) {
}
function handleImport() {
importEffectiveDate.value = ''
importRef.value.open()
}
@@ -208,11 +264,21 @@ function loadRegionOptions() {
function handleCountryImport() {
importCountryCode.value = ''
importEffectiveDate.value = ''
loadRegionOptions()
countryImportRef.value.open()
}
function validateImportEffectiveDate() {
if (!importEffectiveDate.value) {
return '请先选择生效时间'
}
return ''
}
function validateCountryImport() {
const effErr = validateImportEffectiveDate()
if (effErr) return effErr
if (!importCountryCode.value) {
return '请先选择目的国家'
}
@@ -266,7 +332,11 @@ async function runImportAllAsync(uploadFile) {
if (!raw) {
throw new Error('请先选择文件')
}
return runImportWithProgress((onProgress) => importProductExcelAsync(raw, onProgress))
if (!importEffectiveDate.value) {
throw new Error('请先选择生效时间')
}
const eff = importEffectiveDate.value
return runImportWithProgress((onProgress) => importProductExcelAsync(raw, eff, onProgress))
}
async function runImportCountryAsync(uploadFile) {
@@ -274,11 +344,15 @@ async function runImportCountryAsync(uploadFile) {
if (!raw) {
throw new Error('请先选择文件')
}
if (!importEffectiveDate.value) {
throw new Error('请先选择生效时间')
}
if (!importCountryCode.value) {
throw new Error('请先选择目的国家')
}
const code = importCountryCode.value
return runImportWithProgress((onProgress) => importProductCountryExcelAsync(raw, code, onProgress))
const eff = importEffectiveDate.value
return runImportWithProgress((onProgress) => importProductCountryExcelAsync(raw, code, eff, onProgress))
}
</script>