import { defineStore } from 'pinia' import { ref, computed } from 'vue' // import { getUserInfo } from '@/utils/auth' import { getMenus } from '@/api/menu'; export const useMenuStore = defineStore('menu', () => { // 菜单数据 const menus = ref([]); // 加载状态 const loading = ref(false); // 加载错误 const error = ref(null); // 正在加载的 Promise(用于避免重复请求) let loadingPromise = null; // 菜单缓存 key(基于用户类型和角色ID) const getCacheKey = () => { try { const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}'); const loginType = userInfo.type || 'user'; const roleId = userInfo.group_id || 0; return `menu_cache_${loginType}_${roleId}`; } catch (e) { return 'menu_cache_default'; } }; // 从缓存加载菜单 const loadFromCache = () => { try { const cacheKey = getCacheKey(); const cached = localStorage.getItem(cacheKey); if (cached) { const menuData = JSON.parse(cached); // 检查缓存是否过期(5分钟过期) if (menuData.timestamp && Date.now() - menuData.timestamp < 5 * 60 * 1000) { return menuData.menus; } } } catch (e) { console.warn('加载菜单缓存失败:', e); } return null; }; // 保存菜单到缓存 const saveToCache = (menuData) => { try { const cacheKey = getCacheKey(); localStorage.setItem(cacheKey, JSON.stringify({ menus: menuData, timestamp: Date.now() })); } catch (e) { console.warn('保存菜单缓存失败:', e); } }; // 清除菜单缓存 const clearCache = () => { try { const cacheKey = getCacheKey(); localStorage.removeItem(cacheKey); // 也清除其他可能的缓存key(兼容旧代码) localStorage.removeItem('menu_cache'); } catch (e) { console.warn('清除菜单缓存失败:', e); } }; // 获取用户信息 const getUserInfo = () => { try { return JSON.parse(localStorage.getItem('userInfo') || '{}'); } catch (e) { return {}; } }; // 从 API 加载菜单(核心方法,确保只请求一次) const fetchMenus = async (forceRefresh = false) => { // 如果已经有正在加载的请求,直接返回该 Promise if (loadingPromise && !forceRefresh) { return loadingPromise; } // 如果不强制刷新,先尝试从缓存加载 if (!forceRefresh) { const cachedMenus = loadFromCache(); if (cachedMenus && cachedMenus.length > 0) { menus.value = cachedMenus; return Promise.resolve(cachedMenus); } } // 如果正在加载且不是强制刷新,返回现有的 Promise if (loading.value && !forceRefresh) { return loadingPromise; } // 创建新的加载 Promise loadingPromise = (async () => { loading.value = true; error.value = null; try { const userInfo = getUserInfo(); const loginType = userInfo.type || 'user'; const roleId = userInfo.group_id || 0; let res; // 检查用户ID是否存在 if (!userInfo.id) { throw new Error('用户ID不存在,请重新登录'); } // 用户登录,使用 getMenus 接口 res = await getMenus(userInfo.id); // 检查响应格式 if (!res) { throw new Error('获取菜单失败:服务器无响应'); } // 检查后端返回的 code 字段 if (res.code !== 200) { throw new Error(res.msg || '获取菜单失败'); } // 如果 code 为 200,检查 data if (res.code === 200) { // data 可能是空数组,这也是有效的 if (res.data !== undefined && res.data !== null) { // 确保 data 是数组 const menuData = Array.isArray(res.data) ? res.data : []; // 直接使用后端返回的树形结构数据,不需要额外过滤 menus.value = menuData; // 保存到缓存 saveToCache(menuData); return menuData; } else { // data 为 null 或 undefined,使用空数组 console.warn('菜单数据为空,使用空数组'); menus.value = []; saveToCache([]); return []; } } // 如果响应格式不符合预期,尝试直接使用 res.data if (res.data !== undefined) { const menuData = Array.isArray(res.data) ? res.data : []; const filtered = menuData.filter(m => (m.isShow ?? 1) !== 0); menus.value = filtered; saveToCache(filtered); return filtered; } // 如果都不符合,抛出错误 throw new Error(res.message || '获取菜单失败:响应格式错误'); } catch (err) { error.value = err.message || '获取菜单失败'; console.error('获取菜单失败:', err); console.error('错误详情:', { message: err.message, response: err.response, stack: err.stack }); // 如果是 token 无效错误,不使用缓存,直接抛出 if (err.message === 'token无效' || err.response?.status === 401) { clearCache(); menus.value = []; throw err; } // 如果出错,尝试使用缓存数据 const cachedMenus = loadFromCache(); if (cachedMenus && cachedMenus.length > 0) { console.warn('使用缓存的菜单数据'); menus.value = cachedMenus; return cachedMenus; } // 如果连缓存都没有,设置空数组,避免页面崩溃 menus.value = []; throw err; } finally { loading.value = false; loadingPromise = null; } })(); return loadingPromise; }; // 刷新菜单(强制从 API 获取) const refreshMenus = async () => { clearCache(); return await fetchMenus(true); }; // 重置菜单 store(登出时使用) const resetMenus = () => { menus.value = []; loading.value = false; error.value = null; loadingPromise = null; clearCache(); }; // 计算属性:获取菜单列表 const menuList = computed(() => menus.value); // 计算属性:菜单是否已加载 const isLoaded = computed(() => menus.value.length > 0); return { // 状态 menus: menuList, loading, error, isLoaded, // 方法 fetchMenus, refreshMenus, resetMenus, clearCache, loadFromCache, }; });