| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- /**
- * 字典数据管理 Composable
- * 用于加载和缓存字典数据
- *
- * 使用示例:
- * import { useDict } from '@/composables/useDict'
- *
- * // 在 setup 中使用
- * const { dict } = useDict('case_status', 'matter_type')
- *
- * // 访问字典数据
- * dict.case_status // [{ label: '待处理', value: '1', ... }, ...]
- */
- import { onMounted, ref } from 'vue'
- import { request } from '@/utils'
- // 全局字典缓存
- const dictCache = new Map()
- const dictPendingCache = new Map()
- /**
- * 加载字典数据
- * @param {string} dictType - 字典类型
- * @returns {Promise<Array>} 字典数据列表
- */
- async function loadDictData(dictType) {
- try {
- const res = await request.get('/system/dict/data/list', {
- params: { dictType },
- needTip: false,
- })
- if (res.code === 200) {
- // 转换为标准格式:{ label, value, ... }
- const dictList = (res.data || []).map(item => ({
- label: item.dictLabel,
- value: item.dictValue,
- dictCode: item.dictCode,
- dictSort: item.dictSort,
- parentDictCode: item.parentDictCode,
- linkedDictType: item.linkedDictType,
- linkedDictValue: item.linkedDictValue,
- cssClass: item.cssClass,
- listClass: item.listClass || 'default', // 默认为 default
- isDefault: item.isDefault,
- status: item.dictStatus !== undefined ? item.dictStatus : item.status, // 兼容不同字段名
- remark: item.remark,
- raw: item, // 保留原始数据
- }))
- // 按排序字段排序
- dictList.sort((a, b) => (a.dictSort || 0) - (b.dictSort || 0))
- return dictList
- }
- throw new Error(res?.message || `字典 ${dictType} 响应无效`)
- }
- catch (error) {
- console.error(`加载字典 ${dictType} 失败:`, error)
- throw error
- }
- }
- /**
- * 获取原始字典请求。失败时保持 rejected,供 useDict 逐项处理;
- * 只有成功响应(包括合法空字典)才会进入全局缓存。
- * @param {string} dictType - 字典类型
- * @param {boolean} forceReload - 是否强制重新加载
- * @returns {Promise<Array>} 字典数据列表
- */
- function getDictRequest(dictType, forceReload = false) {
- if (!forceReload && dictCache.has(dictType))
- return Promise.resolve(dictCache.get(dictType))
- // 强制刷新也复用正在进行的请求,避免同一字典并发重复加载。
- if (dictPendingCache.has(dictType))
- return dictPendingCache.get(dictType)
- const pending = loadDictData(dictType)
- .then((data) => {
- dictCache.set(dictType, data)
- return data
- })
- .finally(() => {
- if (dictPendingCache.get(dictType) === pending)
- dictPendingCache.delete(dictType)
- })
- dictPendingCache.set(dictType, pending)
- return pending
- }
- /**
- * 获取字典数据(带缓存)
- * @param {string} dictType - 字典类型
- * @param {boolean} forceReload - 是否强制重新加载
- * @returns {Promise<Array>} 字典数据列表
- */
- async function getDictData(dictType, forceReload = false) {
- try {
- return await getDictRequest(dictType, forceReload)
- }
- catch {
- // 保持公共函数原有的安全返回契约,但失败结果不会污染全局缓存。
- return []
- }
- }
- /**
- * 清除字典缓存
- * @param {string} dictType - 字典类型,不传则清除所有
- */
- function clearDictCache(dictType) {
- if (dictType) {
- dictCache.delete(dictType)
- dictPendingCache.delete(dictType)
- }
- else {
- dictCache.clear()
- dictPendingCache.clear()
- }
- }
- /**
- * 字典 Composable
- * @param {...string} dictTypes - 字典类型列表
- * @returns {object} { dict, loading, errors, reload }
- */
- export function useDict(...dictTypes) {
- const dict = ref({})
- const loading = ref(false)
- const errors = ref({})
- function applySettledResults(types, results) {
- const nextErrors = { ...errors.value }
- const failedTypes = []
- types.forEach((type, index) => {
- const result = results[index]
- if (result.status === 'fulfilled') {
- dict.value[type] = result.value
- delete nextErrors[type]
- return
- }
- failedTypes.push(type)
- nextErrors[type] = result.reason?.message || `字典 ${type} 加载失败`
- })
- errors.value = nextErrors
- return failedTypes
- }
- async function loadDictTypes(types, forceReload = false, retryOnce = false) {
- const results = await Promise.allSettled(
- types.map(type => getDictRequest(type, forceReload)),
- )
- const failedTypes = applySettledResults(types, results)
- if (retryOnce && failedTypes.length > 0) {
- const retryResults = await Promise.allSettled(
- failedTypes.map(type => getDictRequest(type, true)),
- )
- applySettledResults(failedTypes, retryResults)
- }
- }
- /**
- * 加载所有字典
- */
- async function loadAllDicts() {
- if (dictTypes.length === 0)
- return
- loading.value = true
- try {
- await loadDictTypes(dictTypes, false, true)
- }
- catch (error) {
- console.error('加载字典失败:', error)
- }
- finally {
- loading.value = false
- }
- }
- /**
- * 重新加载字典
- * @param {...string} types - 要重新加载的字典类型,不传则重新加载所有
- */
- async function reload(...types) {
- const typesToReload = types.length > 0 ? types : dictTypes
- loading.value = true
- try {
- await loadDictTypes(typesToReload, true)
- }
- catch (error) {
- console.error('重新加载字典失败:', error)
- }
- finally {
- loading.value = false
- }
- }
- /**
- * 根据字典值获取标签
- * @param {string} dictType - 字典类型
- * @param {string | number} value - 字典值
- * @returns {string} 字典标签
- */
- function getLabel(dictType, value) {
- const dictList = dict.value[dictType] || []
- const item = dictList.find(d => String(d.value) === String(value))
- return item ? item.label : value
- }
- /**
- * 根据字典值获取字典项
- * @param {string} dictType - 字典类型
- * @param {string | number} value - 字典值
- * @returns {object | null} 字典项
- */
- function getDict(dictType, value) {
- const dictList = dict.value[dictType] || []
- return dictList.find(d => String(d.value) === String(value)) || null
- }
- // 组件挂载时加载字典
- onMounted(() => {
- loadAllDicts()
- })
- return {
- dict,
- loading,
- errors,
- reload,
- getLabel,
- getDict,
- }
- }
- /**
- * 导出工具函数
- */
- export {
- clearDictCache,
- getDictData,
- }
|