import { getDictData, getMultipleDictData } 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: {} }; /** * 字典数据加载Mixin * 使用方式: * 1. 在组件中导入 import dictMixin from '@/utils/mixins/dictMixin'; * 2. 在组件的mixins选项中注册 mixins: [dictMixin] * 3. 在组件的data中定义需要的字典类型 dictTypeList: ['sys_user_sex', 'sys_normal_disable', ...] * 4. 在组件的methods中调用 getDictLabel 等方法使用字典数据 * 5. 在模板中直接使用 dictData 对象获取字典项 v-for="item in dictData.sys_user_sex" */ export default { data() { return { // 组件中的字典数据 dictData: {}, // 定义组件需要加载的字典类型 dictTypeList: [], // 字典加载状态 dictLoading: false }; }, created() { // 组件创建时,如果有定义dictTypeList,则自动加载字典数据 if (this.dictTypeList && this.dictTypeList.length > 0) { console.log(`[DictMixin] Component created, loading dictionaries: ${this.dictTypeList.join(', ')}`); this.loadDict(); } }, methods: { /** * 加载字典数据 * @param {Array} dictTypes - 字典类型数组,如果不传则使用组件中定义的dictTypeList * @returns {Promise} - 返回字典加载的Promise对象 */ loadDict(dictTypes) { const types = dictTypes || this.dictTypeList; if (!types || types.length === 0) { return Promise.resolve({}); } // 标记加载中 this.dictLoading = true; // 需要从服务器获取的字典类型 const needFetch = []; // 检查是否有静态字典或缓存 types.forEach(type => { // 先检查是否有静态字典 if (staticDict[type]) { // 使用静态字典数据 console.log(`[DictMixin] Using static dictionary for ${type}`); this.$set(this.dictData, type, staticDict[type]); } else { // 检查缓存 const cachedDict = this.getDictFromCache(type); if (cachedDict) { // 已有缓存,直接使用 console.log(`[DictMixin] Using cached dictionary for ${type}`); this.$set(this.dictData, type, cachedDict); } else if (!dictCache.loading[type]) { // 需要从服务器获取,并且当前没有其他组件正在加载 console.log(`[DictMixin] Need to fetch dictionary ${type} from server`); needFetch.push(type); } else { console.log( `[DictMixin] Dictionary ${type} is already being loaded by another component, waiting...` ); } } }); // 如果所有字典都已缓存或使用静态数据,直接返回 if (needFetch.length === 0) { this.dictLoading = false; // 检查是否有正在加载的字典,如果有,等待它们完成 const loadingTypes = types.filter(type => dictCache.loading[type]); if (loadingTypes.length > 0) { return this.waitForDictLoading(loadingTypes); } return Promise.resolve(this.dictData); } // 从服务器获取字典数据 return this.fetchDictData(needFetch).then(res => { this.dictLoading = false; console.log("this.dictData", this.dictData); return this.dictData; }).catch(err => { this.dictLoading = false; console.error('加载字典数据失败:', err); return Promise.reject(err); }); }, /** * 等待指定类型的字典加载完成 * @param {Array} types - 字典类型数组 * @returns {Promise} - 返回等待的Promise对象 */ 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 = this.getDictFromCache(type); if (cachedDict) { this.$set(this.dictData, type, cachedDict); } }); resolve(this.dictData); } }, 50); }); }, /** * 从服务器获取字典数据 * - 单个字典类型 → 单次请求 * - 多个字典类型 → 并发请求 * - 支持部分失败:失败的字典会忽略,成功的字典会返回 * * @param {Array} dictTypes - 字典类型数组 * @returns {Promise} * - 单个字典时返回 dictList 数组 * - 多个字典时返回 { dictType: dictList } 的 Map */ fetchDictData(dictTypes) { // 标记这些字典类型正在加载 dictTypes.forEach(type => { dictCache.loading[type] = true; }); // --- 单个字典类型 --- if (dictTypes.length === 1) { const dictType = dictTypes[0]; console.log(`[DictMixin] 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; } // 更新组件数据和缓存 this.$set(this.dictData, dictType, dictList); this.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(`[DictMixin] 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; } this.$set(this.dictData, dictType, dictList); this.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 { // 理论上不会走这里,因为 catch 已经返回对象了 console.error(`字典请求失败:`, r.reason); } }); console.log( `[DictMixin] Loaded ${Object.keys(dictMap).length} dictionaries, failed ${failed.length}` ); if (failed.length > 0) { console.warn('以下字典加载失败:', failed); } return dictMap; }); }, /** * 从服务器获取字典数据 * @param {Array} dictTypes - 字典类型数组 * @returns {Promise} - 返回字典获取的Promise对象 */ // fetchDictData(dictTypes) { // // 标记这些字典类型正在加载 // dictTypes.forEach(type => { // dictCache.loading[type] = true; // }); // if (dictTypes.length === 1) { // // 单个字典类型,直接获取 // console.log(`[DictMixin] Fetching single dictionary: ${dictTypes[0]}`); // return getDictData(dictTypes[0]).then(res => { // if (res.data.code === 200) { // const dictType = dictTypes[0]; // var dictList = [] // if(dictType === 'mall_product_category'){ // dictList.push({ // dictLabel:'推荐', // dictValue:'-1' // }) // // 展开数组追加 // dictList.push(...res.data.data); // }else{ // dictList = res.data.data // } // // 更新组件字典数据 // this.$set(this.dictData, dictType, dictList); // // 更新缓存 // this.updateDictCache(dictType, dictList); // // 取消加载标记 // delete dictCache.loading[dictType]; // return dictList; // } else { // console.error(`获取字典[${dictTypes[0]}]数据失败:`, res.data.msg); // // 取消加载标记 // delete dictCache.loading[dictTypes[0]]; // return Promise.reject(res.data.msg); // } // }).catch(err => { // // 发生错误时取消加载标记 // delete dictCache.loading[dictTypes[0]]; // throw err; // }); // } else { // // 多个字典类型,批量获取 // console.log(`[DictMixin] Batch fetching ${dictTypes.length} dictionaries: ${dictTypes.join(', ')}`); // return getMultipleDictData(dictTypes).then(res => { // if (res.data.code === 200) { // const dictMap = res.data.data || {}; // // 更新组件字典数据和缓存 // Object.keys(dictMap).forEach(dictType => { // const dictList = dictMap[dictType] || []; // // 更新组件字典数据 // this.$set(this.dictData, dictType, dictList); // // 更新缓存 // this.updateDictCache(dictType, dictList); // // 取消加载标记 // delete dictCache.loading[dictType]; // }); // console.log(`[DictMixin] Successfully loaded ${Object.keys(dictMap).length} dictionaries`); // return dictMap; // } else { // console.error(`获取字典数据失败:`, res.data.msg); // // 取消所有加载标记 // dictTypes.forEach(type => { // delete dictCache.loading[type]; // }); // return Promise.reject(res.data.msg); // } // }).catch(err => { // // 发生错误时取消所有加载标记 // dictTypes.forEach(type => { // delete dictCache.loading[type]; // }); // throw err; // }); // } // }, /** * 从缓存中获取字典数据 * @param {String} dictType - 字典类型 * @returns {Array|null} - 返回字典数据,不存在或已过期则返回null */ 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 - 字典数据列表 */ 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 {String} dictType - 字典类型,不传则清除所有缓存 */ clearDictCache(dictType) { if (dictType) { delete dictCache.data[dictType]; delete dictCache.lastUpdateTime[dictType]; storage.removeDict(`dict_time_${dictType}`); } else { dictCache.data = {}; dictCache.lastUpdateTime = {}; // 清除所有字典相关的本地存储 const keys = Object.keys(localStorage); keys.forEach(key => { if (key.startsWith('dict_time_')) { storage.removeDict(key); } }); } }, /** * 根据字典值获取对应的字典标签 * @param {String} dictType - 字典类型 * @param {String|Number} value - 字典值 * @param {String} defaultLabel - 默认标签 * @returns {String} - 字典标签 */ getDictLabel(dictType, value, defaultLabel = '') { // 首先检查组件数据 const dictList = this.dictData[dictType]; if (dictList) { const item = dictList.find(dict => dict.dictValue === value); if (item) return item.dictLabel; } // 再检查静态字典 // const staticDictList = staticDict[dictType]; // if (staticDictList) { // const item = staticDictList.find(dict => dict.value === value); // if (item) return item.label; // } // 都没找到,返回默认值 return defaultLabel; }, /** * 根据字典标签获取对应的字典值 * @param {String} dictType - 字典类型 * @param {String} label - 字典标签 * @param {String|Number} defaultValue - 默认值 * @returns {String|Number} - 字典值 */ getDictValue(dictType, label, defaultValue = '') { // 首先检查组件数据 const dictList = this.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} - 字典列表 */ getDictList(dictType) { // 首先检查组件数据 const dictList = this.dictData[dictType]; if (dictList) return dictList; // 再检查静态字典 return staticDict[dictType] || []; }, /** * 获取字典类型对应的样式类 * @param {String} dictType - 字典类型 * @param {String|Number} value - 字典值 * @param {String} defaultClass - 默认样式类 * @returns {String} - 字典项的样式类 */ getDictClass(dictType, value, defaultClass = '') { // 首先检查组件数据 const dictList = this.dictData[dictType]; if (dictList) { const item = dictList.find(dict => dict.dictValue === value); if (item && item.listClass) return item.listClass; } // 再检查静态字典 // const staticDictList = staticDict[dictType]; // if (staticDictList) { // const item = staticDictList.find(dict => dict.value === value); // if (item && item.listClass) return item.listClass; // } // 都没找到,返回默认值 return defaultClass; } } };