Vue 权限菜单(动态路由)详解

news2024/11/19 9:26:13

今天记录一下Vue权限菜单(动态路由),在我们写后台的时候用的比较多,Vue的权限菜单分两种,一种是通过本地进行,根据账号的权限进行筛选出可用的权限,组合菜单并在页面上渲染显示,另一种是根据登录的账号,后端直接回可用的权限菜单,前端进行整合渲染。第二种在日常中使用比第一种的情况多些,本篇文章讲述记录的也是第二种方式。我这里使用的是 element-ui的Admin 模板。

一、新建请求的js

我这里是新建了一个请求的js,你们可以直接将这个方法写在你们现在的请求js里面。
src目录下新建一个文件夹api,新建是一个名request的js文件。

request.js代码:

//这个request 是模板自带的,封装了axios的request,在utils目录下
import request from '@/utils/request'

export default {
	//...其他的请求
  // 获取权限菜单列表
  getRoleMenu(){
    return request({
      url: `项目请求url`,
      method: 'get',
    })
  },
   // ...其他的请求
  //
}

二、新建一个获取菜单的js

src目录下新建一个文件夹menu,在menu下新建一个menu.js

menu.js代码:

import Layout from '@/layout'//引入admin的layout布局
import router from '../router'//引入router
import request from '../api/request.js'//自定义封装的请求
import store from '../store'//Vuex

export default {
  // 获取路由菜单
  getMenu() {
    return new Promise((resolve, reject) => {
      return request.getRoleMenu().then(res => {
      	/*  回来的数据格式参考
			// [{
			//   type:1,//1表示有子路由,0则没有
			//   path: '路径',
			//   component: Layout,
			//   children: [{
			//     path: '',
			//     name: '名称',
			//     component: '页面路径',
			//     meta: {
			//       title: '页面标题',
			//       icon: '图标'
			//     },
			//		children: []//如果有子路由则需要写
			//   }]
			// }]
		*/
      	//声明一个空数组,用来装处理好的菜单信息
        const result = []
        // 获取到路由菜单,进行递归数据处理
        this.parseRoute(res.data, result, 1)
        // 添加菜单
        this.addMenu(result)
        //缓存用户菜单,我这里使用的是sessionStorage,用localStorage也可以
        sessionStorage.setItem("route", JSON.stringify(res.data))
        // 输出成功
        resolve()
      }).catch(err =>{
        reject()
      })
    })
  },
  // 对路由菜单递归数据处理
  parseRoute(fullList, resultList, step) {
    return new Promise((resolve, reject) => {
      let result = []
      // 路由类型申明,0表示没有二级路由,1表示有一级路由
      const typeArray = [0, 1]
      // 循环进行数据处理
      for (let i = 0; i < fullList.length; i++) {
        const itemElement = fullList[i]
        let routerObject = null
        const childrenList = []

        // 路由类型过滤,如果回来的路由类型不存在,则直接结束当前循环进行下一次循环
        if (!typeArray.includes(itemElement.type)) {
          continue
        }
		//对当前执行步骤进行判断,step 等于1,则属于主路由,反之则属于子路由
        if (step === 1) {
          routerObject = {
            path: itemElement['routePath'],
            component: Layout,
            children: [{
              path: itemElement['routePath'],
              name: itemElement['name'],
              component: resolve => require([`@/views${itemElement.componentsPath}`], resolve),
              meta: {
                title: itemElement['name'],
                icon: itemElement['icon']
              },
              children: childrenList
            }]
          }
        } else {
          routerObject = {
            path: itemElement['routePath'],
            name: itemElement['name'],
            component: resolve => require([`@/views${itemElement.componentsPath}`], resolve),
            meta: {
              title: itemElement['name'],
              icon: itemElement['icon']
            },
            children: childrenList
          }
        }

        const itemResult = []
        //对主路由尽心判断,满足条件则进行递归
        if (itemElement['nextMenuList'] != null && itemElement['nextMenuList'].length) {
          this.parseRoute(itemElement['nextMenuList'], itemResult, step + 1)
        }
		//将组装好的子路由信息插入数组
        for (let j = 0; j < itemResult.length; j++) {
          childrenList.push(itemResult[j])
        }
		//判断子路由数组长度
        if (childrenList.length === 0) {//不存在子路由
          if (step === 1) {//如果是主路由则删除第一个子路由,如果不删除则会出现一个空的父菜单
            delete routerObject.children[0].children
          } else {//如果是子路由,则删除子路由下的子路由,删除的这个子路由是空的,必须需要删除,否则也会出现一个空的菜单
            delete routerObject.children
          }
        } else {//存在子路由
          if (step === 1) {如果是主路由,则添加meta和子路由列表
            routerObject.meta = routerObject.children[0].meta
            routerObject.children = childrenList
            // console.log(routerObject)
          }
        }
		//插入组装好的数据
        resultList.push(routerObject)
        //插入最终完整的数据列表
        result.push(routerObject)
      }
      //输出已经组装好并且能用的数据
      resolve(result)
    })
  },
  // 添加菜单
  addMenu(data) {
    return new Promise((resolve, reject) => {
      // 在处理完的菜单列表数据后面插入404页面,404必须存在菜单列表的最后一项,否则会对所有页面进行拦截,并跳转404页面
      data.push({
        path: '*',
        redirect: '/404',
        hidden: true
      })
      // 打印菜单列表
      // console.log(data)
      // 将可用的路由权限列表存入Vuex
      store.dispatch('user/modifyMenu', data)
      // 添加菜单
      router.addRoutes(data)
      // 将路由元注入路由对象,必须添加
      router.options.routes.push(data)
      //输出成功
      resolve()
    })
  },
  //
}

三、修改Vuex

找到在store目录下的modules/user.js打开这个js文件,
引入刚刚创建的menu.js和请求的request.js

import request from "../../api/request.js"
import menu from "../../menu/menu.js"

state里添加一个menu,也就是getDefaultState()

const getDefaultState = () => {
  return {
    token: getToken(),
    name: '',
    avatar: '',
    menu: []//添加一个数组用来装菜单列表
  }
}

并在mutations里添加一个SET_MENU的方法用来修改state里的菜单列表

  SET_MENU: (state,menu) =>{
    state.menu = menu
  }

接下来就是修改actions里的login方法和添加一个方法用来修改mutations

	//1、修改login方法
	在login 方法的成功回调中添加一个这个方法,用来获取权限菜单
	menu.getMenu()
	//2、添加一个方法用来修改 mutations,这个方法是跟login方法同级的
	 modifyMenu({ commit },menu){
   		 return new Promise(resolve => {
    	  commit('SET_MENU',menu)
    	  resolve()
    	})
 	 }

修改login方法 如图:
在这里插入图片描述
再在logout那个方法里清除缓存,像下面这段代码这样

logout({ commit, state }) {
    return new Promise((resolve, reject) => {
      // 清除缓存的权限菜单
      sessionStorage.removeItem("route")
      //
      removeToken() // must remove  token  first
      // resetRouter()
      location.reload()
      commit('RESET_STATE')
    })
 }

现在已经能够拿到菜单信息并能够在侧边栏菜单显示了,控制台还有个报错,只是说一刷新页面就会没有,只能够通过地址栏跳转,下一步我们先来做路由/菜单的持久化。

四、修改 router,持久化处理

找到router目录,并打开index.js。引入获取权限菜单的menu.jsVuex

	import store from '../store'
	import menu from '../menu/menu.js'

constantRoutes 中只留下登录404首页这三个页面的router路径其他的全部不需要。类似于下面图中那样。
在这里插入图片描述
createRouter() 后面添加路由监听,以实现菜单的持久化。一定要放在const router = createRouter() 后面。
就像下图那样
在这里插入图片描述
监听路由的代码:

	//路由判断
router.beforeEach((to, from, next) => {
	// 判断是否有token
  if (store.state.user.token) {
    // 判断是否是跳转到登录页面
    if (to.path === '/login') next()
    else {//判断菜单列表
      if (!store.state.user.menu.length) { // 判断当前用户是否已拉取完权限菜单信息
          // 如果本地不存在权限菜单,则获取权限菜单,生成菜单列表
          if(!sessionStorage.getItem("route")){
        	//获取路由菜单
            menu.getMenu().then(response =>{
            	//保险起见,组装一次数据
           		menu.parseRoute(JSON.parse(sessionStorage.getItem("route")), [], 			1).then(res =>{
           			//添加路由并进行跳转
             		 menu.addMenu(res).then(e =>{
                		next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
              		})
           		})
            }).catch(err =>{//失败则直接跳转登录页面
              next({path: 'login'})
            })
          }else{//从缓存中读取用户权限列表,并添加菜单到侧边栏和路由元
            menu.parseRoute(JSON.parse(sessionStorage.getItem("route")), [], 1).then(res =>{
              menu.addMenu(res).then(e =>{
                next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
              })
            })
          }
      }
      else next({path: 'login'}) //当有权限菜单存在时,说明所有可访问路由已生成 如访问没权限的全面会自动进入登录页面
    }
  } else next({path: 'login'}); // 否则全部重定向到登录页
})

这样我们的菜单/路由持久化就🆗了,怎么刷新都不会丢失了,如果没有这个缓存了,还会自动的去获取并加载上去,然后完成页面的跳转。

五、处理问题/修改源码

打开我们的项目,找到并打开在src > layout > components > Sidebar 的index.vue。打开过后往下滑,在第34行左右的位置,把routes中的return那行改一下,侧边栏的菜单就可以正常显示了。如下代码:

	// 菜单
    routes() {
    	//修改的代码
      let menuRouter = this.$store.state.user.menu,
          routeMenu = [...this.$router.options.routes,...menuRouter]

      return routeMenu
      //源码中的代码
      // return this.$router.options.routes
    },

经过上面的步骤我们的权限菜单(动态路由)就已经完成了,接下来我们需要去修改一下 element-ui的菜单源码,因为跟我们写的对不上,控制台还有个报错。

打开我们的项目,找到并打开在src > layout > components > Sidebar 的SidebarItem.vue。打开过后往下滑,在第41行左右的位置,把item中的type那行注释掉,就可以解决控制台报错的那个小问题了。如下图:
在这里插入图片描述
我们的 权限菜单(动态路由)就已经搞定了。以上就是操作 Vue 权限菜单(动态路由)的一个详细过程,有问题的小伙伴可以私信我,也可以评论留言,看到会回复

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

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

相关文章

Vue3 从入门到放弃 (第一篇.环境准备)

什么是 Vue&#xff1f;# Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。无论是简单还是复杂的…

uniapp-路由uni-simple-router

背景 专为uniapp打造的路由器&#xff0c;和uniapp深度集成通配小程序、App和H5端H5能完全使用vue-router开发模块化、查询、通配符、路由参数使 uni-app实现嵌套路由&#xff08;仅H5端完全使用vue-router&#xff09;uniapp用到了很多vue的api&#xff0c;但在路由管理的功能…

vue 上传文件和下载文件

vue 上传文件和下载文件1. 上传文件2. 下载文件1. 上传文件 上传文件我所使用的组件是element ui 的 el-upload&#xff0c;我一共进行了如下两步&#xff0c;第一步&#xff1a;修改样式&#xff0c;因为el-upload的样式不是我所要的&#xff0c;我想要这种的 代码如下 <…

【Vue 项目】使用 vuedraggable 实现拖拽效果时遇到的问题及解决方案总结(允许 el-table 行拖拽、部分元素不允许拖拽、拖拽避免影响文字复制和输入框输入文字)

由于在自己的工作和学习过程中&#xff0c;只查看某个大佬的教程或文章无法满足自己的学习需求和解决遇到的问题&#xff0c;所以自己在追赶大佬们步伐的基础上&#xff0c;又自己总结、整理、汇总了一些资料&#xff0c;方便自己理解和后续回顾&#xff0c;同时也希望给大家带…

【Node.js】深度解析常用核心模块-path模块

✅ 作者简介&#xff1a;一名将要迈入大三的大学生&#xff0c;致力于提高前端开发能力 ✨ 个人主页&#xff1a;前端小白在前进的主页 &#x1f525; 系列专栏 &#xff1a; node.js学习专栏 ⭐️ 个人社区 : 个人交流社区 &#x1f340; 学习格言: ☀️ 打不倒你的会使你更强…

本地存储(Local Storage) 和 会话存储(Session Storage)

我不会告诉你任何定义和概念&#xff0c;上车&#xff0c;读完这篇博客&#xff0c;你就会对本地存储(Local Storage) 和 会话存储(Session Storage)有一个清晰的认识。 目录前提知识范例示例1&#xff1a;将键值对提供给本地存储示例2: 本地存储中设置键值对示例3: 获取空值示…

浏览器链接跳转进入小程序指定页面(适用安卓和iOS)

安卓和ios 浏览器外部链接跳转进入小程序 需求&#xff1a;用户点击链接进入小程序 第一步&#xff1a; 需要后端配合调用微信官方提供的接口方法 生成小程序的链接。 官方文档如下&#xff1a; https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/url-…

async与await异步编程

ECMA2017中新加入了两个关键字async与await 简单来说它们是基于promise之上的的语法糖&#xff0c;可以让异步操作更加地简单明了 首先我们需要用async关键字&#xff0c;将函数标记为异步函数 async function f() {} f()异步函数就是指&#xff1a;返回值为promise对象的函…

IDEA如何完美配置Servlet(适用于IDEA 2022及以下版本)

目录 准备Java文件 导入servlet-api.jar 配置Tomcat服务器 准备Java文件 1.首先新建Java项目 选择新建项目&#xff0c;构建系统选择Maven&#xff0c;点击创建 2.选择添加框架支持&#xff08;英文为Add Framework Support&#xff09; 3.选择添加web应用程序&#xff08;…

dom-to-image

前言 之前的文章&#xff1a;vue基于html2canvas和jspdf 生成pdf 、解决jspdf中文乱码问题 简单介绍了html2canvas的基本使用&#xff0c;html2canvas 只能截取可视区域&#xff0c;对于含有滚动条的无法完全截取。后来发现还有一个dom-to-image的库可以支持&#xff0c;并且能…

【C#+JavaScript+SQL Server】实现Web端考试系统 六:后台管理模块设计(附源码和资源)

需要源码和资源请点赞关注收藏后评论区留言私信~~~ 一、后台管理模块概述 在线考试系统中&#xff0c;后台管理员模块具有最高权限&#xff0c;管理员通过登录模块成功登录之后&#xff0c;可以对试题信息&#xff0c;教师信息&#xff0c;考生信息&#xff0c;考试科目信息以…

CSS渐变背景看这一篇就够了

CSS渐变背景看这一篇就够了 在我们自己设计网页的时候&#xff0c;为了好看美观&#xff0c;颜色可谓是最让人头疼的一部分。尤其是在配色上又找不到一些好看的网站。今天我就来记录一些好看的渐变式背景&#xff0c;和一些常用的颜色网站。 CSS 渐变使可以显示两种或多种指定…

Vue Admin Template关闭eslint校验,lintOnSave:false设置无效解决办法

目录 第一步&#xff1a;lintOnSave&#xff1a;false 第二步&#xff1a;修改package.json中的配置 最后一步&#xff1a; 使用Vue Admin Template 二次开发是一件非常愉悦的事情&#xff0c;可是它里面的eslint真的是十分恶心人啊&#xff0c;对此我找了很多方法都没有解决…

浏览器强缓存与协商缓存详解以及实践

"我这边把代码更新上服务器了&#xff0c;你那边看一下呢?" "我这边还有这个问题&#xff0c;你改没改哦?" "我改了啊&#xff0c;不信你看我代码......噢&#xff0c;可能是浏览器缓存问题&#xff0c; 你F5刷新一下试试&#xff0c;如果不行 就Ct…

超星章节内ppt课件下载

超星章节内课件下载 欢迎访问个人博客&#xff1a;www.xuanworld.top 前言 ​ 一般来说&#xff0c;如果老师设置超星学习通章节内的ppt加密&#xff0c;那么ppt是无法下载的&#xff0c;超星不会提供下载接口&#xff0c;但是我们可以通过网络抓包的方式来截取到pdf&#x…

vue-router控制台异常:Uncaught (in promise) Error: Redirected when going from “/“ to “/foo“

qian kun微前端在子应用的路由配置中添加了一个全局前置导航守beforeEach&#xff0c;在前置导航守卫中调用next方法时重写了路由的path&#xff0c;结果控制台每次在路由跳转时都会报异常&#xff0c;但是不影响功能。 这里&#xff0c;我们将这个场景从微前端摘出来&#xf…

uniapp登录拦截器(未登录点击其他地方跳转登录页)

项目场景&#xff1a; 例如&#xff1a;客户要求用户在未登录的情况下&#xff0c;用户只可以在底部导航栏操作&#xff0c;点击其他的的功能都要跳转到登录页面。 uni.addInterceptor拦截器 是uni-app官网提供的拦截器&#xff0c;可以利用它来实现登录拦截器&#xff0c;用户…

vue3+pinia+vuerouter4动态路由菜单

文章目录前言一、用户权限和菜单列表数据二、pinia存储数据状态共享1.创建存储用户详情的user.ts文件2.创建存储用户菜单和权限的menus.ts文件三、设置动态路由1.在router文件夹下面创建routers.ts文件2.设置前置路由守卫3.左侧导航菜单前言 最近在做一个通用后台管理系统的框架…

35.JavaScript对象和数组的解构赋值基础详解、let陷阱、函数参数解构

文章目录35.JavaScript对象和数组的解构赋值数组解构解构不改变原数组忽略数组元素可迭代对象使用解构赋值给任何变量与.entries()方法结合与Map结合变量交换多余的元素对象解构属性变量映射默认值多余的属性let陷阱多层解析函数参数解析总结35.JavaScript对象和数组的解构赋值…

HTML使用Element-UI制作管理系统页面(无需脚手架以及创建vue工程)

HTML正常使用Element-UI前言尝试经历设计附件前言 入职培训到Web前端后布置了一个制作管理系统前端页面的任务&#xff0c;任务要求包含&#xff1a; 1.页面的布局主要为左侧导航菜单栏&#xff0c;右侧为信息展示栏&#xff0c;要体现嵌套 2.点击菜单栏切换右侧信息展示时左侧…