Vue3.0中实现的动态路由权限控制


前言

动态路由权限控制是项目常用的功能,这里介绍一种方式,通过将后端权限码与本地路由的JSON配置ID进行匹配,能够有效地实现用户权限的控制。不同角色的操作项是根据权限码匹配到的路由进行区分的,这样可以确保每个用户只能看到与自己所属角色相关的操作项。随后,通过特定方法将其转换为路由格式,并通过addRoute方法动态添加路由,从而实现了灵活而精准的用户权限路由控制。

功能分析

(1)路由配置项:由parentRouter父级路由和childrenRouter子级路由组成,关联方式为子级路由的parentId和父级路由的id匹配,后续权限路由匹配完毕后,通过formatTree方法生成嵌套路由的格式
(2)基于配置项的动态路由处理:将动态路由的配置项化,这样可以更加灵活地控制路由的生成。通过配置项,你可以定义路由的路径、组件、参数等信息,方便地管理和维护路由配置。
(3)使用标识代替组件引入:使用标识字符串来代替组件的引入。例如,将布局组件标识为’layout’。这样可以简化配置项的设置,并且可以方便地进行组件的替换或扩展
(4)与后端权限码绑定的JSON路由配置:可以在JSON路由配置项中添加parentId、id,绑定后端权限码信息。这样可以方便与后端返回的权限码进行匹配,从而生成符合条件的动态路由对象,实现基于权限的动态路由生成。
(5)权限联系:父级路由的权限按照子级路由的权限来处理,当一个有权限的子级路由都没有时,父级路由剔除

代码+详细解释

(1)此处举例动态路由映射文件配置

// 父级路由
export const parentRouter: RouterProp[] = [
  // 系统管理
  {
    path: "/system",
    meta: { title: "系统管理", icon: "xt", alwaysShow: true },
    component: "Layout",
    id: "4",
    parentId: "0",
  },
];
// 子集路由
export const childrenRouter: RouterProp[] = [
  // -----------------------系统管理子菜单-------------------------------
  {
    path: "role",
    name: "Role",
    meta: {
      title: "角色管理",
      breadcrumb: false,
      icon: "tsrzsb",
    },
    component: "system/role/index",
    id: "09000000",
    parentId: "4",
  },
  {
    path: "menu",
    name: "Menu",
    meta: {
      title: "菜单管理",
      breadcrumb: false,
      icon: "tsrzsb",
    },
    component: "system/menu/index",
    id: "09000001",
    parentId: "4",
  },
  {
    path: "pushFailureLog",
    name: "PushFailureLog",
    meta: {
      title: "推送失败日志",
      breadcrumb: false,
      icon: "tsrzsb",
    },
    component: "system/pushFailureLog/index",
    id: "09000002",
    parentId: "4",
  },
];

(2)递归生成路由对象方法

// 递归函数格式化树状菜单 
export function formatTree(data: any, id = 'permId', pid = 'parentId', child = 'children', root = '0') {
  const tree = [] as any
  if (data && data.length > 0) {
    data.forEach((item: any) => {
      // 获取顶层菜单,parent_id === 0
      if (item[pid] === root) {
        const children = formatTree(data, id, pid, child, item[id])
        if (children && children.length > 0) {
          item[child] = children
        }
        tree.push(item)
      }
    })
  }
  return tree
}

(3)新建pinia store文件,通过后台返回的路由权限码,映射本地路由JSON配置文件,并通过mapComponent方法生成常规路由对象格式

import { defineStore } from "pinia";
import Layout from "@/layout/index.vue";
import { formatTree } from "@/utils/tree";
import { parentRouter, childrenRouter } from "@/router/config";
import { getCookie } from "@/utils";
import { Stores } from "types/stores";
const _import = (file: string) => () => import("@/views/" + file + ".vue");
/**
 * 映射本地组件
 * @returns
 */
export function mapComponent(data: any) {
  const tree_data = data.filter((item: any) => {
    // component
    item.component = item.component === "layout" ? Layout : _import(`${item.component}`);
    // children
    if (item.children && Object.prototype.toString.call(item.children) === "[object Array]" && item.children.length >= 0) {
      mapComponent(item.children);
    }
    return item;
  });
  return tree_data;
}
export const routerStore = defineStore("permission", {
  state: (): Stores.router => ({
    asyncRouter: [],
  }),
  actions: {
    /**
     * 获取路由
     */
    getRouterAction(): Promise<RouterItem[]> {
      return new Promise(async (resolve, reject) => {
        // 权限code集合
        let powerCode = getCookie("permIds")?.split(",") || [];
        // 不存在路由
        if (!powerCode?.length) {
          reject();
          return false;
        }
        const asyncRouterArr: RouterItem[] = [];
        powerCode.forEach((id) => {
          const hasRouter = childrenRouter.filter((item: any) => item.id.split(",").includes(id));
          hasRouter.length && asyncRouterArr.push(hasRouter[0]);
        });
        // 去重
        powerCode = [...new Set(powerCode)];
        // 格式化树形
        const routerTree = formatTree([...parentRouter, ...asyncRouterArr], "id");
        /**
         * 过滤子级
         */
        const filterRouter = routerTree.filter((item: RouterItem) => {
          return item?.children?.length;
        });
        const routers = mapComponent(filterRouter);
        this.asyncRouter = routers;
        resolve(routers);
      });
    },
  },
});

(4)在路由守卫中,添加并更新动态路由

router.beforeEach(async (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => {
  // 判断是否有token
  const token = getCookie(TOKEN);
  const routeStore = routerStore();
  if (!token) {
    next("/login");
  } else {
    // 获取静态路由
    console.log("router.options", router.options);
    if (routeStore.asyncRouter.length === 0) {
      try {
        // 获取动态路由
        const asyncRouter = await routeStore.getRouterAction();
        // 获取静态路由
        const default_router_data = router.options.routes;
        // 更新静态路由
        router.options.routes = default_router_data.concat(asyncRouter);
        // 激活动态路由
        asyncRouter.forEach((item: RouterItem) => {
          router.addRoute(item);
        });
        // 确认进入下一个路由
        next({ ...to, replace: true });
      } catch {
        next();
      }
    } else {
      if (to.name === "Login") {
        if (!from.name) {
          next("/admin");
        } else {
          router.back();
        }
      } else {
        next();
      }
    }
  }
});

相关推荐

  1. Vue3.0实现动态权限控制

    2024-07-16 20:00:02       19 阅读
  2. 后台权限控制动态

    2024-07-16 20:00:02       38 阅读
  3. 前端面试 vue 权限控制

    2024-07-16 20:00:02       17 阅读
  4. Vue 实现动态

    2024-07-16 20:00:02       42 阅读
  5. vue3后台管理系统权限实现

    2024-07-16 20:00:02       30 阅读
  6. Vue实现分布式动态基本实现步骤介绍

    2024-07-16 20:00:02       54 阅读
  7. vue动态是什么该如何实现

    2024-07-16 20:00:02       36 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-16 20:00:02       50 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-16 20:00:02       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-16 20:00:02       43 阅读
  4. Python语言-面向对象

    2024-07-16 20:00:02       54 阅读

热门阅读

  1. 魁北克:美食的天堂

    2024-07-16 20:00:02       16 阅读
  2. 计算机视觉(CV)技术的优势和挑战

    2024-07-16 20:00:02       14 阅读
  3. JVM参数调优经验

    2024-07-16 20:00:02       17 阅读
  4. conda配置虚拟环境的常用命令

    2024-07-16 20:00:02       15 阅读
  5. MATLAB实现一个车辆悬架PID模拟系统

    2024-07-16 20:00:02       17 阅读
  6. python基础语法

    2024-07-16 20:00:02       15 阅读
  7. vue 项目代码架构

    2024-07-16 20:00:02       13 阅读
  8. 通过 Nginx 修复 CORS 漏洞

    2024-07-16 20:00:02       17 阅读
  9. [C++ 入门基础 - 引用]

    2024-07-16 20:00:02       18 阅读