核心想法:
登陸后獲得用戶角色,通過角色獲得用戶的權限,注入權限對應的路由。刷新頁面,從localStorage用角色(更好的方式是通過token)再次獲得所屬權限,再次注入路由。在管理界面左端循環權限對應的路由菜單。
localStorage存用戶的信息(token),權限路由不會存。
所有的路由分為2種:
- 公共路由:所有用戶可以查看。
- 權限路由:當前用戶權限所屬的路由。
實現控制的方式分兩種:
- 通過 vue-router addRoutes 方法注入路由實現控制
- 通過 vue-router beforeEach 鈎子限制路由跳轉
一般來講,需要采用第一種方式,addRoutes注入的方式,第二種每次判斷,開銷比較大,而且一次性拿不到角色對應的所有路由列表。這個列表需要再登陸后立即渲染在左邊。

權限的控制分為好幾種:
- 一級菜單的權限控制,
- 二級菜單的權限控制,
- 按鈕級別的權限控制。
控制方式有2種:
- 后端返回路由控制列表
- 前后端約定路由控制列表
代碼:一級菜單的權限控制,前后端約定的方式確定角色能訪問的路由。
routers/index.js
import Vue from 'vue'
import Router from 'vue-router'
import loginRoute from './modules/login.js';
import homeRoute from './modules/home.js'
import productionRoute from './modules/production.js';
import store from '../stores/index.js';
Vue.use(Router)
//不需要權限的路由
export const constantRoutes = [loginRoute];
//需要權限的路由
//這里只是導出。實際上添加到router里在在store的permission里
export const asyncRoutes = [
homeRoute,
productionRoute,
];
console.log(asyncRoutes);
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
...constantRoutes,
]
})
router.beforeEach((to,from,next)=>{
let user = store.state.user.user;
let permissionRoutes= store.state.permission.permissionRoutes;
console.log(store.state);
console.log(user);
if(!user && to.path!=='/'){
next({
path:'/'
});
}else{
//刷新頁面的情況
if(!permissionRoutes && user){
store.dispatch('generateRoutes',{
role:user.role
})
console.log(router);
console.log(to);
//這里寫next沒用
next({...to})
}else{
next();
}
// next({...to, replace: true })
}
});
export default router;
其中,productionRoute:
import Layout from '../../layouts/Index.vue'
const productionAdd = () => import('../../pages/production/Add.vue')
const productionList = () => import('../../pages/production/List.vue')
const productionTemplateComplete = () => import('../../pages/production/TemplateComplete.vue')
let routes = {
path: '/production',
component: Layout,
meta: {
title: '產品管理',
roles: [0, 1]
},
children: [
{
path: 'add',
component: productionAdd,
beforeEnter (to, from, next) {
console.log('router beforeEnter');
next();
},
meta: {
title: '產品新增'
}
},
{
path: 'list',
component: productionList,
meta: {
title: '產品列表'
}
},
{
path: 'templateComplete',
component: productionTemplateComplete,
meta: {
title: '產品新增完善'
}
}
]
}
export default routes
stores/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user.js'
import permission from './modules/permission.js'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
user,
permission,
}
})
stores/modules/user.js
const SET_USER = 'SET_USER' //登錄成功
const CLEAR_USER = 'CLEAR_USER' //退出登錄
import $ajax from '../../axios/index.js';
import store from '../index.js';
export default {
state: {
user:JSON.parse(localStorage.getItem('user')) || null
},
mutations: {
[SET_USER](state, user) {
state.user = user;
localStorage.setItem('user', JSON.stringify(user))
},
[CLEAR_USER](state) {
state.user = null;
localStorage.removeItem('user');
}
},
actions: {
login({commit}, param) {
console.log('action');
console.log(param);
return new Promise((resolve, reject) => {
//一期不做后台登陸,前端判斷
// $ajax({
// url:api.login,
// data:{
// ...param
// }
// }).then(res=>{
// let user = res.data.data;
// commit(SET_USER, user);
// resolve();
// }).catch(res=>{
// reject();
// })
//前端驗證用戶名,密碼
window.setTimeout(()=>{
if(param.username === 'huainanju' && param.password === 'huainanju123'){
let user = {
username:'管理員',
role:0
}
commit(SET_USER, user);
store.dispatch('generateRoutes',{
role:user.role
})
resolve();
}else{
reject();
}
},1000)
});
},
logout({commit}) {
commit(CLEAR_USER)
}
}
}
stores/modules/permisson.js
import {asyncRoutes} from '../../routers/index';
import router from '../../routers/index';
/**
* 遞歸過濾異步路由表,返回符合用戶角色權限的路由表
* @param role
*/
function filterAsyncRoutes(role) {
const res = asyncRoutes.filter(route => {
return route.meta.roles.includes(role)
});
return res
}
const permisssion = {
state:{
permissionRoutes:null
},
getter:{
},
mutations:{
SET_ROUTERS: (state, routes) => {
//添加路由
console.log('添加路由');
router.addRoutes(routes);
state.permissionRoutes = routes // 權限路由
},
},
actions:{
generateRoutes({ commit }, data) {
let {role} = data;
let routes = filterAsyncRoutes(role);
console.log(routes);
commit('SET_ROUTERS', routes)
}
}
}
export default permisssion;
layouts/Left.vue
<template>
<el-aside style="min-height:100%;min-width:250px;" width="250px">
<el-menu :default-active="defaultActive"
background-color="#324157"
text-color="#bfcbd9"
active-text-color="#409EFF"
style="position:fixed;top:0;left:0;min-height: 100%;width:250px;z-index:100"
router>
<template v-for="(item,index) in permissionRoutes">
<!--一級菜單-->
<el-menu-item :index="item.path" v-if="item.children.length === 1 ">
<i class="el-icon-menu"></i>
<!-- <div>我是</div>-->
<span>{{item.meta.title}}</span>
</el-menu-item>
<!--多級菜單-->
<el-submenu :index="index+''" v-else>
<template slot="title">
<i class="el-icon-menu"></i>
<span>{{item.meta.title}}</span>
</template>
<template v-for="child in item.children">
<el-menu-item :index="item.path+'/'+child.path">{{child.meta.title}}</el-menu-item>
</template>
</el-submenu>
</template>
</el-menu>
</el-aside>
</template>
<script>
import {mapState} from 'vuex'
export default {
data() {
return {
}
},
computed: {
...mapState({
'permissionRoutes':state=>state.permission.permissionRoutes
}),
defaultActive: function () {
if (this.$route.path.indexOf('/classInfo') >= 0) {
return 'classList'
}
return this.$route.path.replace('/org/', '');
}
},
created() {
window.setTimeout(()=>{
console.log(this.permissionRoutes);
},1000)
}
}
</script>
