dictMixin.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. import { getDictData, getMultipleDictData } from '@/api/services/dict';
  2. import storage from '@/utils/storage';
  3. import staticDict from '@/utils/staticDict';
  4. // 全局字典缓存对象,用于存储已加载的字典数据
  5. const dictCache = {
  6. // 缓存的字典数据,格式为 { dictType: [{label, value, ...}, ...] }
  7. data: {},
  8. // 缓存过期时间,单位为毫秒
  9. expireTime: 1000 * 60 * 60, // 1小时
  10. // 缓存最后更新时间
  11. lastUpdateTime: {},
  12. // 正在加载的字典类型,用于防止重复请求
  13. loading: {}
  14. };
  15. /**
  16. * 字典数据加载Mixin
  17. * 使用方式:
  18. * 1. 在组件中导入 import dictMixin from '@/utils/mixins/dictMixin';
  19. * 2. 在组件的mixins选项中注册 mixins: [dictMixin]
  20. * 3. 在组件的data中定义需要的字典类型 dictTypeList: ['sys_user_sex', 'sys_normal_disable', ...]
  21. * 4. 在组件的methods中调用 getDictLabel 等方法使用字典数据
  22. * 5. 在模板中直接使用 dictData 对象获取字典项 v-for="item in dictData.sys_user_sex"
  23. */
  24. export default {
  25. data() {
  26. return {
  27. // 组件中的字典数据
  28. dictData: {},
  29. // 定义组件需要加载的字典类型
  30. dictTypeList: [],
  31. // 字典加载状态
  32. dictLoading: false
  33. };
  34. },
  35. created() {
  36. // 组件创建时,如果有定义dictTypeList,则自动加载字典数据
  37. if (this.dictTypeList && this.dictTypeList.length > 0) {
  38. console.log(`[DictMixin] Component created, loading dictionaries: ${this.dictTypeList.join(', ')}`);
  39. this.loadDict();
  40. }
  41. },
  42. methods: {
  43. /**
  44. * 加载字典数据
  45. * @param {Array} dictTypes - 字典类型数组,如果不传则使用组件中定义的dictTypeList
  46. * @returns {Promise} - 返回字典加载的Promise对象
  47. */
  48. loadDict(dictTypes) {
  49. const types = dictTypes || this.dictTypeList;
  50. if (!types || types.length === 0) {
  51. return Promise.resolve({});
  52. }
  53. // 标记加载中
  54. this.dictLoading = true;
  55. // 需要从服务器获取的字典类型
  56. const needFetch = [];
  57. // 检查是否有静态字典或缓存
  58. types.forEach(type => {
  59. // 先检查是否有静态字典
  60. if (staticDict[type]) {
  61. // 使用静态字典数据
  62. console.log(`[DictMixin] Using static dictionary for ${type}`);
  63. this.$set(this.dictData, type, staticDict[type]);
  64. } else {
  65. // 检查缓存
  66. const cachedDict = this.getDictFromCache(type);
  67. if (cachedDict) {
  68. // 已有缓存,直接使用
  69. console.log(`[DictMixin] Using cached dictionary for ${type}`);
  70. this.$set(this.dictData, type, cachedDict);
  71. } else if (!dictCache.loading[type]) {
  72. // 需要从服务器获取,并且当前没有其他组件正在加载
  73. console.log(`[DictMixin] Need to fetch dictionary ${type} from server`);
  74. needFetch.push(type);
  75. } else {
  76. console.log(`[DictMixin] Dictionary ${type} is already being loaded by another component, waiting...`);
  77. }
  78. }
  79. });
  80. // 如果所有字典都已缓存或使用静态数据,直接返回
  81. if (needFetch.length === 0) {
  82. this.dictLoading = false;
  83. // 检查是否有正在加载的字典,如果有,等待它们完成
  84. const loadingTypes = types.filter(type => dictCache.loading[type]);
  85. if (loadingTypes.length > 0) {
  86. return this.waitForDictLoading(loadingTypes);
  87. }
  88. return Promise.resolve(this.dictData);
  89. }
  90. // 从服务器获取字典数据
  91. return this.fetchDictData(needFetch).then(res => {
  92. this.dictLoading = false;
  93. return this.dictData;
  94. }).catch(err => {
  95. this.dictLoading = false;
  96. console.error('加载字典数据失败:', err);
  97. return Promise.reject(err);
  98. });
  99. },
  100. /**
  101. * 等待指定类型的字典加载完成
  102. * @param {Array} types - 字典类型数组
  103. * @returns {Promise} - 返回等待的Promise对象
  104. */
  105. waitForDictLoading(types) {
  106. return new Promise(resolve => {
  107. const checkInterval = setInterval(() => {
  108. const stillLoading = types.some(type => dictCache.loading[type]);
  109. if (!stillLoading) {
  110. clearInterval(checkInterval);
  111. // 加载完成后,从缓存中获取数据
  112. types.forEach(type => {
  113. const cachedDict = this.getDictFromCache(type);
  114. if (cachedDict) {
  115. this.$set(this.dictData, type, cachedDict);
  116. }
  117. });
  118. resolve(this.dictData);
  119. }
  120. }, 50);
  121. });
  122. },
  123. /**
  124. * 从服务器获取字典数据
  125. * @param {Array} dictTypes - 字典类型数组
  126. * @returns {Promise} - 返回字典获取的Promise对象
  127. */
  128. fetchDictData(dictTypes) {
  129. // 标记这些字典类型正在加载
  130. dictTypes.forEach(type => {
  131. dictCache.loading[type] = true;
  132. });
  133. if (dictTypes.length === 1) {
  134. // 单个字典类型,直接获取
  135. console.log(`[DictMixin] Fetching single dictionary: ${dictTypes[0]}`);
  136. return getDictData(dictTypes[0]).then(res => {
  137. if (res.data.code === 200) {
  138. const dictType = dictTypes[0];
  139. const dictList = res.data.data || [];
  140. // 更新组件字典数据
  141. this.$set(this.dictData, dictType, dictList);
  142. // 更新缓存
  143. this.updateDictCache(dictType, dictList);
  144. // 取消加载标记
  145. delete dictCache.loading[dictType];
  146. return dictList;
  147. } else {
  148. console.error(`获取字典[${dictTypes[0]}]数据失败:`, res.data.msg);
  149. // 取消加载标记
  150. delete dictCache.loading[dictTypes[0]];
  151. return Promise.reject(res.data.msg);
  152. }
  153. }).catch(err => {
  154. // 发生错误时取消加载标记
  155. delete dictCache.loading[dictTypes[0]];
  156. throw err;
  157. });
  158. } else {
  159. // 多个字典类型,批量获取
  160. console.log(`[DictMixin] Batch fetching ${dictTypes.length} dictionaries: ${dictTypes.join(', ')}`);
  161. return getMultipleDictData(dictTypes).then(res => {
  162. if (res.data.code === 200) {
  163. const dictMap = res.data.data || {};
  164. // 更新组件字典数据和缓存
  165. Object.keys(dictMap).forEach(dictType => {
  166. const dictList = dictMap[dictType] || [];
  167. // 更新组件字典数据
  168. this.$set(this.dictData, dictType, dictList);
  169. // 更新缓存
  170. this.updateDictCache(dictType, dictList);
  171. // 取消加载标记
  172. delete dictCache.loading[dictType];
  173. });
  174. console.log(`[DictMixin] Successfully loaded ${Object.keys(dictMap).length} dictionaries`);
  175. return dictMap;
  176. } else {
  177. console.error(`获取字典数据失败:`, res.data.msg);
  178. // 取消所有加载标记
  179. dictTypes.forEach(type => {
  180. delete dictCache.loading[type];
  181. });
  182. return Promise.reject(res.data.msg);
  183. }
  184. }).catch(err => {
  185. // 发生错误时取消所有加载标记
  186. dictTypes.forEach(type => {
  187. delete dictCache.loading[type];
  188. });
  189. throw err;
  190. });
  191. }
  192. },
  193. /**
  194. * 从缓存中获取字典数据
  195. * @param {String} dictType - 字典类型
  196. * @returns {Array|null} - 返回字典数据,不存在或已过期则返回null
  197. */
  198. getDictFromCache(dictType) {
  199. // 判断是否有缓存
  200. if (!dictCache.data[dictType]) {
  201. return null;
  202. }
  203. // 判断缓存是否过期
  204. const lastUpdateTime = dictCache.lastUpdateTime[dictType] || 0;
  205. const now = Date.now();
  206. if (now - lastUpdateTime > dictCache.expireTime) {
  207. // 缓存已过期,删除缓存
  208. delete dictCache.data[dictType];
  209. delete dictCache.lastUpdateTime[dictType];
  210. return null;
  211. }
  212. // 返回缓存的字典数据
  213. return dictCache.data[dictType];
  214. },
  215. /**
  216. * 更新字典缓存
  217. * @param {String} dictType - 字典类型
  218. * @param {Array} dictList - 字典数据列表
  219. */
  220. updateDictCache(dictType, dictList) {
  221. dictCache.data[dictType] = dictList;
  222. dictCache.lastUpdateTime[dictType] = Date.now();
  223. // 更新本地存储
  224. try {
  225. // 只存储最后更新时间,具体数据保存在内存中
  226. storage.setDict(`dict_time_${dictType}`, dictCache.lastUpdateTime[dictType]);
  227. } catch (e) {
  228. console.error('更新字典缓存失败:', e);
  229. }
  230. },
  231. /**
  232. * 清除字典缓存
  233. * @param {String} dictType - 字典类型,不传则清除所有缓存
  234. */
  235. clearDictCache(dictType) {
  236. if (dictType) {
  237. delete dictCache.data[dictType];
  238. delete dictCache.lastUpdateTime[dictType];
  239. storage.removeDict(`dict_time_${dictType}`);
  240. } else {
  241. dictCache.data = {};
  242. dictCache.lastUpdateTime = {};
  243. // 清除所有字典相关的本地存储
  244. const keys = Object.keys(localStorage);
  245. keys.forEach(key => {
  246. if (key.startsWith('dict_time_')) {
  247. storage.removeDict(key);
  248. }
  249. });
  250. }
  251. },
  252. /**
  253. * 根据字典值获取对应的字典标签
  254. * @param {String} dictType - 字典类型
  255. * @param {String|Number} value - 字典值
  256. * @param {String} defaultLabel - 默认标签
  257. * @returns {String} - 字典标签
  258. */
  259. getDictLabel(dictType, value, defaultLabel = '') {
  260. // 首先检查组件数据
  261. const dictList = this.dictData[dictType];
  262. if (dictList) {
  263. const item = dictList.find(dict => dict.dictValue === value);
  264. if (item) return item.dictLabel;
  265. }
  266. // 再检查静态字典
  267. // const staticDictList = staticDict[dictType];
  268. // if (staticDictList) {
  269. // const item = staticDictList.find(dict => dict.value === value);
  270. // if (item) return item.label;
  271. // }
  272. // 都没找到,返回默认值
  273. return defaultLabel;
  274. },
  275. /**
  276. * 根据字典标签获取对应的字典值
  277. * @param {String} dictType - 字典类型
  278. * @param {String} label - 字典标签
  279. * @param {String|Number} defaultValue - 默认值
  280. * @returns {String|Number} - 字典值
  281. */
  282. getDictValue(dictType, label, defaultValue = '') {
  283. // 首先检查组件数据
  284. const dictList = this.dictData[dictType];
  285. if (dictList) {
  286. const item = dictList.find(dict => dict.label === label);
  287. if (item) return item.value;
  288. }
  289. // 再检查静态字典
  290. const staticDictList = staticDict[dictType];
  291. if (staticDictList) {
  292. const item = staticDictList.find(dict => dict.label === label);
  293. if (item) return item.value;
  294. }
  295. // 都没找到,返回默认值
  296. return defaultValue;
  297. },
  298. /**
  299. * 获取字典列表
  300. * @param {String} dictType - 字典类型
  301. * @returns {Array} - 字典列表
  302. */
  303. getDictList(dictType) {
  304. // 首先检查组件数据
  305. const dictList = this.dictData[dictType];
  306. if (dictList) return dictList;
  307. // 再检查静态字典
  308. return staticDict[dictType] || [];
  309. },
  310. /**
  311. * 获取字典类型对应的样式类
  312. * @param {String} dictType - 字典类型
  313. * @param {String|Number} value - 字典值
  314. * @param {String} defaultClass - 默认样式类
  315. * @returns {String} - 字典项的样式类
  316. */
  317. getDictClass(dictType, value, defaultClass = '') {
  318. // 首先检查组件数据
  319. const dictList = this.dictData[dictType];
  320. if (dictList) {
  321. const item = dictList.find(dict => dict.dictValue === value);
  322. if (item && item.listClass) return item.listClass;
  323. }
  324. // 再检查静态字典
  325. // const staticDictList = staticDict[dictType];
  326. // if (staticDictList) {
  327. // const item = staticDictList.find(dict => dict.value === value);
  328. // if (item && item.listClass) return item.listClass;
  329. // }
  330. // 都没找到,返回默认值
  331. return defaultClass;
  332. }
  333. }
  334. };