import { defineStore } from 'pinia'; import auth from '@/plugins/auth'; import router, { constantRoutes, dynamicRoutes } from '@/router'; import Layout from '@/components/layout/index.vue'; import { getUserMenu } from '@/api/sys.js'; import { handleTree } from '~/utils/aiotagro.js'; // 匹配views里面所有的.vue文件 const modules = import.meta.glob('./../views/**/*.vue'); const usePermissionStore = defineStore('permission', { state: () => ({ routes: [], addRoutes: [], sidebarRouters: [], routeFlag: false, userPermission: [], }), actions: { setRoutes(routes) { this.addRoutes = routes; this.routes = constantRoutes.concat(routes); }, setSidebarRouters(routes, flag) { this.sidebarRouters = routes; this.routeFlag = !!(flag == 200); }, setUserPermission(arr) { this.userPermission = arr; console.log('=== 权限store更新 ===', arr); }, // 强制刷新权限数据 async refreshPermissions() { console.log('=== 强制刷新权限数据 ==='); this.routeFlag = false; // 重置路由标志,强制重新生成路由 return this.generateRoutes(); }, generateRoutes() { return new Promise((resolve) => { // 向后端请求路由数据 getUserMenu().then((res) => { const { code, data } = res; console.log('=== 权限路由生成 ===', { code, data }); const btnList = data.filter((i) => i.type === 2); const permissionList = btnList.map((i) => i.authority).filter(auth => auth); // 过滤掉空权限 console.log('=== 设置用户权限列表 ===', permissionList); this.setUserPermission(permissionList); let menuList = data.filter((i) => i.type !== 2); menuList = menuList.map((item) => { // 确保 routeUrl 存在且不为空 let routeUrl = item.routeUrl || item.pageUrl || ''; // 规范化路径 routeUrl = normalizeRoutePath(routeUrl); return { id: item.id, parentId: item.parentId, // eslint-disable-next-line no-use-before-define name: capitalizeFirstLetter(routeUrl), path: routeUrl, component: item.pageUrl, alwaysShow: true, meta: { title: item.name, icon: item.icon, noCache: false, link: null, }, }; }); menuList = handleTree(menuList, 'id', 'parentId'); JSON.parse(JSON.stringify(menuList)); const sdata = JSON.parse(JSON.stringify(menuList)); console.log('=== 处理后的菜单列表 ===', menuList); console.log('=== 路径检查 ===', menuList.map(item => ({ name: item.name, path: item.path }))); // 检查并修复双斜杠路径 const doubleSlashPaths = menuList.filter(item => item.path && item.path.includes('//')); if (doubleSlashPaths.length > 0) { console.error('=== 发现双斜杠路径 ===', doubleSlashPaths); // 修复双斜杠路径 menuList.forEach(item => { if (item.path && item.path.includes('//')) { const originalPath = item.path; item.path = item.path.replace(/\/+/g, '/'); console.warn('修复菜单路径:', originalPath, '->', item.path); } }); } // eslint-disable-next-line no-use-before-define const rewriteRoutes = filterAsyncRouter(menuList, false, true); // eslint-disable-next-line no-use-before-define const sidebarRoutes = filterAsyncRouter(sdata); // eslint-disable-next-line no-use-before-define const asyncRoutes = filterDynamicRoutes(dynamicRoutes); console.log('=== 最终路由配置 ===', { rewriteRoutes, sidebarRoutes, asyncRoutes }); asyncRoutes.forEach((route) => { router.addRoute(route); }); this.setSidebarRouters(sidebarRoutes, code); this.setRoutes(rewriteRoutes); resolve(rewriteRoutes); }).catch((error) => { console.error('=== 获取用户菜单失败 ===', error); // 如果获取菜单失败,返回空路由数组 this.setSidebarRouters([], 500); this.setRoutes([]); resolve([]); }); }); }, }, }); function capitalizeFirstLetter(string) { // 处理 null 或 undefined 值 if (!string || typeof string !== 'string') { console.warn('capitalizeFirstLetter: Invalid string input:', string); return 'Unknown'; } // eslint-disable-next-line no-param-reassign string = string.replace('/', ''); return string.charAt(0).toUpperCase() + string.toLowerCase().slice(1); } // 规范化路由路径 function normalizeRoutePath(path) { if (!path || typeof path !== 'string') { return '/'; } // 移除首尾空格 path = path.trim(); // 移除重复的斜杠 path = path.replace(/\/+/g, '/'); // 确保路径以 "/" 开头 if (!path.startsWith('/')) { path = '/' + path; } // 确保路径不为空 if (path === '') { path = '/'; } return path; } // 安全拼接路径 function joinPaths(parentPath, childPath) { // 规范化父路径 parentPath = normalizeRoutePath(parentPath); childPath = normalizeRoutePath(childPath); // 如果父路径是根路径,直接返回子路径 if (parentPath === '/') { return childPath; } // 如果子路径是根路径,直接返回父路径 if (childPath === '/') { return parentPath; } // 确保父路径不以斜杠结尾,子路径不以斜杠开头 if (parentPath.endsWith('/')) { parentPath = parentPath.slice(0, -1); } if (childPath.startsWith('/')) { childPath = childPath.slice(1); } // 拼接路径 const joinedPath = parentPath + '/' + childPath; // 再次规范化,确保没有双斜杠 return normalizeRoutePath(joinedPath); } // 遍历后台传来的路由字符串,转换为组件对象 // eslint-disable-next-line no-unused-vars function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) { return asyncRouterMap.filter((route) => { if (type && route.children) { // eslint-disable-next-line no-use-before-define,no-param-reassign route.children = filterChildren(route.children); } if (route.component) { if (route.component === 'Layout') { // eslint-disable-next-line no-param-reassign route.component = Layout; } else if (!route.component) { // eslint-disable-next-line no-param-reassign route.component = Layout; } else { // eslint-disable-next-line no-param-reassign,no-use-before-define route.component = loadView(route.component); } } if (route.children != null && route.children && route.children.length) { // eslint-disable-next-line no-param-reassign route.children = filterAsyncRouter(route.children, route, type); } else { // eslint-disable-next-line no-param-reassign delete route.children; // eslint-disable-next-line no-param-reassign delete route.redirect; } // eslint-disable-next-line no-param-reassign delete route.id; // eslint-disable-next-line no-param-reassign delete route.parentId; return true; }); } function filterChildren(childrenMap, lastRouter = false) { let children = []; childrenMap.forEach((el) => { if (el.children && el.children.length) { if (el.component === 'ParentView' && !lastRouter) { el.children.forEach((c) => { // eslint-disable-next-line no-param-reassign c.path = joinPaths(el.path, c.path); if (c.children && c.children.length) { children = children.concat(filterChildren(c.children, c)); return; } children.push(c); }); return; } } if (lastRouter) { // eslint-disable-next-line no-param-reassign el.path = joinPaths(lastRouter.path, el.path); if (el.children && el.children.length) { children = children.concat(filterChildren(el.children, el)); return; } } children = children.concat(el); }); return children; } // 动态路由遍历,验证是否具备权限 export function filterDynamicRoutes(routes) { const res = []; routes.forEach((route) => { if (route.permissions) { if (auth.hasPermiOr(route.permissions)) { res.push(route); } } else if (route.roles) { if (auth.hasRoleOr(route.roles)) { res.push(route); } } }); return res; } export const loadView = (view) => { // 添加默认的视图路径作为后备方案 const defaultView = () => import('~/views/entry/details.vue'); if (!view) { console.warn('loadView: view parameter is empty, using default view'); return defaultView; } console.log('loadView: Loading view:', view); let res; // eslint-disable-next-line guard-for-in,no-restricted-syntax for (const path in modules) { const dir = path.split('views/')[1].split('.vue')[0]; if (dir === view) { console.log('loadView: Found matching module:', path); // 使用函数包装导入过程,添加错误处理 res = () => modules[path]().catch((error) => { console.error('Failed to load module:', path, error); // 如果模块加载失败,返回默认视图 return import('~/views/entry/details.vue'); }); break; // 找到匹配的模块后立即退出循环 } } // 如果没有找到匹配的视图,返回默认视图 if (!res) { console.warn('loadView: View not found:', view, 'Available modules:', Object.keys(modules)); return defaultView; } return res; }; export default usePermissionStore;