今天记录一下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.js和Vuex。
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 权限菜单(动态路由)的一个详细过程,有问题的小伙伴可以私信我,也可以评论留言,看到会回复