基本思路:
1,创建vueRouter,用公共路由实例化
2,创建需要根据权限筛选的路由对象(在路由对象,添加必要的权限判断字段)
3,登录完成,由后端配合返回当前用户的权限集合
4,筛选出有权限的路由对象,利用vueRouter的addRoutes方法,生成完整路由
5,处理刷新页面导致vueRouter重新实例化导致路由对象不完善 (利用router.beforeEach导航守卫,,利用addRoutes()完善 路由对象 )
6,侧边导航栏相关代码
相关代码
根据上面的顺序
1,
Vue.use(Router)
// 公共路由
export const publicRoutes = [
{
path: '/',
name: 'login',
component: login,
meta: {
title: '登录'
}
}
]
// 需要根据权限筛选的路由
export const asyncRoutes = [
...Home,
...Seting,
...CRM,
{
path: '*',
component: login,
meta: {
hidden: true
},
redirect: '/'
}
]
const vr = new Router({
mode: 'history',
routes: publicRoutes
})
2
以seting模块为例,role为判断是的权限字段,角色有这个字段对应的值就有当前页面的权限
import Layout from '@/views/layout/layout.vue'
const account = r => require.ensure([], () => r(require('@/views/seting/account/account.vue')), 'seting');
const logs = r => require.ensure([], () => r(require('@/views/seting/logs/logs.vue')), 'seting');
const role = r => require.ensure([], () => r(require('@/views/seting/role/role.vue')), 'seting');
const Seting = [
{
path: '/seting',
component: Layout,
redirect: '/seting/account',
meta: {
title: '系统设置',
role: '123c6c6514d416472e640bc3f49297c550',
icon: 'icon-xitong'
},
children: [
{
path: 'account',
name: 'account',
component: account,
meta: {
title: '账号管理',
role: '1325cdeb897cc7f8e951d647de9b1d8e11',
}
},
{
path: 'logs',
name: 'logs',
component: logs,
meta: {
title: '日志管理',
role: '14bfbb0337ad3e7e2c9fc101294c3fe645',
}
},
{
path: 'role',
name: 'role',
component: role,
meta: {
title: '角色管理',
role: '1559d1c05d15a0dce5549b8bf5a58c0cf9',
}
}
]
}
]
export default Seting
如果有一些详情页不需要在导航列表展示还可以添加字段,在生成导航栏时去掉
eg:
{
path: 'addJoiner',
name: 'addJoiner',
component: addJoiner,
meta: {
hidden: true, // 隐藏字段
title: '***详情页',
role: '14bfbb0337ad3e7e2c9fc101294c3fe645',
}
},
3
登录获取权限集合和基本信息
// 登录
login () {
this.rememberPassword()
this.$refs.form.validate((valid) => {
if (valid) {
this.loading = true
let params = {
login_name: this.form.user,
password: this.form.password,
code: this.form.yanzhenma,
}
this.$api.post('api/user/login', params).then(res => {
if (res.err_code === 1) {
// 把用户的基本信息储存在vuex,中
this.$store.dispatch('setBaseInfo', res.data).then(() => {
// 获取有权限的路由表,添加到路由
router.addRoutes(this.$store.getters.addRouters)
this.$router.push({ name: 'home' })
})
}
this.loading = false
})
}
})
部分vuex代码
如果不太理解,点:vuex actions.js
// import api from '@/api'
const actions = {
setBaseInfo ({
commit
}, data) {
return new Promise(resolve => {
commit('set_userInfo', data.userInfo)
commit('set_token', data.token)
commit('set_roles', data.menus)
// 把基本信息保存在本地防止刷新之后丢失
sessionStorage.setItem('baseInfo', JSON.stringify(data))
resolve()
})
}
}
export default actions
mutations.js
const setStorage = (key, value) => {
if (typeof (value) === 'object') {
value = JSON.stringify(value)
}
sessionStorage.setItem(key, value)
}
/*
* 避免刷新之后vuex被重置,在sessionStorage做一个备份
*/
const mutations = {
set_userInfo (state, payload) {
state.userInfo = payload
setStorage('userInfo', payload)
},
set_token (state, payload) {
state.token = payload
setStorage('token', payload)
},
set_roles (state, payload) {
state.roles = payload
setStorage('roles', payload)
},
set_breadcrumb (state, payload) {
state.breadcrumb = payload
setStorage('breadcrumb', payload)/* */
},
changeCollapsed (state, payload) {
state.isCollapsed = payload
}
}
export default mutations
getters.js
import createdRoutes from '@/utils/createdRoutes.js'
import { asyncRoutes } from '@/router/index.js'
let getStoryage = (item) => {
let str = sessionStorage.getItem(item)
return JSON.parse(str)
}
const getters = {
get_userInfo: (state) => {
return state.userInfo ? state.userInfo : getStoryage('userInfo')
},
get_token: (state) => {
return state.token ? state.token : sessionStorage.getItem('token')
},
get_roles: (state) => {
return state.roles.length ? state.roles : getStoryage('roles')
},
addRouters: (state, getters) => {
let routes = createdRoutes(asyncRoutes, getters.get_roles)
return routes
},
get_breadcrumb: (state, getters) => {
return state.breadcrumb.length ? state.breadcrumb : getStoryage('getStoryage')
}
}
export default getters;
4,
核心的筛选需要权限的路由方法:createdRoutes()
也就是3的getters,用到的方法,毫无保留奉上
/**
* 判单当前的路由对象是否在登录人的权限之内
* @param {Array} roles 权限
* @param {Object} route 路由
*/
function hasPermission (roles, route) {
if (route.meta && route.meta.role) { // 路由需要权限就要在权限数组里面判断
return roles.includes(route.meta.role)
} else { // 不需要权限就直接通过
return true
}
}
/**
* 根据接口获取的权限列表动态生成当前用户的侧边导航栏,返回通过权限验证的路由数组
* @param {Array} asyncRoutes 需要过滤的路由
* @param {Array} roles 权限
*/
function createdRoutes (asyncRoutes, roles) {
const accessedRouters = asyncRoutes.filter(route => {
if (hasPermission(roles, route)) { // 当前路由通过权限验证直接通过
if (route.children && route.children.length) { // 当前路由有子路由,就递归验证
route.children = createdRoutes(route.children, roles)
}
return true
}
return false
})
return accessedRouters
}
export default createdRoutes
5,
处理刷新带来的问题
其实这里的代码是连接1的,注意注释
// 全局的导航守卫
vr.beforeEach((to, from, next) => {
// 刷新页面之后导致vue-router和vuex重置,路由丢失,利用的就是刷新后vuex的state被重置判断
if (to.name !== 'login' && !store.state.token) {
// 避免直接不登陆进页面
if (!sessionStorage.getItem('token')) {
location.href = '/'
return
}
let data = JSON.parse(sessionStorage.getItem('baseInfo'))
store.dispatch('setBaseInfo', data).then(() => {
vr.addRoutes(store.getters.addRouters)
})
}
// 设置面包屑导航
let breadcrumb = to.matched.filter(item => item.meta.title)
if (breadcrumb.length) {
breadcrumb = breadcrumb.map(item => item.meta.title)
store.commit('set_breadcrumb', breadcrumb)
}
// 设置title
document.title = to.meta.title
next()
})
6,侧边导航栏的完整代码,
还是遍历的根据权限生成的路由表,干掉一些需要隐藏的详情页之类,
这里的代码和过滤l路由的核心函数,要根据自己的业务做相应的处理
element-ui的menu
<template>
<el-menu class="el-menu-vertical-demo" :collapse="isCollapsed" background-color="#545c64" :default-active='activeIndex' text-color="#fff" active-text-color="#7EA8F5">
<section v-for="(item,index) in addRouters" :key="item.name" :class="isCollapsed ? 'collapsed':''">
<!-- 有子菜单 -->
<el-submenu :index=" `${index+1}`" v-if="!item.meta.hidden && item.children && item.children.length">
<template slot="title">
<i :class="`icon iconfont ${item.meta.icon}`"></i>
<span slot="title">{{item.meta.title}}</span>
</template>
<section v-for="(item2,index2) in item.children" :key="item2.name">
<!-- 二级菜单有子菜单 -->
<el-submenu :index="`${index+1}-${index2+1}`" v-if="item2.children && item2.children.length" class="sub2">
<template slot="title">
<span slot="title">{{item2.meta.title}}</span>
</template>
<!-- 三级菜单 -->
<el-menu-item v-for="(item3,index3) in item2.children" v-if="!item3.meta.hidden" :index="item3.name" :key="index3" @click.native="$router.push({name:item3.name})">
<span slot="title">{{item3.meta.title}}</span>
</el-menu-item>
</el-submenu>
<!-- 二级菜单无子菜单 -->
<!-- 不是隐藏的,详情页隐藏 -->
<el-menu-item :index="item2.name" v-else-if="!item2.meta.hidden" @click.native="$router.push({name:item2.name})">
<span slot="title">{{item2.meta.title}}</span>
</el-menu-item>
</section>
</el-submenu>
<!-- 无子菜单 -->
<el-menu-item v-else-if="item.meta.hidden && item.children && item.children.length" :index="item.children[0].name" @click.native="$router.push({name:item.children[0].name})" class="item">
<i :class="`iconfont ${item.children[0].meta.icon}`"></i>
<span slot="title">{{item.children[0].meta.title}}</span>
</el-menu-item>
</section>
</el-menu>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
props: {
isCollapsed: {
type: Boolean,
default: false
}
},
computed: {
...mapGetters(['addRouters']),
activeIndex () { //集火的菜单
return this.$route.name
}
}
}
</script>
<style lang="scss" scoped>
section {
/deep/ .el-submenu__title {
.icon {
margin-right: 10px;
}
i {
color: white;
font-size: 14px;
}
}
/deep/ .el-menu-item {
padding-left: 50px !important;
}
/deep/ .el-menu-item.item {
padding-left: 19px !important;
i {
color: white;
font-size: 14px;
margin-right: 12px;
}
}
/deep/ .el-submenu .el-menu-item {
min-width: 0;
}
/deep/ .el-submenu.sub2 .el-submenu__title {
padding-left: 50px !important;
i {
margin-right: 0px;
}
}
/* /deep/ .el-submenu.sub2 .el-menu-item {
text-indent: 12px;
} */
}
.collapsed {
width: 50px;
/deep/ .el-submenu__title {
.el-icon-arrow-right {
display: none;
}
span[slot="title"] {
display: none;
}
}
}
</style>
路由图