Vue + Element Plus 实现权限管理系统(四):动态添加路由

在权限系统开发中,根据后端返回的菜单列表动态添加路由是非常常见的需求,它可以实现根据用户权限动态加载可访问的页面。在上一篇文章中,我们已经了解到如何渲染侧边栏菜单。本篇文章我们将重点介绍如何优化动态路由的添加过程。

将菜单列表转换为路由格式

学过 vue 的都知道,vue 路由包含 name,path,component 等属性,其中 component 属性是一个函数返回一个模块,比如

{
      path"/",
      name"Layout",
      component() =>
        import(/* webpackChunkName: "Layout" */ "../layout/index.vue"),
}

但是后端返回的却是一个字符串,如aa/bb,所以我们需要一个函数将后端返回的菜单列表处理成路由格式,在 utils 下新建filterRoute.ts

// 匹配views里面所有的.vue文件
const modules = import.meta.glob("../views/**/*.vue");

export const loadView = (view: any) => {
  let res;
  for (const path in modules) {
    const dir = path.split("views/")[1].split(".vue")[0];
    if (dir === view) {
      res = () => modules[path]();
    }
  }
  return res;
};
export const filterRoute = (data: any) => {
  data.forEach((item: any) => {
    if (item.children?.length > 0) {
      delete item.component;
      filterRoute(item.children);
    } else {
      item.component = loadView(item.component);
      // item.redirect = "/404";
    }
  });
  return data;
};

其中,import.meta.glob("../views/**/*.vue")可以匹配到 views 下所有后缀为.vue的文件,然后通过对比后端返回的 component 与views/后面的路径来生成 vue 路由中component所需要的格式。同时 filterRoute 函数中如果菜单是父菜单,则其不能有 component 属性。

根据以上规则可以看出,如果菜单 component 配置了aa/bb则我们需要在 views 目录下创建aa/bb.vue文件才能匹配当前组件路径

引入 Pinia

pinia 是一个很好用的状态管理器,它可以方便的管理全局的状态,首先安装pinia

npm i pinia

然后在main.ts中注册

import { createPinia } from "pinia";
const app = createApp(App);

const pinia = createPinia();
app.use(pinia).use(router).mount("#app");

新建store/index文件用于存放 pinia 管理的状态,同时这里我们定义了获取菜单的方法GenerateRoutes

import { defineStore } from "pinia";
import { getMenuList } from "@/http/menu/index";
import router from "@/router";
import { MenuVo } from "@/http/menu/types/menu.vo";
type StoreState = {
  isCollapse: boolean,
  menuList: MenuVo[],
};
export default defineStore("home", {
  state: (): StoreState => {
    return {
      isCollapsefalse,
      menuList: [],
    };
  },
  actions: {
    async GenerateRoutes() {
      const { data } = await getMenuList({});
      this.menuList = data;
      return data;
    },
  },
});

动态添加路由

在路由配置文件中router/index.ts,我们先定义好公共路由页面

import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
import home from "@/store";
import { nextTick } from "vue";
import { filterRoute } from "@/utils/filterRoute";
const router = createRouter({
  history: createWebHashHistory(),
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition;
    } else {
      return { top0 };
    }
  },
  routes: [
    {
      path"/",
      name"Layout",
      component() =>
        import(/* webpackChunkName: "Layout" */ "../layout/index.vue"),
    },
    {
      path"/:pathMatch(.*)*",
      component() => import("../views/404.vue"),
    },
    {
      path"/login",
      name"login",
      component() =>
        import(/* webpackChunkName: "login" */ "../views/login.vue"),
    },
  ],
});
export default router;

然后在路由守卫beforeEach中使用addRoute进行动态路由添加,同时定义一个白名单路由列表,这里暂时只有一个login页面,表示直接放行

import home from "@/store";
import { nextTick } from "vue";
import { filterRoute } from "@/utils/filterRoute";
...
const writeLists = ['login']
router.beforeEach(async (to, from, next) => {
  if (writeLists.includes(to.name)) {
    next();
    return;
  }
  await nextTick();
  const homeStore = home();
  if (homeStore.menuList.length) {
    next();
    return;
  }
  const data = await homeStore.GenerateRoutes();
  const routers = filterRoute(data);
  routers.forEach((route: RouteRecordRaw) => {
    router.addRoute("Layout", route);
  });

  next({ ...to, replacetrue });
});

Tips

  • 这里在获取 pinia 中数据时,先执行了await nextTick();是因为此时如果直接拿 pinia 中数据是拿不到的,因为 pinia 还没有初始化完毕
  • next({ ...to, replace: true })再次加载当前路由,即重新执行beforeEach,因为第一次进入 beforeEach 函数中的时候还没有加载动态路由,所以我们需要在添加完路由后重新加载当前路由以触发 beforeEach 钩子函数

路由跳转

我们模拟修改一下菜单表中子菜单 2.1 的 component 为test2/index

Vue + Element Plus 实现权限管理系统(四):动态添加路由
image.png

相应的我们则需要在 views 下新建test2/index.vue

<template>
    <div>test2</div>
</template>

<script lang='ts' setup>
</
script>

来到layout/components/sidebar.vue中处理菜单选择事件

Vue + Element Plus 实现权限管理系统(四):动态添加路由
image.png

其中 getPath 中d参数代表当前菜单的父菜单和子菜单 index 的数组,即父菜单的 path 和子菜单 path,如['aa','bb'],刚好根据它们进行相应的路由跳转

最后点击子菜单2.1就会发现这个页面被渲染出来了

Vue + Element Plus 实现权限管理系统(四):动态添加路由
image.png

写在最后

到这里动态路由添加基本完成了,当然可能还会有一些细枝末节需要处理,后面发现的话会进行完善。如果你想系统的了解一个权限管理系统如何搭建的话可以关注公众号查看菜单[实战项目]->[后台权限管理系统]查看完整教程,分为前端篇和后端篇,当然如果你只对前端感兴趣的话,关注前端篇即可

最后动动小手给个赞和在看吧👍


原文始发于微信公众号(web前端进阶):Vue + Element Plus 实现权限管理系统(四):动态添加路由

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/232216.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!