| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- /**
- * 字典数据加载 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;
|