当前位置:   article > 正文

vue3权限管理实现以及按钮权限实现_vue3 v-authority

vue3 v-authority
  1. /src/router/index.ts
  2. import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
  3. export const Layout = () => import("@/layout/index.vue");
  4. // 静态路由
  5. export const constantRoutes: RouteRecordRaw[] = [
  6. {
  7. path: "/redirect",
  8. component: Layout,
  9. meta: { hidden: true },
  10. children: [
  11. {
  12. path: "/redirect/:path(.*)",
  13. component: () => import("@/views/redirect/index.vue"),
  14. },
  15. ],
  16. },
  17. {
  18. path: "/login",
  19. component: () => import("@/views/login/index.vue"),
  20. meta: { hidden: true },
  21. },
  22. {
  23. path: "/",
  24. component: Layout,
  25. redirect: "/dashboard",
  26. children: [
  27. {
  28. path: "dashboard",
  29. component: () => import("@/views/dashboard/index.vue"),
  30. name: "Dashboard",
  31. meta: { title: "dashboard", icon: "homepage", affix: true },
  32. },
  33. {
  34. path: "401",
  35. component: () => import("@/views/error-page/401.vue"),
  36. meta: { hidden: true },
  37. },
  38. {
  39. path: "404",
  40. component: () => import("@/views/error-page/404.vue"),
  41. meta: { hidden: true },
  42. },
  43. ],
  44. },
  45. // 外部链接
  46. /*{
  47. path: '/external-link',
  48. component: Layout,
  49. children: [
  50. {
  51. path: 'https://www.cnblogs.com/haoxianrui/',
  52. meta: { title: '外部链接', icon: 'link' }
  53. }
  54. ]
  55. }*/
  56. // 多级嵌套路由
  57. /* {
  58. path: '/nested',
  59. component: Layout,
  60. redirect: '/nested/level1/level2',
  61. name: 'Nested',
  62. meta: {title: '多级菜单', icon: 'nested'},
  63. children: [
  64. {
  65. path: 'level1',
  66. component: () => import('@/views/nested/level1/index.vue'),
  67. name: 'Level1',
  68. meta: {title: '菜单一级'},
  69. redirect: '/nested/level1/level2',
  70. children: [
  71. {
  72. path: 'level2',
  73. component: () => import('@/views/nested/level1/level2/index.vue'),
  74. name: 'Level2',
  75. meta: {title: '菜单二级'},
  76. redirect: '/nested/level1/level2/level3',
  77. children: [
  78. {
  79. path: 'level3-1',
  80. component: () => import('@/views/nested/level1/level2/level3/index1.vue'),
  81. name: 'Level3-1',
  82. meta: {title: '菜单三级-1'}
  83. },
  84. {
  85. path: 'level3-2',
  86. component: () => import('@/views/nested/level1/level2/level3/index2.vue'),
  87. name: 'Level3-2',
  88. meta: {title: '菜单三级-2'}
  89. }
  90. ]
  91. }
  92. ]
  93. },
  94. ]
  95. }*/
  96. ];
  97. /**
  98. * 创建路由
  99. */
  100. const router = createRouter({
  101. history: createWebHashHistory(),
  102. routes: constantRoutes as RouteRecordRaw[],
  103. // 刷新时,滚动条位置还原
  104. scrollBehavior: () => ({ left: 0, top: 0 }),
  105. });
  106. /**
  107. * 重置路由
  108. */
  109. export function resetRouter() {
  110. router.replace({ path: "/login" });
  111. location.reload();
  112. }
  113. export default router;
  1. /src/store/permission
  2. import { RouteRecordRaw } from "vue-router";
  3. import { defineStore } from "pinia";
  4. import { constantRoutes } from "@/router";
  5. import { store } from "@/store";
  6. import { listRoutes } from "@/api/menu";
  7. const modules = import.meta.glob("../../views/**/**.vue");
  8. const Layout = () => import("@/layout/index.vue");
  9. /**
  10. * Use meta.role to determine if the current user has permission
  11. *
  12. * @param roles 用户角色集合
  13. * @param route 路由
  14. * @returns
  15. */
  16. const hasPermission = (roles: string[], route: RouteRecordRaw) => {
  17. if (route.meta && route.meta.roles) {
  18. // 角色【超级管理员】拥有所有权限,忽略校验
  19. if (roles.includes("ROOT")) {
  20. return true;
  21. }
  22. return roles.some((role) => {
  23. if (route.meta?.roles !== undefined) {
  24. return (route.meta.roles as string[]).includes(role);
  25. }
  26. });
  27. }
  28. return false;
  29. };
  30. /**
  31. * 递归过滤有权限的异步(动态)路由
  32. *
  33. * @param routes 接口返回的异步(动态)路由
  34. * @param roles 用户角色集合
  35. * @returns 返回用户有权限的异步(动态)路由
  36. */
  37. const filterAsyncRoutes = (routes: RouteRecordRaw[], roles: string[]) => {
  38. const asyncRoutes: RouteRecordRaw[] = [];
  39. routes.forEach((route) => {
  40. const tmpRoute = { ...route }; // ES6扩展运算符复制新对象
  41. // console.log(tmpRoute,route);
  42. // 判断用户(角色)是否有该路由的访问权限
  43. // console.log(modules,tmpRoute.component);
  44. if (hasPermission(roles, tmpRoute)) {
  45. if (tmpRoute.component?.toString() == "Layout") {
  46. tmpRoute.component = Layout;
  47. } else {
  48. const component = modules[`../../views/${tmpRoute.component}.vue`];
  49. // console.log(modules['../../views/system/menu/index.vue'],'../../views/system/menu/index.vue');
  50. if (component) {
  51. tmpRoute.component = component;
  52. } else {
  53. tmpRoute.component = modules[`../../views/error-page/404.vue`];
  54. }
  55. }
  56. // debugger
  57. if (tmpRoute.children) {
  58. tmpRoute.children = filterAsyncRoutes(tmpRoute.children, roles);
  59. }
  60. // console.log(asyncRoutes);
  61. asyncRoutes.push(tmpRoute);
  62. // console.log(asyncRoutes)
  63. }
  64. });
  65. return asyncRoutes;
  66. };
  67. // setup
  68. export const usePermissionStore = defineStore("permission", () => {
  69. // state
  70. const routes = ref<RouteRecordRaw[]>([]);
  71. // actions
  72. function setRoutes(newRoutes: RouteRecordRaw[]) {
  73. routes.value = constantRoutes.concat(newRoutes);
  74. }
  75. /**
  76. * 生成动态路由
  77. *
  78. * @param roles 用户角色集合
  79. * @returns
  80. */
  81. function generateRoutes(roles: string[]) {
  82. return new Promise<RouteRecordRaw[]>((resolve, reject) => {
  83. // 接口获取所有路由
  84. listRoutes()
  85. .then(({ data: asyncRoutes }) => {
  86. console.log(asyncRoutes,'asyncRoutes');
  87. // 根据角色获取有访问权限的路由
  88. const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles);
  89. console.log(accessedRoutes,'accessedRoutes');
  90. setRoutes(accessedRoutes);
  91. resolve(accessedRoutes);
  92. })
  93. .catch((error) => {
  94. reject(error);
  95. });
  96. });
  97. }
  98. return { routes, setRoutes, generateRoutes };
  99. });
  100. // 非setup
  101. export function usePermissionStoreHook() {
  102. return usePermissionStore(store);
  103. }
  1. /src/permission
  2. import router from "@/router";
  3. import { useUserStoreHook } from "@/store/modules/user";
  4. import { usePermissionStoreHook } from "@/store/modules/permission";
  5. import NProgress from "nprogress";
  6. import "nprogress/nprogress.css";
  7. NProgress.configure({ showSpinner: false }); // 进度条
  8. const permissionStore = usePermissionStoreHook();
  9. // 白名单路由
  10. const whiteList = ["/login"];
  11. router.beforeEach(async (to, from, next) => {
  12. NProgress.start();
  13. const hasToken = localStorage.getItem("accessToken");
  14. if (hasToken) {
  15. if (to.path === "/login") {
  16. // 如果已登录,跳转首页
  17. next({ path: "/" });
  18. NProgress.done();
  19. } else {
  20. const userStore = useUserStoreHook();
  21. const hasRoles = userStore.roles && userStore.roles.length > 0;
  22. if (hasRoles) {
  23. // 未匹配到任何路由,跳转404
  24. if (to.matched.length === 0) {
  25. from.name ? next({ name: from.name }) : next("/404");
  26. } else {
  27. next();
  28. }
  29. } else {
  30. try {
  31. const { roles,perms } = await userStore.getInfo();
  32. console.log(roles,'roles');
  33. const accessRoutes = await permissionStore.generateRoutes(roles);
  34. accessRoutes.forEach((route) => {
  35. router.addRoute(route);
  36. });
  37. next({ ...to, replace: true });
  38. } catch (error) {
  39. // 移除 token 并跳转登录页
  40. await userStore.resetToken();
  41. next(`/login?redirect=${to.path}`);
  42. NProgress.done();
  43. }
  44. }
  45. }
  46. } else {
  47. // 未登录可以访问白名单页面
  48. if (whiteList.indexOf(to.path) !== -1) {
  49. next();
  50. } else {
  51. next(`/login?redirect=${to.path}`);
  52. NProgress.done();
  53. }
  54. }
  55. });
  56. router.afterEach(() => {
  57. NProgress.done();
  58. });
  1. main.ts
  2. import { createApp } from 'vue';
  3. import App from './App.vue';
  4. import router from '@/router';
  5. import { setupStore } from '@/store';
  6. import { setupDirective } from '@/directive';
  7. //引入权限过滤功能
  8. import '@/permission';
  9. // 本地SVG图标
  10. import 'virtual:svg-icons-register';
  11. // 国际化
  12. import i18n from '@/lang/index';
  13. // 样式
  14. import 'element-plus/theme-chalk/dark/css-vars.css';
  15. import '@/styles/index.scss';
  16. import 'uno.css';
  17. const app = createApp(App);
  18. // 全局注册 自定义指令(directive)
  19. setupDirective(app);
  20. // 全局注册 状态管理(store)
  21. setupStore(app);
  22. app.use(router).use(i18n).mount('#app');

以上是路由菜单管理的权限

  1. //指令权限 、src/directive/index
  2. import { useUserStoreHook } from '@/store/modules/user';
  3. import { Directive, DirectiveBinding } from 'vue';
  4. /**
  5. * 按钮权限
  6. */
  7. export const hasPerm: Directive = {
  8. mounted(el: HTMLElement, binding: DirectiveBinding) {
  9. // 「超级管理员」拥有所有的按钮权限
  10. const { roles, perms } = useUserStoreHook();
  11. if (roles.includes('ROOT')) {
  12. return true;
  13. }
  14. // 「其他角色」按钮权限校验
  15. const { value } = binding;
  16. if (value) {
  17. const requiredPerms = value; // DOM绑定需要的按钮权限标识
  18. const hasPerm = perms?.some(perm => {
  19. return requiredPerms.includes(perm);
  20. });
  21. if (!hasPerm) {
  22. el.parentNode && el.parentNode.removeChild(el);
  23. }
  24. } else {
  25. throw new Error(
  26. "need perms! Like v-has-perm=\"['sys:user:add','sys:user:edit']\""
  27. );
  28. }
  29. }
  30. };
  31. /**
  32. * 角色权限
  33. */
  34. export const hasRole: Directive = {
  35. mounted(el: HTMLElement, binding: DirectiveBinding) {
  36. const { value } = binding;
  37. if (value) {
  38. const requiredRoles = value; // DOM绑定需要的角色编码
  39. const { roles } = useUserStoreHook();
  40. const hasRole = roles.some(perm => {
  41. return requiredRoles.includes(perm);
  42. });
  43. if (!hasRole) {
  44. el.parentNode && el.parentNode.removeChild(el);
  45. }
  46. } else {
  47. throw new Error("need roles! Like v-has-role=\"['admin','test']\"");
  48. }
  49. }
  50. };

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/58906
推荐阅读
相关标签
  

闽ICP备14008679号