基于vue实现权限控制,动态渲染菜单栏

news2025/4/8 14:18:59

Vue+菜单权限+动态路由

实现原理:用户登录,服务端返回相关权限,进行持久化存储,筛选动态路由,同时菜单栏也需动态渲染

静态路由

静态路由,也叫常量路由,即所有角色都可以访问到的路由界面。如: login404

ts
复制代码
const constantRoute = [
  {
    //登录
    path: '/login',
    component: () => import('@/views/login/index.vue'),
    name: 'login',
    meta: {
      title: '登录', 
      hidden: true, 
      icon: 'Promotion', 
    },
  },

  {
    //登录成功以后的布局路由
    path: '/',
    component: () => import('@/layout/layout.vue'),
    name: 'layout',
    meta: {
      title: '',
      hidden: false,
      icon: '',
    },
    redirect: '/home',
    children: [
      {
        path: '/home',
        name: 'home',
        component: () => import('@/views/home/index.vue'),
        meta: {
          title: '首页',
          hidden: false,
          icon: 'House',
        },
      },
    ],
  },
  {
    //404
    path: '/404',
    component: () => import('@/views/404/index.vue'),
    name: '404',
    meta: {
      title: '404',
      hidden: true,
      icon: 'DocumentDelete',
    },
  },
 
]

对应的菜单权限如图:

在这里插入图片描述

动态路由

即不同角色所拥有的权限路由,一般登录成功后,向后端发送请求,由服务器返回对应的权限,然后进行筛选过滤。

ts
复制代码
//返回的用户信息
[
  {
    "userId": 1,
    "avatar": "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif",
    "username": "admin",
    "password": "111111",
    "desc": "平台管理员",
    "roles": ["平台管理员"],
    "buttons": ["cuser.detail"],
    "routes": [
      "Home",
      "User",
      "Role",
      "Permission",
      "Trademark",
      "Product",
      "Acl"
    ],
    "token": "Admin Token"
  },
]
//所有的权限路由
 const asyncRoute = [
        {
          path: '/acl',
          component: () => import('@/layout/index.vue'),
          name: 'Acl',
          meta: {
            title: '权限管理',
            icon: 'Lock',
          },
          redirect: '/acl/user',
          children: [
            {
              path: '/acl/user',
              component: () => import('@/views/acl/user/index.vue'),
              name: 'User',
              meta: {
                title: '用户管理',
                icon: 'User',
              },
            },
            {
              path: '/acl/role',
              component: () => import('@/views/acl/role/index.vue'),
              name: 'Role',
              meta: {
                title: '角色管理',
                icon: 'UserFilled',
              },
            },
            {
              path: '/acl/permission',
              component: () => import('@/views/acl/permission/index.vue'),
              name: 'Permission',
              meta: {
                title: '菜单管理',
                icon: 'Monitor',
              },
            },
          ],
        },
        {
          path: '/product',
          component: () => import('@/layout/index.vue'),
          name: 'Product',
          meta: {
            title: '商品管理',
            icon: 'Goods',
          },
          redirect: '/product/trademark',
          children: [
            {
              path: '/product/trademark',
              component: () => import('@/views/product/trademark/index.vue'),
              name: 'Trademark',
              meta: {
                title: '品牌管理',
                icon: 'ShoppingCartFull',
              },
            },
            {
              path: '/product/attr',
              component: () => import('@/views/product/attr/index.vue'),
              name: 'Attr',
              meta: {
                title: '属性管理',
                icon: 'ChromeFilled',
              },
            },
            {
              path: '/product/spu',
              component: () => import('@/views/product/spu/index.vue'),
              name: 'Spu',
              meta: {
                title: 'SPU管理',
                icon: 'Calendar',
              },
            },
            {
              path: '/product/sku',
              component: () => import('@/views/product/sku/index.vue'),
              name: 'Sku',
              meta: {
                title: 'SKU管理',
                icon: 'Orange',
              },
            },
          ],
        },
      ]

菜单权限

本次demo演示使用的是element-plus的el-menu组件。

在较为简单的开发中,菜单我们经常写死,这也就导致了不同的角色所看到的菜单列表是一致的。

所以,一般实现动态路由,也要二次封装一个对应的菜单权限组件

实现步骤

  • 通过pinia或者vuex全局状态管理工具,定义一个全局状态 menuRoutes ,初始值为对应的静态路由数组

在这里插入图片描述

  • 二次封装menu组件,通过 menuRoutes递归渲染展示不同的菜单栏

    重点:需要使用到vue3的递归组件,因此需要定义组件名。同时 menuRoutes 需要以父传子的方式传递

vue
复制代码
<template>
  <div>
    <template v-for="(item, index) in props.menuList" :key="item.path">
      <!-- 没有子路由 -->  
      <template v-if="!item.children">
        <el-menu-item
          :index="item.path"
          v-if="!item.meta.hidden"
          @click="goRoute"
        >
          <template #title>
            <el-icon>
              <component :is="item.meta.icon" />
            </el-icon>
            <span>{{ item.meta.title }}</span>
          </template>
        </el-menu-item>
      </template>
      <!-- 只有一个子路由 (例如home页,它是layout的子路由,但是只有一个,直接渲染home) -->
      <el-menu-item
        v-if="item.children && item.children.length == 1"
        :index="item.children[0].path"
        @click="goRoute"
      >
        <template #title>
          <el-icon>
            <component :is="item.children[0].meta.icon" />
          </el-icon>
          <span>{{ item.children[0].meta.title }}</span>
        </template>
      </el-menu-item>
      <!-- 有多个子路由 -->
      <el-sub-menu
        :index="item.path"
        v-if="item.children && item.children.length > 1"
      >
        <template #title>
          <el-icon>
            <component :is="item.meta.icon"></component>
          </el-icon>
          <span>{{ item.meta.title }}</span>
        </template>
         <!-- 子路由递归动态渲染 -->
        <Menu :menuList="item.children"></Menu>
      </el-sub-menu>
    </template>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, computed, onMounted, watch } from 'vue'
import { useRouter } from 'vue-router'
const $router = useRouter()
//获取父组件传递的路由数组
interface Iprops {
  menuList: any[]
}
const props = withDefaults(defineProps<Iprops>(), {
  menuList: () => [],
})

const goRoute = (vc: any) => {
  $router.push(vc.index)
}
</script>
<script lang="ts">
export default {
  name: 'Menu',
}
</script>
  • 登录成功后,获取用户信息,从而获取对应的权限列表数据,传入所有之前定义好的权限路由,进行过滤。最后通过addRoute方法追加动态路由。

    ts
    复制代码
    import { constantRoute, asyncRoute, anyRoute } from '@/router/routes'
    //getUserInfo
     const res = await getUserInfo()
     let routes = this.filterAsyncRoute(
          _.cloneDeep(asyncRoute),
          res.data.checkUser.routes,
          )
     //修改菜单栏显示
     this.menuRoutes = [...constantRoute, ...routes, anyRoute]
     //通过addRoute追加动态路由
       let activeRoutes = [...routes, anyRoute]
          activeRoutes.forEach((route) => {
            router.addRoute(route)
          })
     
     
     //过滤权限路由
     filterAsyncRoute(asyncRoute: RouteRecordRaw[], routes: RouteRecordName[]) {
          let result: RouteRecordRaw[] = []
          asyncRoute.forEach((item) => {
            if (routes.includes(item.name!)) {
              result.push(item)
              if (item.children) {
                item.children = this.filterAsyncRoute(item.children, routes)
              }
            }
          })
          return result
        },
      },
    

注意点:1、每次过滤权限路由的时候,必须深拷贝一份asyncRoute,懂的都懂(引用类型数据是地址)

2、pinia中的数据是非持久性缓存的,所以一刷新数据就会丢失。解决方案:使用pinia的持久性插件或者路由鉴权的同时,在路由前置导航守卫,每次跳转的时候,判断pinia中是否存储了用户信息,如果没有,重新调用getUserInfo方法,获取用户信息

3、是基于第二点,在组件外部通过同步语句获取仓库,是获取不到的,必须通过如下方式获取

javascript
复制代码
import pinia from '@/store/index'
let userStore = useUserStore(pinia)

4、至此,我们成功实现了菜单权限+动态路由,但还有个bug

BUG:如果我们在动态路由页面进行刷新,会导致白屏

原因:刷新页面的时候,触发了路由前置导航守卫,获取用户信息,如果获取到了,就放行。但是放行的时候,动态路由还没有加载完成! 得确保获取完用户信息且全部路由组件渲染完毕

解决办法:next({…to})

意义:死循环加载,直至路由组件加载完毕

学习更多Vue知识请关注CRMEB

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/792895.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

手把手移植 simpleFOC (二)

目录 前言 1、建立 arduino文件夹&#xff0c;如图&#xff1a; 2、提取必要的文件 二、修改源码 1.屏蔽arduino.h、wiring.h里代码 2.修改Print.cpp文件 三&#xff0c;编译 总结 一、前言 本章主要实现 simpleFoc 里的 Serial.print功能&#xff0c;建立setup、loop函…

Games101学习笔记 - 变换矩阵基础

二维空间下的变换 缩放矩阵 缩放变换: 假如一个点&#xff08;X,Y&#xff09;。x经过n倍缩放&#xff0c;y经过m倍缩放&#xff0c;得到的新点&#xff08;X1&#xff0c;Y1&#xff09;&#xff1b;那么新点和远点有如下关系&#xff0c;X1 n*X, Y1 m*Y写成矩阵就是如下…

Matlab----下载和安装教程

Matlab----下载 文件中有以下文件 Matlab----安装 步骤1&#xff1a;打开安装软件 步骤2&#xff1a;运行安装软件 在matlab 2018的文件夹下找到setup&#xff0c;选中右键以管理员身份运行。 步骤3 选择使用文件安装密钥&#xff0c;然后点击下一步。 步骤4 是否接收…

C语言---动态内存管理

C语言—动态内存管理 文章目录 C语言---动态内存管理1. 为什么要进行动态内存分配1.1 动态内存管理所在的区域 2. 动态内存函数的介绍2.1 malloc2.1.1 malloc语法2.1.2 malloc具体实例 2.2 free2.2.1 free语法2.2.2 free具体实例 2.3 calloc2.3.1 calloc语法2.3.2 calloc具体实…

基于Linux操作系统中的MySQL数据库SQL语句(三十一)

MySQL数据库SQL语句 目录 一、SQL语句类型 1、DDL 2、DML 3、DCL 4、DQL 二、数据库操作 1、查看 2、创建 2.1、默认字符集 2.2、指定字符集 3、进入 4、删除 5、更改 6、练习 三、数据表操作 &#xff08;一&#xff09;数据类型 1、数值类型 1.1、TINYINT …

【C++】总结3

文章目录 1.类的访问限定符2.封装3.类对象的存储方式4.为什么要进行内存对齐&#xff1f;结构体怎么对齐&#xff1f;5.如何让结构体按照指定的对齐参数进行对齐6.如何知道结构体中某个成员相对于结构体起始位置的偏移量7.C有哪几种构造函数8.类的六个默认成员函数9.构造函数10…

web-xss

HTML的< >&amp;&quot;©分别是<&#xff0c;>&#xff0c;&&#xff0c;"&#xff0c;©;的转义字符 XML只有5个转义符: < >&amp; &quot; &apos; HTTP请求中&#xff0c;Referer是header的一部分&#xff0c;当浏览器…

【防火墙】iptables防火墙(二)

1.写在命令行的备份和还原 2.把我们的规则配置在服务的文件当中&#xff0c;形成永久生效 iptables-save > /opt/ky30.bak iptables-restore < /opt/ky30.bak cat /etc/sysconfig/iptables 永久生效的配置文件 自定义链&#xff1a; 1.创建自定义链&#xff1a; i…

【数据结构】---时间复杂度与空间复杂度

时间复杂度与空间复杂度 1.&#x1f4c9; 时间复杂度&#x1f4cc;1.1 时间复杂度的概念1.2 大O的渐进表示法 &#x1f3f0;空间复杂度&#x1f4c3;例题分析1.案例&#xff08;常数阶&#xff09;2.案例&#xff08;线性阶&#xff09;3.案例&#xff1a;&#xff08;平方阶&a…

香港中文大学多媒体实验室——人工智能与计算机视觉的创新引擎

原创 | 文 BFT机器人 01 引言 香港中文大学多媒体实验室&#xff08;MultimediaLaboratory&#xff09;成立于2001年7月&#xff0c;是香港中文大学信息工程学系的重要组成部分。该实验室由汤晓鸥教授执导&#xff0c;是最早应用深度学习进行计算机视觉研究的华人团队之一。因…

Latex | 将MATLAB图并导入Latex中的方法

一、问题描述 用Latex时写paper时&#xff0c;要导入MATLAB生成的图进去 二、解决思路 &#xff08;1&#xff09;在MATLAB生成图片的窗口中&#xff0c;导出.eps矢量图 &#xff08;2&#xff09;把图上传到overleaf的目录 &#xff08;3&#xff09;在文中添加相应代码 三…

麒麟v10-coredns 启动失败

现象 在麒麟ARM芯片的机器上搭建k8s&#xff0c;其中的的一个组件cordons 发现启动失败&#xff0c;查看日志如下所示&#xff1a;No such device or address 问题分析 期初猜测kubelet与containerd的cgroupDriver驱动不一致导致。分别查看是一致的。没有问题。发现系统存在…

阿里云NVIDIA A100 GPU云服务器性能详解及租用费用

阿里云GPU服务器租用费用表包括包年包月、一个小时收费以及学生GPU服务器租用费用&#xff0c;阿里云GPU计算卡包括NVIDIA V100计算卡、T4计算卡、A10计算卡和A100计算卡&#xff0c;GPU云服务器gn6i可享受3折&#xff0c;阿里云百科分享阿里云GPU服务器租用表、GPU一个小时多少…

javafx拖拽图片实现

效果 代码 package cn.juhe.zjsb.test;import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.HBox; import javaf…

jmeter接口自动化测试工具在企业开展实际的操作

在企业使用jmeter开展实际的接口自动化测试工具&#xff0c;建议按如下操作流程&#xff0c; 可以使整个接口测试过程更规范&#xff0c;更有效。 接口自动化的流程&#xff1a; 1、获取到接口文档&#xff1a;swagger、word、excel ... 2、熟悉接口文档然后设计测试用例&am…

SAFe工具,SAFe规模化敏捷工具,SAFe实施流程,SAFe框架管理工具

​Leangoo领歌敏捷工具覆盖了敏捷项目研发全流程&#xff0c;包括小型团队敏捷开发&#xff0c;Scrum of Scrums大规模敏捷。 随着SAFe的越来越普及&#xff0c;Leangoo本次上线提供了完整的SAFe框架功能&#xff0c;包括&#xff1a;Program Backlog&#xff0c;PI规划&#…

Linux相关指令(上)

常见指令&#xff1a; 1 pwd&#xff1a;查看用户当前所在目录 以下面的路径为例&#xff1a; 2 ls&#xff1a;对于目录&#xff0c;该命令列出该目录下的所有子目录与文件。 对于文件&#xff0c;将列出文件名以及其他信息 ls-l&#xff08;or ll&#xff09;&#xff1a;列…

插槽slot复习

1.认识插槽 ◼ 在开发中&#xff0c;我们会经常封装一个个可复用的组件&#xff1a;  前面我们会通过props传递给组件一些数据&#xff0c;让组件来进行展示&#xff1b;  但是为了让这个组件具备更强的通用性&#xff0c;我们不能将组件中的内容限制为固定的div、span等等…

学生公寓报修管理系统的设计与实现(论文+源码)_kaic

摘 要 随着科技的发展&#xff0c;信息化的管理手段早以在人们生活的各个方面取代了传统的管理手段&#xff0c;以先进管理理念为基础的现代化信息管理系统已经成为了许多机构的必备工具。在如今大学的校园里&#xff0c;有着许许多多的信息化管理系统&#xff0c;如图书管理系…

面试官:Redis 为什么变慢了?怎么解决?

一、Redis为什么变慢了 二、Redis如何优化 三、Redis变慢了排查步骤 一、Redis为什么变慢了 1.Redis真的变慢了吗&#xff1f; 对 Redis 进行基准性能测试 例如&#xff0c;我的机器配置比较低&#xff0c;当延迟为 2ms 时&#xff0c;我就认为 Redis 变慢了&#xff0c;…