Files
vue3-element-admin/src/store/modules/permission.ts
2024-06-24 08:21:46 +08:00

122 lines
3.3 KiB
TypeScript

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<RouteRecordRaw[]>([]);
// actions
function setRoutes(newRoutes: RouteRecordRaw[]) {
routes.value = constantRoutes.concat(newRoutes);
}
/**
* 生成动态路由
*
* @param roles 用户角色集合
* @returns
*/
function generateRoutes(roles: string[]) {
return new Promise<RouteRecordRaw[]>((resolve, reject) => {
// 接口获取所有路由
MenuAPI.getRoutes()
.then((data) => {
// 过滤有权限的动态路由
const accessedRoutes = filterAsyncRoutes(data, roles);
setRoutes(accessedRoutes);
resolve(accessedRoutes);
})
.catch((error) => {
reject(error);
});
});
}
/**
* 获取与激活的顶部菜单项相关的混合模式左侧菜单集合
*/
const mixLeftMenus = ref<RouteRecordRaw[]>([]);
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);
}