import { RouteRecordRaw } from "vue-router"; import { constantRoutes } from "@/router"; import { store } from "@/store"; import MenuAPI from "@/api/menu"; import { RouteVO } from "@/api/menu/types"; const modules = import.meta.glob("../../views/**/**.vue"); const Layout = () => import("@/layout/index.vue"); /** * Use meta.role to determine if the current user has permission * * @param roles 用户角色集合 * @param route 路由 * @returns */ const hasPermission = (roles: string[], route: RouteRecordRaw) => { if (route.meta && route.meta.roles) { // 角色【超级管理员】拥有所有权限,忽略校验 if (roles.includes("ROOT")) { return true; } return roles.some((role) => { if (route.meta?.roles) { return route.meta.roles.includes(role); } }); } return false; }; /** * 递归过滤有权限的动态路由 * * @param routes 接口返回所有的动态路由 * @param roles 用户角色集合 * @returns 返回用户有权限的动态路由 */ const filterAsyncRoutes = (routes: RouteVO[], roles: string[]) => { const asyncRoutes: RouteRecordRaw[] = []; routes.forEach((route) => { const tmpRoute = { ...route } as RouteRecordRaw; // 深拷贝 route 对象 避免污染 if (hasPermission(roles, tmpRoute)) { // 如果是顶级目录,替换为 Layout 组件 if (tmpRoute.component?.toString() == "Layout") { tmpRoute.component = Layout; } else { // 如果是子目录,动态加载组件 const component = modules[`../../views/${tmpRoute.component}.vue`]; if (component) { tmpRoute.component = component; } else { tmpRoute.component = modules[`../../views/error-page/404.vue`]; } } if (tmpRoute.children) { tmpRoute.children = filterAsyncRoutes(route.children, roles); } asyncRoutes.push(tmpRoute); } }); return asyncRoutes; }; // setup export const usePermissionStore = defineStore("permission", () => { // state const routes = ref([]); // actions function setRoutes(newRoutes: RouteRecordRaw[]) { routes.value = constantRoutes.concat(newRoutes); } /** * 生成动态路由 * * @param roles 用户角色集合 * @returns */ function generateRoutes(roles: string[]) { return new Promise((resolve, reject) => { // 接口获取所有路由 MenuAPI.getRoutes() .then((data) => { // 过滤有权限的动态路由 const accessedRoutes = filterAsyncRoutes(data, roles); setRoutes(accessedRoutes); resolve(accessedRoutes); }) .catch((error) => { reject(error); }); }); } /** * 获取与激活的顶部菜单项相关的混合模式左侧菜单集合 */ const mixLeftMenus = ref([]); function setMixLeftMenus(topMenuPath: string) { const matchedItem = routes.value.find((item) => item.path === topMenuPath); if (matchedItem && matchedItem.children) { mixLeftMenus.value = matchedItem.children; } } return { routes, setRoutes, generateRoutes, mixLeftMenus, setMixLeftMenus, }; }); // 非setup export function usePermissionStoreHook() { return usePermissionStore(store); }