需要用到的插件,vuex持久化插件(vuex-persistedstate),vuex,element-ui(需要用到el-menu,如果有其他的也可以使用)
适用场景:老项目新加角色路由权限需求时,初次接触权限路由菜单
不适用场景:新项目中涉及动态路由
流程:在路由配置中设置对应权限标识,和后台返回相同即可,在登录页面设置处理组装路由方式,处理好后存储在vuex中,在对应页面引入封装好的菜单组件,然后在vuex中拿到菜单列表渲染即可
1.首先在router中设置对应路由权限,提前配置好,只需要在menu中设置一个判断值
2.在登录中对路由(子路由)进行判断是否有权限渲染,全部判断完之后添加至vuex的状态中
3.在首页(包含但不限于)中进行嵌套菜单组件引入,在从vuex中取出菜单列表渲染即可
接下来是代码流程展示
1.设置对应权限标识(subMenuType)
{
path: '/home',
menu: true, // 菜单路由标识字段
component: () => import('@/views/home.vue'),
children: [
// 会议
{
path: '/schedule',
title: '会议',
label: '会议',
component: () => import('@/views/schedule/schedule.vue'),
meta: {
title: '会议',
icon: 'el-icon-s-custom',
menuPermission: 'schedule'
}
},
// 运维
{
path: '',
title: '运维',
label: '运维',
component: () => import('@/views/monitor/monitor.vue'),
meta: {
title: '运维',
icon: 'el-icon-s-custom',
menuPermission: 'monitor'
},
children: [
{
path: '/log',
title: '日志管理',
label: '日志管理',
component: () => import('@/views/monitor/systemSettings/LeftRightLayout.vue'),
meta: {
title: '日志管理',
// subMenuType 这个字段有值时,菜单数据组装时会忽略当前路由的子路由数据。
// 在此处,组装菜单时,“操作日志”子路由会被忽略
subMenuType: 'logger',
menuPermission: 'logger'
},
children: [
{
path: '/log/operation-log',
name: '操作日志',
component: () => import('@/views/monitor/LogManagement/OperationLog.vue'),
meta: {
title: '操作日志',
menuPermission: 'logger',
}
}
]
}
]
},
]
}
2.在登录页面进行权限路由拦截
submitForm(formName) {
// console.log(formName);
this.$refs[formName].validate((valid) => {
if (valid) {
console.log('正则校验之后进入判断')
this.PermissionsMenu(staticRouter.options.routes, ['monitor','logger'])
// console.log(this.PermissionsMenu);
} else {
console.log('正则校验失败')
return false
}
})
},
PermissionsMenu(staticRouter, permissions) {
const that = this;
const filterMenus = function (menus, accessMenu) {
// console.log(menus,);
menus.forEach((m) => {
// 主菜单组装
if (m.children && !m.meta.subMenuType) {
// 有子路由,且subMenuType没有值,说明该子路由属于子菜单
let subMenu = [];
// m.children说明当前路由还有子路由,继续递归
filterMenus(m.children, subMenu);
// 递归后,如果子路由组装后存在值,则将子路由的值合并到当前路由的children,并将当前路由push到最终accessMenu中
if (subMenu.length > 0) {
let _aMenu = Object.assign({}, m);
_aMenu.children = subMenu;
accessMenu.push(_aMenu)
}
} else {
// console.log(m,'过滤了菜单');
// 过滤子菜单(子路由不属于头部菜单时,需要进行过滤)
if (m.meta.subMenuType) {
m.children && delete m.children
}
// 递归遍历菜单,筛选有权限的菜单
console.log(m.meta.menuPermission);
permissions.includes(m.meta.menuPermission) && accessMenu.push(m)
}
})
};
//以上是复用函数
let menus = []; // 全部菜单路由
let accessMenu = []; // 根据权限过滤后的菜单路由
// 获取路由是菜单的部分
console.log(staticRouter,'打印静态路由');
staticRouter.forEach((item) => {
if (item.menu) {
// 菜单路由
menus = item.menu ? menus.concat(item.children) : menus
}
});
console.log('组装前--全部菜单路由', menus);
// 根据当前用户菜单权限来组装菜单列表
filterMenus(menus, accessMenu);
// 保存到vuex中
this.$store.commit('setUser', accessMenu)
// this.$store.state.user.accessMenu=accessMenu
this.$router.push('/home')
console.log(this.$store);
},
3.在对应页面引入菜单组件
<el-menu
:default-active="activeMenu"
class="el-menu-demo"
mode="horizontal"
:router="true"
>
<template v-for="menu in user.accessMenu">
<el-menu-item
class="sub-nav"
v-if="!menu.children"
:key="menu.title"
:index="menu.path"
>
<i :class="menu.icon" v-if="menu.icon"></i>
<span slot="title">{{menu.title }}</span>
</el-menu-item>
<the-submenu
:key="menu.title"
:subMenu="menu"
v-else
></the-submenu>
</template>
</el-menu>
<script>
import TheLayoutSubSidebar from '@/components/TheLayoutSubSidebar/TheLayoutSubSidebar.vue'
export default {
components: { 'theSubmenu': TheLayoutSubSidebar },
props: {},
data() {
return {
user:[],
}
},
computed: {
activeMenu() {
const route = this.$route
console.log(route);
const { meta, path } = route
if (meta.activeMenu) {
return meta.activeMenu
}
return path
},
},
mounted() {
this.user=this.$store.state.user
},
}
</script>
若是没有更好的菜单组件可以使用这个
<el-submenu v-on="listeners" :key="data.key" :index="data.key">
<template slot="title">
<i :class="data.attrs.subMenu.icon" v-if="data.attrs.subMenu.icon"></i>
<span slot="title">{{ data.attrs.subMenu.title }}</span>
</template>
<template v-for="item in data.attrs.subMenu.children">
<el-menu-item v-if="!item.children" :key="item.title" :index="item.path">
<a-icon :type="item.icon" v-if="item.icon" />
<span>{{ item.title }}</span>
</el-menu-item>
<the-submenu
v-else
:subMenu="item"
:key="item.title"
></the-submenu>
</template>
</el-submenu>
import TheLayoutSubSidebar from '@/components/TheLayoutSubSidebar/TheLayoutSubSidebar.vue'
export default {
components: { theSubmenu: TheLayoutSubSidebar },
props: {},
data() {
return {}
},
}
一定不要忘了引入哦!
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
// global css
import './assets/styles/index.scss'
Vue.use(ElementUI)
new Vue({
router,
store,
el: '#app',
render: function (h) { return h(App) }
}).$mount('#app')