核心想法:
登陸后獲得用戶角色,通過角色獲得用戶的權限,注入權限對應的路由。刷新頁面,從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>