sideNum.ts 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. import { defineStore } from 'pinia';
  2. import { ref, computed } from 'vue';
  3. import request from '@/utils/request';
  4. export interface MenuApiConfig {
  5. // 接口URL
  6. apiUrl?: string;
  7. // 请求方法
  8. method?: 'GET' | 'POST';
  9. // 请求参数
  10. params?: Record<string, any>;
  11. // 数据提取路径,从接口响应中提取数量的路径
  12. dataPath?: string;
  13. // 是否为父级菜单(数字由子菜单汇总而来,不调用接口)
  14. isParent?: boolean;
  15. // 子菜单路径列表(仅当isParent为true时有效)
  16. children?: string[];
  17. }
  18. export const useSideNumStore = defineStore('sideNum', () => {
  19. // 存储每个菜单路径对应的气泡数量
  20. const menuCounts = ref<Record<string, number>>({});
  21. // 存储每个菜单路径对应的接口配置
  22. const menuApiConfigs = ref<Record<string, MenuApiConfig>>({});
  23. // 获取指定菜单路径的气泡数量
  24. const getMenuCount = (menuPath: string): number => {
  25. const config = menuApiConfigs.value[menuPath];
  26. // 如果是父级菜单,计算子菜单数量总和
  27. if (config && config.isParent && config.children) {
  28. return config.children.reduce((total, childPath) => {
  29. return total + (menuCounts.value[childPath] || 0);
  30. }, 0);
  31. }
  32. // 普通菜单直接返回存储的数量
  33. return menuCounts.value[menuPath] || 0;
  34. };
  35. // 获取所有有数量的菜单项
  36. const getActiveMenus = computed(() => {
  37. const activeMenus: Record<string, number> = {};
  38. // 遍历所有已配置的菜单
  39. Object.keys(menuApiConfigs.value).forEach((key) => {
  40. const config = menuApiConfigs.value[key];
  41. let count = 0;
  42. if (config.isParent && config.children) {
  43. // 父级菜单:使用存储的值,如果没有则计算子菜单总和
  44. count =
  45. menuCounts.value[key] ||
  46. config.children.reduce((total, childPath) => {
  47. return total + (menuCounts.value[childPath] || 0);
  48. }, 0);
  49. } else {
  50. // 普通菜单:直接使用存储的值
  51. count = menuCounts.value[key] || 0;
  52. }
  53. if (count > 0) {
  54. activeMenus[key] = count;
  55. }
  56. });
  57. return activeMenus;
  58. });
  59. // 获取总的未处理数量
  60. const getTotalCount = computed(() => {
  61. // 只计算非父级菜单的数量,避免重复计算
  62. return Object.keys(menuApiConfigs.value).reduce((total, menuPath) => {
  63. const config = menuApiConfigs.value[menuPath];
  64. if (!config.isParent) {
  65. return total + (menuCounts.value[menuPath] || 0);
  66. }
  67. return total;
  68. }, 0);
  69. });
  70. // 设置菜单接口配置
  71. const setMenuApiConfig = (menuPath: string, config: MenuApiConfig) => {
  72. menuApiConfigs.value[menuPath] = {
  73. method: 'GET',
  74. dataPath: 'data.count', // 默认数据路径
  75. isParent: false, // 默认不是父级菜单
  76. ...config
  77. };
  78. };
  79. // 设置父级菜单配置(数字由子菜单汇总)
  80. const setParentMenuConfig = (menuPath: string, children: string[]) => {
  81. menuApiConfigs.value[menuPath] = {
  82. apiUrl: '', // 父级菜单不需要接口URL
  83. isParent: true,
  84. children: children
  85. };
  86. };
  87. // 批量设置多个父级菜单的配置项
  88. const setMultipleParentConfigs = (configs: Record<string, string[]>) => {
  89. Object.keys(configs).forEach((menuPath) => {
  90. setParentMenuConfig(menuPath, configs[menuPath]);
  91. });
  92. };
  93. // 批量设置多个菜单的接口配置
  94. const setMultipleApiConfigs = (configs: Record<string, MenuApiConfig>) => {
  95. Object.keys(configs).forEach((menuPath) => {
  96. setMenuApiConfig(menuPath, configs[menuPath]);
  97. });
  98. };
  99. // 更新指定菜单的气泡数量
  100. const updateMenuCount = (menuPath: string, count: number) => {
  101. menuCounts.value[menuPath] = Math.max(0, count); // 确保数量不为负数
  102. // 自动更新包含此子菜单的父级菜单数量
  103. Object.keys(menuApiConfigs.value).forEach((parentPath) => {
  104. const config = menuApiConfigs.value[parentPath];
  105. if (config.isParent && config.children && config.children.includes(menuPath)) {
  106. // 重新计算父级菜单数量
  107. const parentCount = config.children.reduce((total, childPath) => {
  108. return total + (menuCounts.value[childPath] || 0);
  109. }, 0);
  110. menuCounts.value[parentPath] = parentCount;
  111. }
  112. });
  113. };
  114. // 清空指定菜单的气泡数量
  115. const clearMenuCount = (menuPath: string) => {
  116. menuCounts.value[menuPath] = 0;
  117. // 自动更新包含此子菜单的父级菜单数量
  118. Object.keys(menuApiConfigs.value).forEach((parentPath) => {
  119. const config = menuApiConfigs.value[parentPath];
  120. if (config.isParent && config.children && config.children.includes(menuPath)) {
  121. // 重新计算父级菜单数量
  122. const parentCount = config.children.reduce((total, childPath) => {
  123. return total + (menuCounts.value[childPath] || 0);
  124. }, 0);
  125. menuCounts.value[parentPath] = parentCount;
  126. }
  127. });
  128. };
  129. // 清空所有菜单的气泡数量
  130. const clearAllCounts = () => {
  131. menuCounts.value = {};
  132. };
  133. // 从接口响应中提取数量
  134. const extractCountFromResponse = (response: any): number => {
  135. try {
  136. if (response.code === 200) {
  137. return response.data;
  138. }
  139. } catch {
  140. return 0;
  141. }
  142. };
  143. // 获取指定菜单的数量(调用接口)
  144. const fetchMenuCount = async (menuPath: string): Promise<number> => {
  145. const config = menuApiConfigs.value[menuPath];
  146. if (!config) {
  147. console.warn(`菜单路径 ${menuPath} 没有配置接口信息`);
  148. return 0;
  149. }
  150. console.log(menuPath, '========');
  151. // 如果是父级菜单,不调用接口,直接返回子菜单数量总和
  152. if (config.isParent) {
  153. return getMenuCount(menuPath);
  154. }
  155. try {
  156. const response = await makeApiRequest(config);
  157. const count = extractCountFromResponse(response);
  158. updateMenuCount(menuPath, count);
  159. return count;
  160. } catch (error) {
  161. console.error(`获取菜单 ${menuPath} 数量失败:`, error);
  162. return 0;
  163. }
  164. };
  165. // 发起API请求
  166. const makeApiRequest = async (config: MenuApiConfig): Promise<any> => {
  167. const { apiUrl, method = 'GET', params } = config;
  168. if (method === 'GET') {
  169. return await request.get(apiUrl, { params });
  170. } else {
  171. return await request.post(apiUrl, params);
  172. }
  173. };
  174. // 刷新指定菜单的数量(立即调用接口)
  175. const refreshMenuCount = async (menuPath: string): Promise<number> => {
  176. return fetchMenuCount(menuPath);
  177. };
  178. // 刷新所有菜单的数量
  179. const refreshAllCounts = async (): Promise<void> => {
  180. // 只刷新非父级菜单的数量
  181. const nonParentMenus = Object.keys(menuApiConfigs.value).filter((menuPath) => !menuApiConfigs.value[menuPath].isParent);
  182. const promises = nonParentMenus.map((menuPath) => fetchMenuCount(menuPath));
  183. await Promise.allSettled(promises);
  184. // 刷新完子菜单后,更新所有父级菜单的数量
  185. Object.keys(menuApiConfigs.value).forEach((menuPath) => {
  186. const config = menuApiConfigs.value[menuPath];
  187. if (config.isParent && config.children) {
  188. // 父级菜单数量 = 子菜单数量总和
  189. const parentCount = config.children.reduce((total, childPath) => {
  190. return total + (menuCounts.value[childPath] || 0);
  191. }, 0);
  192. // 将计算的数量存储到menuCounts中
  193. menuCounts.value[menuPath] = parentCount;
  194. }
  195. });
  196. };
  197. // 重置Store状态
  198. const resetStore = () => {
  199. menuCounts.value = {};
  200. menuApiConfigs.value = {};
  201. };
  202. return {
  203. // 状态
  204. menuCounts,
  205. menuApiConfigs,
  206. // 计算属性
  207. getActiveMenus,
  208. getTotalCount,
  209. // 方法
  210. getMenuCount,
  211. setMenuApiConfig,
  212. setParentMenuConfig,
  213. setMultipleApiConfigs,
  214. updateMenuCount,
  215. clearMenuCount,
  216. clearAllCounts,
  217. extractCountFromResponse,
  218. fetchMenuCount,
  219. makeApiRequest,
  220. refreshMenuCount,
  221. refreshAllCounts,
  222. resetStore,
  223. setMultipleParentConfigs
  224. };
  225. });