首页 > 其他分享 >vue 实现路由权限方式2,通过菜单角色,和用户角色是否重合实现匹配过滤权限

vue 实现路由权限方式2,通过菜单角色,和用户角色是否重合实现匹配过滤权限

时间:2024-06-05 21:54:58浏览次数:19  
标签:vue const 角色 roles route meta 权限 路由

接口数据查看,业务方式查看

  • 给角色分配路由权限,然后路由信息上meta就会有哪些角色可以访问的数组。就是说一个路径,哪些角色可以访问,都在meta下的roles里面保存着
    image
  • 接着用户角色分配
    image

前端代码实现

  • 核心代码
    通过用户信息上用户的角色数组和路由meta上的角色数组是否包含用户角色,来过滤用户是否有权限访问这个路由路径
/**
 * 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;
};

permission.ts代码

import { RouteRecordRaw } from "vue-router";
import { constantRoutes } from "@/router";
import { store } from "@/store";
import MenuAPI from "@/api/menu";
import { RouteVO } from "@/api/menu/model";

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);
}

路由守卫实现权限路由添加到可访问的路由表中

hasroles 状态这个挺关键的,主要标识是否添加过权限路由,也代表用户角色

export function setupPermission() {
  // 白名单路由
  const whiteList = ["/login"];

  router.beforeEach(async (to, from, next) => {
    NProgress.start();
    const hasToken = localStorage.getItem(TOKEN_KEY);
    if (hasToken) {
      if (to.path === "/login") {
        // 如果已登录,跳转首页
        next({ path: "/" });
        NProgress.done();
      } else {
        const userStore = useUserStore();
        const hasRoles =
          userStore.user.roles && userStore.user.roles.length > 0;

          //hasRoles 可以判断是否添加过生成的权限路由,如果有直接跳转页面,没有再去添加权限路由
        if (hasRoles) {
          // 未匹配到任何路由,跳转404
          if (to.matched.length === 0) {
            from.name ? next({ name: from.name }) : next("/404");
          } else {
            next();
          }
        } else {
          // 还没添加权限路由,这里实现添加下
          const permissionStore = usePermissionStore();
          try {
            const { roles } = await userStore.getUserInfo(); //请求到用户角色,之后会作为状态写入store标识是否添加过权限路由
            const accessRoutes = await permissionStore.generateRoutes(roles);
            accessRoutes.forEach((route: RouteRecordRaw) => {
              router.addRoute(route);
            });
            next({ ...to, replace: true });
          } catch (error) {
            // 移除 token 并跳转登录页
            await userStore.resetToken();
            next(`/login?redirect=${to.path}`);
            NProgress.done();
          }
        }
      }
    } else {
      // 未登录可以访问白名单页面
      if (whiteList.indexOf(to.path) !== -1) {
        next();
      } else {
        next(`/login?redirect=${to.path}`);
        NProgress.done();
      }
    }
  });

  router.afterEach(() => {
    NProgress.done();
  });
}

总结

就是通过后台路由绑定角色,角色信息记录在meta里面,meta里面的roles代表了这个路径哪些角色可以访问。
第二再用户绑定角色,用户绑定的角色是否存在某个路由的meta下的roles,有则表示可以访问该路由,没有则表示不能访问该路由。

标签:vue,const,角色,roles,route,meta,权限,路由
From: https://www.cnblogs.com/jocongmin/p/18233975

相关文章

  • 前端-Vue 快速上手
    Vue概念:Vue是一个用于构建用户界面的渐进式框架①构建用户界面:基于数据动态渲染页面②渐进式:循环渐进的学习③框架:一套完整的项目解决方案,提升开发效率↑(理解记忆规则)   规则→官网(v2.cn.vuejs.org / cn.vuejs.org)Vue的两种使用方式:1.Vue核......
  • Vue 3 Teleport:掌控渲染的艺术
    title:Vue3Teleport:掌控渲染的艺术date:2024/6/5updated:2024/6/5description:这篇文章介绍了Vue3框架中的一个创新特性——Teleport,它允许开发者将组件内容投送到文档对象模型(DOM)中的任意位置,即使这个位置在组件的挂载点之外。Teleport旨在解决某些特定场景下的布局和......
  • Vue的viewUI框架安装与使用
    1.安装pycharm进入到项目目录C:\Users\Administrator\PycharmProjects\myvue02>npminstallview-design--save 2.引用在项目的src/main.js中加入如下代码【src/main.js】importVuefrom'vue'importAppfrom'./App.vue'importViewUIfrom'view-design&......
  • vue中html导出成word
    vue中将html中内容转换为word文档导出,无需模板,可以导出echarts图表。使用html-docx-js、file-saver。先将html中内容获取,之后将页面上的元素转成图片,然后把图片放到word文件中进行导出。参考链接:https://blog.csdn.net/weixin_47494533/article/details/13701867......
  • vue中html导出成word
    vue中将html中内容转换为word文档导出,无需模板,可以导出echarts图表。使用html-docx-js、file-saver。先将html中内容获取,之后将页面上的元素转成图片,然后把图片放到word文件中进行导出。参考链接:https://blog.csdn.net/weixin_47494533/article/details/137018678 ......
  • Ant Design Vue 动态表头并填充数据
    AntDesignVue动态表头并填充数据AntDesignVue是基于AntDesign的Vue版本,它为Vue.js提供了一套高质量的UI组件库。在本文中,我们将介绍如何使用AntDesignVue创建一个动态表头并填充数据。首先,确保你已经安装了AntDesignVue。如果还没有安装,可以通过以下命......
  • 【vuex小试牛刀】
    了解vuex核心概念请移步https://vuex.vuejs.org/zh/#一、初始vuex#1.1vuex是什么就是把需要共享的变量全部存储在一个对象里面,然后将这个对象放在顶层组件中供其他组件使用父子组件通信时,我们通常会采用props+emit这种方式。但当通信......
  • 【慢慢理解Vue的设计思想】
    #理解Vue的设计思想MVVM框架的三要素:数据响应式、模板引擎及其渲染数据响应式:监听数据变化并在视图中更新Object.defineProperty()Proxy模版引擎:提供描述视图的模版语法插值:{{}}指令:v-bind,v-on,v-model,v-for,v-if渲染:如何将模板转换为html模板=>vdom=>dom#......
  • vue中将html导出成pdf
    vue中将页面html内容导出成pdf文件格式,使用 html2canvas、jspdf。首先使用 html2canvas将内容转换为图片,之后写入pdf。1、引用第一个.将页面html转换成图片npminstall--savehtml2canvas第二个.将图片生成pdfnpminstalljspdf--save2、创建  exportTo......
  • VUE网易云【附:资料➕文档】
    前言:我是源码分享交流Coding,专注Java+Vue领域,专业提供程序设计开发、源码分享、技术指导讲解、各类项目免费分享,定制和毕业设计服务!免费获取方式--->>文章末尾处!项目介绍007:项目名:仿网易云技术栈:VUE(页面数据对接网易云官方数据,需联网运行)访问网页:http://localh......