系统有多个角色身份,对应不同的角色身份展示对应不同的角色功能,也就对应不同的功能页面。一般登录后,由后端根据不同的角色登录返回不同的菜单目录以及不同的权限页面。前端根据后端返回信息,动态的生成菜单目录以及注册前端页面。
根据不同角色登录后台返回权限以及目录信息,动态的生成路由以及菜单目录。由于系统是按照不同的页面对应不同的功能权限,不同的页面配置对应不同的权限code。后端会在返回菜单的同时,把权限code列表一起返回。因此,设计思路如下
路由配置
该系统了路由分为两个部分,1.系统默认不需要权限配置的页面路由 2.需要由后端返回权限的页面路由。将本地默认不需要配置权限的页面路由先挂载注册。
import Vue from 'vue'
import Router from 'vue-router'
import { Layout } from "../layout"; // 页面整体布局
Vue.use(Router)
//动态引入页面
export const pageList = {
information: () => import("@/page/authorityManagement/informationManagement"), //个人信息管理
authorityRole: () =>
import("@/page/authorityManagement/rolePermission/index"), //角色权限管理
"subscriberData/index": () =>
import("@/page/authorityManagement/subscriberData/index"), //用户资料管理
changePassword: () => import("@/page/authorityManagement/changePassword"), //修改密码
};
//手动跳转的页面白名单
const whiteList = [
'/login'
];
//默认不需要权限的页面
export const constantRouterMap = [
{ path: '/login', name: 'login', component: () => import('@/page/login/index'), hidden: true },
{ path: '/404', component: () => import('@/page/errorPage/404'), hidden: true },
{ path: '/401', component: () => import('@/page/errorPage/401'), hidden: true },
{
path: '',
name: '首页',
component: Layout,
meta: {
title: '首页',
icon: 'el-icon-my-export'
},
noDropdown: true,
children: [
{
path: '',
name: '首页',
component: () => import('@/page/home/index'),
}
]
},
{
path: '/systemInforms',
name: '系统通知',
component: Layout,
meta: {
title: '系统通知',
},
hidden: true,
noDropdown: true,
children: [
{
path: 'systemInforms',
meta: {
title: '系统通知',
},
component: () => import('@/page/systemInforms/index'),
}
]
}
]
//注册路由
export default new Router({
mode: 'history', // 默认为'hash'模式
base: '/', // 添加跟目录,对应服务器部署子目录
routes: constantRouterMap
})
/**
* 路由设置要求:
* 1、该路由有子菜单,可以设置多层嵌套路由children;如果没有子菜单,不需要设置children;通过item.children.length来判断路由的级数;
* 2、登录成功后,定位到系统首页时,需要加载页面整体布局组件Layout并进行子路由定向加载;
*
* 按需加载路由组件的2种方法:
* 1、component: () => import('@/page/login')
* 2、component:resolve => require(['@/page/fundPosition'], resolve)
*
**/
vuex 配置
将后端拿到的路由列表,与本地动态引入的页面做匹配,在全局守卫中动态添加路由
import {constantRouterMap,pageList } from '@/router'
import { topRouterMap } from "@/router/topRouter";
import * as mutils from '@/utils/mUtils'
import { Layout } from "@/layout"; // 页面整体布局
//将路由列表与本地动态注册的路由匹配。动态挂载页面component
function updataAsyncRouterMap(){
let memu = JSON.parse(window.localStorage.getItem("menus"))
if(memu && memu.length){
// console.log(memu)
memu = memu.filter((item,index)=>{
return index >0
})
// console.log(memu)
memu.forEach((item) => {
item.path = "/" + item.path;
item.code = ""; //页面返回菜单内容项,全部都可以跳转
item.component = Layout;
if (item.children && item.children.length) {
item.children.forEach((citem) => {
citem.code = "";
citem.component = pageList[citem.path];
});
}
});
return memu
}
}
const permission = {
state: {
routers: constantRouterMap,
addRouters: [],
changePermission:true,//是否修改路由
},
getters:{
permission_routers: state => state.routers, // 所有路由
addRouters: state => state.addRouters, // 权限过滤路由
changePermission:state => state.changePermission,//是否修改权限
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers // 权限路由
state.changePermission = false //是否修改路由
state.routers = constantRouterMap.concat(routers) // 总路由
},
changePermission:(state,data) =>{
state.changePermission = data //是否修改路由
}
},
actions: {
// 根据角色,重新设置权限路由;并保存到vuex中,SET_ROUTERS;
GenerateRoutes({ commit }, data) {
return new Promise(resolve => {
let roles = data.roles;
let accessedRouters = '';
accessedRouters = updataAsyncRouterMap();//直接根据后台返回的菜单修改路由
commit('SET_ROUTERS', accessedRouters)
resolve()
})
},
changePermission({ commit },data) {
commit('changePermission',data)
}
}
}
export default permission
登录获取路由列表
登录成功之后,获取的路由菜单信息分为两个部分如下图所示。权限数组以及页面数组(权限数组是所有页面权限code 的集合)动态创建路由只需要将页面集合匹配对应的component,动态添加到路由表中即可。
this.$axios.user
.getUserMenuList()
.then((res) => {
// console.log(res);
let leftMemus = res.data.menus;
//将页面目录存储在本地,防止刷新vuex重载为空
window.localStorage.setItem("menus", JSON.stringify(leftMemus));
//修改vuex中,页面存储的目录
this.$store.dispatch("updateMemuList", leftMemus);
this.$router.push({ path: "/" });
})
.catch(() => {});
},
全局守卫中,动态添加路由
//拼接所有路由表
function concatRouterList(routerList) {
let array = ["/"];
routerList.forEach((item) => {
// console.log(item)
if (item.children && item.children.length) {
item.children.forEach((citem) => {
array.push(item.path + "/" + citem.path);
});
} else {
array.push(item.path);
}
});
return array;
}
const whiteList = ["/login"]; // 不重定向白名单
let routerList = concatRouterList(store.state.permission.routers); //所有可以跳转的路由表
router.beforeEach((to, from, next) => {
//点击登录时,拿到了token并存入了cookie,保证页面刷新时,始终可以拿到token
if (getToken("Token")) {
if (to.path === "/login") {
next({ path: "/" });
} else {
// 用户登录成功之后,每次点击路由都进行了角色的判断;
//根据vuex中,是否为首次登录,需要动态添加路由
if (store.getters.changePermission) {
let roles = JSON.parse(getToken("roles"));
store.commit("SET_ROLES", roles);
store.commit("SET_NAME", "测试");
store.commit("SET_NAME", "测试");
store.commit("SET_AVATAR", "");
store.dispatch("GenerateRoutes", { roles: roles }).then(() => {
router.addRoutes(store.getters.addRouters); // 动态添加可访问权限路由表
next({ ...to, replace: true }); // hack方法 确保addRoutes已完成
});
} else {
// 不是第一次登录,判断路由页面是否可以进
// 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
if (hasPermission(store.getters.roles, to.meta.roles)) {
//所有有权限的页面集合
routerList = concatRouterList(store.state.permission.routers);
if (routerList.indexOf(to.path) !== -1) {
next();
} else {
next({ path: "/401", replace: true, query: { noGoBack: true } });
}
} else {
next({ path: "/401", replace: true, query: { noGoBack: true } });
}
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
// console.log("退出了")
// 点击退出时,会定位到这里
next();
} else {
next("/login");
NProgress.done();
}
}
});