修改公共配置字典

This commit is contained in:
wuweihua
2026-09-08 10:51:20 +08:00
parent 647be14d66
commit 38796d8683
25 changed files with 389 additions and 80 deletions
+8
View File
@@ -25,6 +25,14 @@ export function getDicts(dictType) {
})
}
// 全量查询字典(按 dictType 分组)
export function getDictAll() {
return request({
url: '/system/dict/data/all',
method: 'get'
})
}
// 新增字典数据
export function addData(data) {
return request({
+4
View File
@@ -9,6 +9,7 @@ import useUserStore from '@/store/modules/user'
import useLockStore from '@/store/modules/lock'
import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission'
import useDictStore from '@/store/modules/dict'
NProgress.configure({ showSpinner: false })
@@ -43,6 +44,8 @@ router.beforeEach(async (to, from) => {
try {
// 拉取user_info信息
await useUserStore().getInfo()
// 全量字典单独接口,按 dictType code 本地匹配
await useDictStore().loadAllDicts()
isRelogin.show = false
// 根据roles权限生成可访问的路由
const accessRoutes = await usePermissionStore().generateRoutes()
@@ -57,6 +60,7 @@ router.beforeEach(async (to, from) => {
return { ...to, replace: true }
} catch (err) {
await useUserStore().logOut()
useDictStore().cleanDict()
ElMessage.error(err)
return { path: '/' }
}
+41 -7
View File
@@ -1,13 +1,16 @@
import { getDictAll } from '@/api/system/dict/data'
const useDictStore = defineStore(
'dict',
{
state: () => ({
dict: new Array()
dict: new Array(),
loaded: false
}),
actions: {
// 获取字典
getDict(_key) {
if (_key == null && _key == "") {
if (_key == null || _key === '') {
return null
}
try {
@@ -19,19 +22,49 @@ const useDictStore = defineStore(
} catch (e) {
return null
}
return null
},
// 设置字典
// 设置字典(同 key 覆盖)
setDict(_key, value) {
if (_key !== null && _key !== "") {
if (_key !== null && _key !== '') {
for (let i = 0; i < this.dict.length; i++) {
if (this.dict[i].key == _key) {
this.dict[i].value = value
return
}
}
this.dict.push({
key: _key,
value: value
})
}
},
// 全量写入:{ dictType: [{dictLabel,dictValue,...}] }
setDictMap(dictMap) {
this.cleanDict()
if (!dictMap || typeof dictMap !== 'object') {
return
}
Object.keys(dictMap).forEach(dictType => {
const list = dictMap[dictType] || []
this.setDict(dictType, list.map(p => ({
label: p.dictLabel,
value: p.dictValue,
elTagType: p.listClass,
elTagClass: p.cssClass
})))
})
this.loaded = true
},
// 登录后单独拉取全量字典
loadAllDicts() {
return getDictAll().then(res => {
this.setDictMap(res.data || {})
return res.data
})
},
// 删除字典
removeDict(_key) {
var bln = false
try {
for (let i = 0; i < this.dict.length; i++) {
if (this.dict[i].key == _key) {
@@ -40,13 +73,14 @@ const useDictStore = defineStore(
}
}
} catch (e) {
bln = false
return false
}
return bln
return false
},
// 清空字典
cleanDict() {
this.dict = new Array()
this.loaded = false
},
// 初始字典
initDict() {
+2
View File
@@ -5,6 +5,7 @@ import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { isHttp, isEmpty } from "@/utils/validate"
import useLockStore from '@/store/modules/lock'
import useDictStore from '@/store/modules/dict'
import defAva from '@/assets/images/profile.jpg'
const useUserStore = defineStore(
@@ -82,6 +83,7 @@ const useUserStore = defineStore(
this.token = ''
this.roles = []
this.permissions = []
useDictStore().cleanDict()
removeToken()
resolve()
}).catch(error => {
+10 -4
View File
@@ -2,23 +2,29 @@ import useDictStore from '@/store/modules/dict'
import { getDicts } from '@/api/system/dict/data'
/**
* 获取字典数据
* 获取字典数据(优先使用登录后全量缓存,按 dictType code 匹配)
*/
export function useDict(...args) {
const res = ref({})
return (() => {
args.forEach((dictType, index) => {
args.forEach((dictType) => {
res.value[dictType] = []
const dicts = useDictStore().getDict(dictType)
if (dicts) {
res.value[dictType] = dicts
} else {
// 缓存未命中时按类型补拉(如字典管理刚改完被 removeDict)
getDicts(dictType).then(resp => {
res.value[dictType] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass }))
res.value[dictType] = (resp.data || []).map(p => ({
label: p.dictLabel,
value: p.dictValue,
elTagType: p.listClass,
elTagClass: p.cssClass
}))
useDictStore().setDict(dictType, res.value[dictType])
})
}
})
return toRefs(res.value)
})()
}
}
+1 -2
View File
@@ -195,10 +195,9 @@
<script setup name="Region">
import { listRegion, getRegion, updateRegion, delRegion, changeRegionStatus, syncRegion } from "@/api/data/region"
import { getSyncLog, listSyncLog, delSyncLog } from "@/api/data/region-log"
import { nextTick, onBeforeUnmount } from "vue"
import { nextTick, onBeforeUnmount, onMounted, ref, reactive, toRefs, watch, getCurrentInstance } from "vue"
const { proxy } = getCurrentInstance()
const tableRef = ref()
const open = ref(false)
const title = ref("")
@@ -30,26 +30,18 @@
</el-col>
</template>
<template #col-taxMode="{ row }">
<el-tag :type="row.taxMode === 'TAX_INCLUDED' ? 'success' : 'warning'" size="small">
{{ row.taxMode === 'TAX_INCLUDED' ? '包税' : '不包税' }}
</el-tag>
</template>
<template #col-cargoAttr="{ row }">
<el-tag type="primary" size="small" effect="plain">{{ row.cargoAttr || '-' }}</el-tag>
</template>
<template #col-status="{ row }">
<el-tag :type="row.status === '0' ? 'success' : 'info'" size="small">
{{ row.status === '0' ? '正常' : '停用' }}
</el-tag>
<dict-tag :options="bms_product_effect_status" :value="resolveEffectStatus(row)" />
</template>
<template #col-action="{ row }">
<el-button link type="primary" icon="View" @click="openCountries(row)" v-hasPermi="['price:product:query']">产品明细</el-button>
<el-button :disabled="isProductActive(row)" :title="isProductActive(row) ? '生效中不可编辑' : ''" link type="primary" icon="Edit" @click="handleUpdate(row)" v-hasPermi="['price:product:edit']">编辑</el-button>
<el-button :disabled="isProductActive(row)" :title="isProductActive(row) ? '生效中不可删除' : ''" link type="primary" icon="Delete" @click="handleDelete(row.productId)" v-hasPermi="['price:product:remove']">删除</el-button>
<el-button :disabled="isProductActive(row)" :title="isProductActive(row) ? '已生效不可编辑' : ''" link type="primary" icon="Edit" @click="handleUpdate(row)" v-hasPermi="['price:product:edit']">编辑</el-button>
<el-button :disabled="isProductActive(row)" :title="isProductActive(row) ? '已生效不可删除' : ''" link type="primary" icon="Delete" @click="handleDelete(row.productId)" v-hasPermi="['price:product:remove']">删除</el-button>
</template>
</basic-table>
@@ -88,8 +80,12 @@
<el-col :span="12">
<el-form-item label="税模式" prop="taxMode">
<el-select v-model="form.taxMode" placeholder="请选择" style="width: 100%">
<el-option label="包税" value="TAX_INCLUDED" />
<el-option label="不包税" value="TAX_EXCLUDED" />
<el-option
v-for="dict in bms_tax_mode"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
@@ -103,8 +99,8 @@
<el-col :span="12">
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio value="0">正常</el-radio>
<el-radio value="1">停用</el-radio>
<el-radio value="0">启用(按生效日期自动:待生效/已生效)</el-radio>
<el-radio value="1">已失效</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
@@ -146,6 +142,10 @@
</template>
</el-table-column>
<el-table-column label="时效" prop="leadTime" width="100" show-overflow-tooltip />
<el-table-column label="生效日期" prop="effectiveDate" width="110" align="center" />
<el-table-column label="失效日期" width="110" align="center">
<template #default="{ row }">{{ row.expireDate || '长期' }}</template>
</el-table-column>
</el-table>
</el-drawer>
@@ -154,7 +154,7 @@
title="产品 Excel 导入"
action="/price/product/importData"
:default-update-support="true"
update-support-label="是否更新已经存在的产品主数据(国家/运费明细每次导入都会覆盖)"
update-support-label="是否更新已存在的产品主数据(国家明细:新生效日晚于上次则保留历史,上次失效日=本次生效日)"
@success="() => tableRef.refresh()"
/>
</div>
@@ -165,6 +165,7 @@ import ExcelImportDialog from '@/components/ExcelImportDialog'
import { listProduct, getProduct, addProduct, updateProduct, delProduct, listProductCountries } from '@/api/price/product'
const { proxy } = getCurrentInstance()
const { bms_product_effect_status, bms_tax_mode } = useDict('bms_product_effect_status', 'bms_tax_mode')
const tableRef = ref()
const importRef = ref()
const formRef = ref()
@@ -190,34 +191,32 @@ const columns = [
{ prop: 'serviceLevel', label: '时效等级', width: 90, align: 'center' },
{ prop: 'cargoAttr', label: '走货属性', minWidth: 120, slot: 'cargoAttr' },
{ prop: 'destRegion', label: '目的区域', width: 90, align: 'center' },
{ prop: 'taxMode', label: '税模式', width: 90, align: 'center', slot: 'taxMode' },
{ prop: 'taxMode', label: '税模式', width: 90, align: 'center', dict: 'bms_tax_mode' },
{ prop: 'effectiveDate', label: '生效日期', width: 120, align: 'center' },
{ prop: 'countryCount', label: '国家数', width: 80, align: 'center' },
{ prop: 'status', label: '状态', width: 80, align: 'center', slot: 'status' },
{ prop: 'status', label: '状态', width: 90, align: 'center', slot: 'status' },
{ prop: 'updateNote', label: '更新内容', minWidth: 160, showOverflowTooltip: true },
{ prop: 'action', label: '操作', width: 240, fixed: 'right', slot: 'action' }
]
const searchColumns = [
{ prop: 'productCode', label: '产品代码' },
{ prop: 'productName', label: '产品名称' },
{ prop: 'productCode', label: '产品代码', component: 'input', placeholder: '产品代码' },
{ prop: 'productName', label: '产品名称', component: 'input', placeholder: '产品名称' },
{
prop: 'taxMode',
label: '税模式',
type: 'select',
options: [
{ label: '包税', value: 'TAX_INCLUDED' },
{ label: '不包税', value: 'TAX_EXCLUDED' }
]
component: 'select',
dict: 'bms_tax_mode',
placeholder: '税模式',
clearable: true
},
{
prop: 'status',
prop: 'effectStatus',
label: '状态',
type: 'select',
options: [
{ label: '正常', value: '0' },
{ label: '停用', value: '1' }
]
component: 'select',
dict: 'bms_product_effect_status',
placeholder: '请选择状态',
clearable: true
}
]
@@ -235,13 +234,21 @@ function queryList(params) {
return listProduct(params)
}
// 判断产品是否"生效中":status='0'(未停用)且 effectiveDate <= 今天
function todayStr() {
return new Date().toISOString().slice(0, 10)
}
/** ACTIVE已生效 / PENDING待生效 / EXPIRED已失效 */
function resolveEffectStatus(row) {
if (!row) return 'PENDING'
if (row.status === '1') return 'EXPIRED'
if (!row.effectiveDate) return 'PENDING'
return String(row.effectiveDate).slice(0, 10) <= todayStr() ? 'ACTIVE' : 'PENDING'
}
// 已生效不可编辑/删除
function isProductActive(row) {
if (!row) return false
if (row.status !== '0') return false
if (!row.effectiveDate) return false
const today = new Date().toISOString().slice(0, 10)
return String(row.effectiveDate).slice(0, 10) <= today
return resolveEffectStatus(row) === 'ACTIVE'
}
function reset() {
@@ -269,7 +276,7 @@ function handleAdd() {
function handleUpdate(row) {
if (isProductActive(row)) {
proxy.$modal.msgError('生效中的产品不可编辑')
proxy.$modal.msgError('已生效的产品不可编辑')
return
}
reset()
+2 -3
View File
@@ -59,7 +59,7 @@
style="width: 100%"
/>
</el-form-item>
<el-form-item label="计数时间" prop="bizDate">
<el-form-item label="计费时间" prop="bizDate">
<el-date-picker
v-model="form.bizDate"
type="date"
@@ -67,7 +67,6 @@
placeholder="默认今天,用于筛选已生效的产品明细"
style="width: 100%"
/>
<div class="form-hint">只匹配 effective_date ≤ 计数时间的产品明细;多条匹配时取最新</div>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="loading" @click="handleQuote">计算价格</el-button>
@@ -171,7 +170,7 @@ function handleProductChange(productCode) {
return
}
listProductCountries(productCode).then(res => {
countryOptions.value = uniqueCountries(res.data || [])
countryOptions.value = uniqueCountries((res.data || []).filter(item => !item.expireDate))
})
}
+1 -1
View File
@@ -323,7 +323,7 @@ function ensureCountries(productCode) {
if (!productCode) return Promise.resolve([])
if (countryCache[productCode]) return Promise.resolve(countryCache[productCode])
return listProductCountries(productCode).then(res => {
const rows = res.data || []
const rows = (res.data || []).filter(item => !item.expireDate)
countryCache[productCode] = rows
return rows
})
+1 -1
View File
@@ -281,7 +281,7 @@ function ensureCountries(productCode) {
if (!productCode) return Promise.resolve([])
if (countryCache[productCode]) return Promise.resolve(countryCache[productCode])
return listProductCountries(productCode).then(res => {
const rows = res.data || []
const rows = (res.data || []).filter(item => !item.expireDate)
countryCache[productCode] = rows
return rows
})
+4 -1
View File
@@ -114,6 +114,7 @@
import DictDataDrawer from './detail'
import useDictStore from '@/store/modules/dict'
import { listType, getType, delType, addType, updateType, refreshCache } from "@/api/system/dict/type"
import { getDictAll } from "@/api/system/dict/data"
const { proxy } = getCurrentInstance()
const { sys_normal_disable } = useDict("sys_normal_disable")
@@ -248,8 +249,10 @@ function handleExport() {
/** 刷新缓存按钮操作 */
function handleRefreshCache() {
refreshCache().then(() => {
return getDictAll()
}).then(res => {
useDictStore().setDictMap(res.data)
proxy.$modal.msgSuccess("刷新成功")
useDictStore().cleanDict()
})
}
</script>