/** * 字典数据加载 Composable (Vue 3 Composition API) * * 使用方式: * 1. 在组件中导入 import { useDict } from '@/utils/composables/useDict'; * 2. 在 setup 中调用 const { dictData, dictLoading, loadDict, getDictLabel, ... } = useDict(['sys_user_sex', 'sys_normal_disable']); * 3. 在模板中直接使用 dictData 对象获取字典项 v-for="item in dictData.sys_user_sex" */ import { ref, reactive, onMounted } from 'vue'; import { getDictData } from '@/api/services/dict'; import storage from '@/utils/storage'; import staticDict from '@/utils/staticDict'; // 全局字典缓存对象,用于存储已加载的字典数据 const dictCache = { // 缓存的字典数据,格式为 { dictType: [{label, value, ...}, ...] } data: {}, // 缓存过期时间,单位为毫秒 expireTime: 1000 * 60 * 60, // 1小时 // 缓存最后更新时间 lastUpdateTime: {}, // 正在加载的字典类型,用于防止重复请求 loading: {} }; /** * 字典数据加载 Composable * @param {Array} initialDictTypes - 初始需要加载的字典类型数组 * @param {Object} options - 配置选项 * @param {Boolean} options.autoLoad - 是否自动加载,默认为 true * @returns {Object} - 返回字典相关的响应式数据和方法 */ export function useDict(initialDictTypes = [], options = {}) { const { autoLoad = true } = options; // 组件中的字典数据 const dictData = reactive({}); // 字典加载状态 const dictLoading = ref(false); // 定义组件需要加载的字典类型 const dictTypeList = ref(initialDictTypes); /** * 从缓存中获取字典数据 * @param {String} dictType - 字典类型 * @returns {Array|null} - 返回字典数据,不存在或已过期则返回null */ const getDictFromCache = (dictType) => { // 判断是否有缓存 if (!dictCache.data[dictType]) { return null; } // 判断缓存是否过期 const lastUpdateTime = dictCache.lastUpdateTime[dictType] || 0; const now = Date.now(); if (now - lastUpdateTime > dictCache.expireTime) { // 缓存已过期,删除缓存 delete dictCache.data[dictType]; delete dictCache.lastUpdateTime[dictType]; return null; } // 返回缓存的字典数据 return dictCache.data[dictType]; }; /** * 更新字典缓存 * @param {String} dictType - 字典类型 * @param {Array} dictList - 字典数据列表 */ const updateDictCache = (dictType, dictList) => { dictCache.data[dictType] = dictList; dictCache.lastUpdateTime[dictType] = Date.now(); // 更新本地存储 try { // 只存储最后更新时间,具体数据保存在内存中 storage.setDict(`dict_time_${dictType}`, dictCache.lastUpdateTime[dictType]); } catch (e) { console.error('更新字典缓存失败:', e); } }; /** * 等待指定类型的字典加载完成 * @param {Array} types - 字典类型数组 * @returns {Promise} - 返回等待的Promise对象 */ const waitForDictLoading = (types) => { return new Promise(resolve => { const checkInterval = setInterval(() => { const stillLoading = types.some(type => dictCache.loading[type]); if (!stillLoading) { clearInterval(checkInterval); // 加载完成后,从缓存中获取数据 types.forEach(type => { const cachedDict = getDictFromCache(type); if (cachedDict) { dictData[type] = cachedDict; } }); resolve(dictData); } }, 50); }); }; /** * 从服务器获取字典数据 * @param {Array} dictTypes - 字典类型数组 * @returns {Promise} - 返回字典获取的Promise对象 */ const fetchDictData = (dictTypes) => { // 标记这些字典类型正在加载 dictTypes.forEach(type => { dictCache.loading[type] = true; }); // 单个字典类型 if (dictTypes.length === 1) { const dictType = dictTypes[0]; console.log(`[useDict] Fetching single dictionary: ${dictType}`); return getDictData(dictType).then(res => { if (res.data.code === 200) { let dictList = []; if (dictType === 'mall_product_category') { dictList.push({ dictLabel: '推荐', dictValue: '-1' }); dictList.push(...res.data.data); } else { dictList = res.data.data; } // 更新组件数据和缓存 dictData[dictType] = dictList; updateDictCache(dictType, dictList); delete dictCache.loading[dictType]; return dictList; } else { console.error(`获取字典[${dictType}]数据失败:`, res.data.msg); delete dictCache.loading[dictType]; return Promise.reject(res.data.msg); } }).catch(err => { delete dictCache.loading[dictType]; throw err; }); } // 多个字典类型,并发请求 console.log(`[useDict] Concurrently fetching ${dictTypes.length} dictionaries: ${dictTypes.join(', ')}`); const requests = dictTypes.map(dictType => { return getDictData(dictType).then(res => { if (res.data.code === 200) { let dictList = []; if (dictType === 'mall_product_category') { dictList.push({ dictLabel: '推荐', dictValue: '-1' }); dictList.push(...res.data.data); } else { dictList = res.data.data; } dictData[dictType] = dictList; updateDictCache(dictType, dictList); delete dictCache.loading[dictType]; return { dictType, dictList, success: true }; } else { console.error(`获取字典[${dictType}]数据失败:`, res.data.msg); delete dictCache.loading[dictType]; return { dictType, dictList: [], success: false, msg: res.data.msg }; } }).catch(err => { console.error(`获取字典[${dictType}]异常:`, err); delete dictCache.loading[dictType]; return { dictType, dictList: [], success: false, msg: err }; }); }); // 等所有请求完成 return Promise.allSettled(requests).then(results => { const dictMap = {}; const failed = []; results.forEach(r => { if (r.status === 'fulfilled') { const { dictType, dictList, success, msg } = r.value; if (success) { dictMap[dictType] = dictList; } else { failed.push({ dictType, msg }); } } else { console.error(`字典请求失败:`, r.reason); } }); console.log(`[useDict] Loaded ${Object.keys(dictMap).length} dictionaries, failed ${failed.length}`); if (failed.length > 0) { console.warn('以下字典加载失败:', failed); } return dictMap; }); }; /** * 加载字典数据 * @param {Array} dictTypes - 字典类型数组,如果不传则使用初始化时的dictTypeList * @returns {Promise} - 返回字典加载的Promise对象 */ const loadDict = (dictTypes) => { const types = dictTypes || dictTypeList.value; if (!types || types.length === 0) { return Promise.resolve({}); } // 标记加载中 dictLoading.value = true; // 需要从服务器获取的字典类型 const needFetch = []; // 检查是否有静态字典或缓存 types.forEach(type => { // 先检查是否有静态字典 if (staticDict[type]) { // 使用静态字典数据 console.log(`[useDict] Using static dictionary for ${type}`); dictData[type] = staticDict[type]; } else { // 检查缓存 const cachedDict = getDictFromCache(type); if (cachedDict) { // 已有缓存,直接使用 console.log(`[useDict] Using cached dictionary for ${type}`); dictData[type] = cachedDict; } else if (!dictCache.loading[type]) { // 需要从服务器获取,并且当前没有其他组件正在加载 console.log(`[useDict] Need to fetch dictionary ${type} from server`); needFetch.push(type); } else { console.log(`[useDict] Dictionary ${type} is already being loaded by another component, waiting...`); } } }); // 如果所有字典都已缓存或使用静态数据,直接返回 if (needFetch.length === 0) { dictLoading.value = false; // 检查是否有正在加载的字典,如果有,等待它们完成 const loadingTypes = types.filter(type => dictCache.loading[type]); if (loadingTypes.length > 0) { return waitForDictLoading(loadingTypes); } return Promise.resolve(dictData); } // 从服务器获取字典数据 return fetchDictData(needFetch).then(res => { dictLoading.value = false; console.log("dictData", dictData); return dictData; }).catch(err => { dictLoading.value = false; console.error('加载字典数据失败:', err); return Promise.reject(err); }); }; /** * 清除字典缓存 * @param {String} dictType - 字典类型,不传则清除所有缓存 */ const clearDictCache = (dictType) => { if (dictType) { delete dictCache.data[dictType]; delete dictCache.lastUpdateTime[dictType]; storage.removeDict(`dict_time_${dictType}`); } else { dictCache.data = {}; dictCache.lastUpdateTime = {}; // 清除所有字典相关的本地存储 // TODO: 需要根据平台使用不同的方式获取所有存储的key // 原因: uni-app 没有直接获取所有key的API,需要手动管理字典key列表 // 推荐: 维护一个字典key列表,在添加字典时记录key,清除时遍历列表删除 } }; /** * 根据字典值获取对应的字典标签 * @param {String} dictType - 字典类型 * @param {String|Number} value - 字典值 * @param {String} defaultLabel - 默认标签 * @returns {String} - 字典标签 */ const getDictLabel = (dictType, value, defaultLabel = '') => { // 首先检查组件数据 const dictList = dictData[dictType]; if (dictList) { const item = dictList.find(dict => dict.dictValue === value); if (item) return item.dictLabel; } // 都没找到,返回默认值 return defaultLabel; }; /** * 根据字典标签获取对应的字典值 * @param {String} dictType - 字典类型 * @param {String} label - 字典标签 * @param {String|Number} defaultValue - 默认值 * @returns {String|Number} - 字典值 */ const getDictValue = (dictType, label, defaultValue = '') => { // 首先检查组件数据 const dictList = dictData[dictType]; if (dictList) { const item = dictList.find(dict => dict.label === label); if (item) return item.value; } // 再检查静态字典 const staticDictList = staticDict[dictType]; if (staticDictList) { const item = staticDictList.find(dict => dict.label === label); if (item) return item.value; } // 都没找到,返回默认值 return defaultValue; }; /** * 获取字典列表 * @param {String} dictType - 字典类型 * @returns {Array} - 字典列表 */ const getDictList = (dictType) => { // 首先检查组件数据 const dictList = dictData[dictType]; if (dictList) return dictList; // 再检查静态字典 return staticDict[dictType] || []; }; /** * 获取字典类型对应的样式类 * @param {String} dictType - 字典类型 * @param {String|Number} value - 字典值 * @param {String} defaultClass - 默认样式类 * @returns {String} - 字典项的样式类 */ const getDictClass = (dictType, value, defaultClass = '') => { // 首先检查组件数据 const dictList = dictData[dictType]; if (dictList) { const item = dictList.find(dict => dict.dictValue === value); if (item && item.listClass) return item.listClass; } // 都没找到,返回默认值 return defaultClass; }; // 自动加载字典 if (autoLoad && initialDictTypes.length > 0) { onMounted(() => { console.log(`[useDict] Auto-loading dictionaries: ${initialDictTypes.join(', ')}`); loadDict(); }); } return { dictData, dictLoading, dictTypeList, loadDict, clearDictCache, getDictLabel, getDictValue, getDictList, getDictClass }; } export default useDict;