commits
This commit is contained in:
+237
-237
@@ -1,237 +1,237 @@
|
||||
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,
|
||||
};
|
||||
});
|
||||
|
||||
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,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user