Vue 根据权限过滤菜单、按钮
菜单权限
store\modules\permisstion.ts -> generateRoutes()
import { RouteRecordRaw } from "vue-router";
//privateRoutes 前端配置好的JS菜单,权限控制由前端控制,【后端省事】
import { constantRoutes, privateRoutes } from "@/router";
import { store } from "@/store";
import router from "@/router";
import MenuAPI, { type RouteVO } from "@/api/system/menu";
import { MenuInfo } from "@/api/auth";
const modules = import.meta.glob("../../views/**/**.vue");
const Layout = () => import("@/layout/index.vue");
const menuList = useStorage<MenuInfo[]>("menuList", []);
export const usePermissionStore = defineStore("permission", () => {
// 储所有路由,包括静态路由和动态路由
const routes = ref<RouteRecordRaw[]>([]);
// 混合模式左侧菜单路由
const mixedLayoutLeftRoutes = ref<RouteRecordRaw[]>([]);
// 路由是否加载完成
const isRoutesLoaded = ref(false);
/**
* 获取后台动态路由数据,解析并注册到全局路由
*
* @returns Promise<RouteRecordRaw[]> 解析后的动态路由列表
*/
function generateRoutes() {
return new Promise<RouteRecordRaw[]>((resolve, reject) => {
// console.log("menuList", menuList);
// console.log("privateRoutes", privateRoutes);
// console.log("IsSuperAdmin", useUserStore().userInfo.IsSuperAdmin);
// 超级管理员拥有所有权限
if (useUserStore().userInfo.IsSuperAdmin == 1) {
//管理员看到所有
routes.value = [...constantRoutes, ...privateRoutes];
isRoutesLoaded.value = true;
resolve(privateRoutes);
return;
}
// 筛选出有权限的菜单
const filterPrivateRoutes = ref<RouteRecordRaw[]>([]);
const menuCodeList = ref(
menuList.value.map((item) => item.Code).filter((code) => code !== undefined)
);
// console.log("menuCodeList", menuCodeList);
//过滤出有权限的菜单
privateRoutes.forEach((item) => {
if (Array.isArray(item.code)) {
// 过滤出有权限的子菜单
const authorizedChildren = item.children?.filter((child) =>
menuCodeList.value.includes(child.code)
);
// 如果有权限的子菜单存在,则添加到结果中
if (authorizedChildren && authorizedChildren.length > 0) {
filterPrivateRoutes.value.push({
...item,
children: authorizedChildren,
});
}
} else {
if (menuCodeList.value.includes(item.code)) {
filterPrivateRoutes.value.push(item);
}
}
});
//console.log("filterPrivateRoutes", filterPrivateRoutes.value);
routes.value = [...constantRoutes, ...filterPrivateRoutes.value];
isRoutesLoaded.value = true;
resolve(filterPrivateRoutes.value);
//不从服务器获取
// MenuAPI.getRoutes()
// .then((data) => {
// const dynamicRoutes = parseDynamicRoutes(data);
// routes.value = [...constantRoutes, ...dynamicRoutes, ...privateRoutes];
// //routes.value = [...constantRoutes, ...privateRoutes]; //去掉 dynamicRoutes 隐藏 原始菜单
// isRoutesLoaded.value = true;
// resolve(dynamicRoutes);
// })
// .catch((error) => {
// reject(error);
// });
});
}
按钮权限
directive\permission\index.ts
import type { Directive, DirectiveBinding } from "vue";
import { useUserStore } from "@/store";
/**
* 按钮权限
*/
export const hasPerm: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding) {
const requiredPerms = binding.value;
// 校验传入的权限值是否合法
if (!requiredPerms || (typeof requiredPerms !== "string" && !Array.isArray(requiredPerms))) {
throw new Error(
"需要提供权限标识!例如:v-has-perm=\"'sys:user:add'\" 或 v-has-perm=\"['sys:user:add', 'sys:user:edit']\""
);
}
const { IsSuperAdmin } = useUserStore().userInfo;
const functionList = useUserStore().functionList;
const perms = ref(functionList.map((item) => item.Code).filter((code) => code !== undefined));
// console.log("IsSuperAdmin", IsSuperAdmin);
// console.log("functionList", functionList);
// console.log("perms", perms);
// console.log("requiredPerms", requiredPerms);
// 超级管理员拥有所有权限
if (IsSuperAdmin == 1) {
return;
}
// // 检查权限
const hasAuth = Array.isArray(requiredPerms)
? requiredPerms.some((perm) => perms.value.includes(perm))
: perms.value.includes(requiredPerms);
// console.log("hasAuth", hasAuth);
//如果没有权限,移除该元素
if (!hasAuth && el.parentNode) {
// console.log("removeChild", el);
el.parentNode.removeChild(el);
}
},
};
/**
* 角色权限指令
*/
export const hasRole: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding) {
const requiredRoles = binding.value;
// 校验传入的角色值是否合法
if (!requiredRoles || (typeof requiredRoles !== "string" && !Array.isArray(requiredRoles))) {
throw new Error(
"需要提供角色标识!例如:v-has-role=\"'ADMIN'\" 或 v-has-role=\"['ADMIN', 'TEST']\""
);
}
const { roles } = useUserStore().userInfo;
// 检查是否有对应角色权限
const hasAuth = Array.isArray(requiredRoles)
? requiredRoles.some((role) => roles.includes(role))
: roles.includes(requiredRoles);
// 如果没有权限,移除元素
if (!hasAuth && el.parentNode) {
el.parentNode.removeChild(el);
}
},
};
路由权限
plugins\permission.ts
import type { NavigationGuardNext, RouteLocationNormalized, RouteRecordRaw } from "vue-router";
import NProgress from "@/utils/nprogress";
import { getAccessToken } from "@/utils/auth";
import router from "@/router";
import { usePermissionStore, useUserStore } from "@/store";
export function setupPermission() {
// 白名单路由
const whiteList = ["/login", "/datav"];
// 公共菜单,只要登录了就能访问
const constantPathList = ["/redirect", "/dashboard", "/401", "/404", "/profile", "/myNotice"];
router.beforeEach(async (to, from, next) => {
// 开始进度条
NProgress.start();
const isLogin = !!getAccessToken(); // 判断是否登录
if (isLogin) {
if (to.path === "/login") {
// 已登录,访问登录页,跳转到首页
next({ path: "/" });
} else {
const permissionStore = usePermissionStore();
//console.log("permissionStore", permissionStore);
// 判断路由是否加载完成
if (permissionStore.isRoutesLoaded) {
if (to.matched.length === 0) {
// 路由未匹配,跳转到404
console.log("Route 加载完成后,路由未匹配 => ", to.path);
next("/404");
} else if (whiteList.includes(to.path)) {
console.log("Route 加载完成后,白名单 => ", to.path);
next();
} else {
const routeExists = constantPathList.some((path) => to.path.includes(path));
if (!routeExists) {
//有权限的 + 白名单 = 可访问
//console.log("Route 加载【后】 => ", to.path);
permissionCheck(permissionStore, to, next);
}
// 动态设置页面标题
const title = (to.params.title as string) || (to.query.title as string);
if (title) {
to.meta.title = title;
}
next();
}
} else {
try {
// 生成动态路由 -- 直接游览器里输入地址会走到这边
const dynamicRoutes = await permissionStore.generateRoutes();
const routeExists = constantPathList.some((path) => to.path.includes(path));
if (!routeExists) {
//有权限的 + 白名单 = 可访问
//console.log("Route 加载【前】 => ", to.path);
permissionCheck(permissionStore, to, next);
}
dynamicRoutes.forEach((route: RouteRecordRaw) => router.addRoute(route));
next({ ...to, replace: true });
} catch (error) {
console.error(error);
// 路由加载失败,重置 token 并重定向到登录页
await useUserStore().clearUserData();
redirectToLogin(to, next);
NProgress.done();
}
}
}
} else {
// 未登录,判断是否在白名单中
if (whiteList.includes(to.path)) {
next();
} else {
// 不在白名单,重定向到登录页
redirectToLogin(to, next);
NProgress.done();
}
}
});
// 后置守卫,保证每次路由跳转结束时关闭进度条
router.afterEach(() => {
NProgress.done();
});
}
function permissionCheck(permissionStore: any, to: any, next: NavigationGuardNext) {
//console.log("permissionStore.routes", permissionStore.routes);
let found = false;
permissionStore.routes.forEach((route: RouteRecordRaw) => {
//console.log("permissionStore.route", route.path);
if (to.path.includes(route.path) && route.path != "/") {
//console.log("to.path => ", to.path);
//console.log("route.path => ", route.path);
found = true;
}
});
if (!found) {
console.log("无权 => ", to.path);
next("/401");
}
}
// 重定向到登录页
function redirectToLogin(to: RouteLocationNormalized, next: NavigationGuardNext) {
const params = new URLSearchParams(to.query as Record<string, string>);
const queryString = params.toString();
const redirect = queryString ? `${to.path}?${queryString}` : to.path;
next(`/login?redirect=${encodeURIComponent(redirect)}`);
}
/** 判断是否有权限 */
export function hasAuth(value: string | string[], type: "button" | "role" = "button") {
const { roles, perms } = useUserStore().userInfo;
// 超级管理员 拥有所有权限
if (type === "button" && roles.includes("ROOT")) {
return true;
}
const auths = type === "button" ? perms : roles;
return typeof value === "string"
? auths.includes(value)
: value.some((perm) => auths.includes(perm));
}
本文来自博客园,作者:VipSoft 转载请注明原文链接:https://www.cnblogs.com/vipsoft/p/19053185
浙公网安备 33010602011771号