写这篇文章的目的是为了记录两天的成果。。
关于vue权限的参考是从花裤衩
大佬那得到的启发
1、首先声明,阅读此博客需要:
2、讲解一下本项目权限的实现:
权限:动态菜单栏,不同用户能看到的页面是不同的,所以,当用户登陆时,服务端判断该用户的权限,返回该用户的路由表–至于按钮级别的权限,可以用页面权限去实现,或者使用v-if命令实现【注意,前台不可以相信,权限还是要后台判断】
路由:vuex+localStorage实现【刷新重新拉取路由表】
3、实现思路+部分代码【后续代码全部贴出】
a.定义一个未登录可以查看的页面–路由守卫第一层判断
const whiteList = ['/login', '/solution/index',
'/productIntroduction/index',
'/seo/index', '/contactUs/index', '/farm/index', '/404']
// no redirect whitelist 没有重定向白名单
router.beforeEach((to, from, next) => {
//如果含有token值,也就是处于登陆状态
if (getToken()) {
//不允许跳出登陆状态【本系统下规定】
if (whiteList.indexOf(to.path) !== -1) {
next({ path: '/' });
NProgress.done();
}else {
.........................
}
b.因为后台返回的数据是没有经过处理的,所以前台不能直接使用,还需进行一步转换
转换函数如下:递归
function filterAsyncRouter(asyncRouterMap) { //遍历后台传来的路由字符串,转换为组件对象
const accessed = asyncRouterMap.filter(route => {
if (route.component) {
if (route.component === 'LayoutF') {//Layout组件特殊处理【本系统的布局组件】
route.component = LayoutF
} else {
route.component = _import(route.component) //自定的引入组件的方式
}
}
if (route.children && route.children.length) { //递归调用
route.children = filterAsyncRouter(route.children)
}
return true
});
return accessed
}
c.在写本项目时,笔者遇到将经过处理的路由表放入localStorage中,用的时候再取,结果一直遇到陷入死循环的问题,且问题还未解决,大佬路过请指点~~【错误源码见本文尾】
笔者更改方法为每次用户刷新向服务器从新拉取路由表,这样一来,动态修改权限同时也就实现了。
定义一个变量为true,拉取完信息更改其为false,用户刷新则又变为true,这样,我们又多了一个判断条件
//registerRouteFresh定义为true
if (registerRouteFresh) {
//判断vuex中是否含有异步路由表
if(store.getters.asyncRouterMap.length ===0){
//从服务器拉取路由表
store.dispatch('RouteFresh',getToken()).then((data)=>{
//返回空,则强制退出
if(data===''){
quit();
next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
}else{
//如果vuex中含有异步路由表,将异步路由表转换
GenerateRoutes(store.getters.asyncRouterMap);
//将经处理路由放入内存中【定义全局变量,给菜单栏组件使用】
global.antRouter = store.getters.addRouters;
//将处理过的路由表放入localStorage中
saveObjArr('routers', store.getters.addRouters);
//添加路由
router.addRoutes(store.getters.addRouters);
//变量更改为false
registerRouteFresh = false;
next({...to, replace: true});
}
}).catch(err=>{
console.log(err);
});
d.核心的权限判断方法到这里就基本已经实现了。我的逻辑大致如此,至于源码,贴到下面~
1、permission.js:
var getRouter; //用来获取后台拿到的路由
let registerRouteFresh = true;
function GenerateRoutes(async) {
let accessedRouters;
accessedRouters = filterAsyncRouter(async);
console.log(accessedRouters)
store.dispatch('AddRoutes', accessedRouters);
}
router.beforeEach((to, from, next) => {
NProgress.start();
if (getToken()) {
/*
* 更改方法判断值
* */
if (whiteList.indexOf(to.path) !== -1) {
next({ path: '/' });
NProgress.done();
}else {
if (registerRouteFresh) {
if(store.getters.asyncRouterMap.length ===0){
store.dispatch('RouteFresh',getToken()).then((data)=>{
if(data===''){
quit();
next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
}else{
GenerateRoutes(store.getters.asyncRouterMap);
//将路由放入内存中
global.antRouter = store.getters.addRouters;
saveObjArr('routers', store.getters.addRouters);
router.addRoutes(store.getters.addRouters);
registerRouteFresh = false;
next({...to, replace: true});
}
}).catch(err=>{
console.log(err);
});
}
GenerateRoutes(store.getters.asyncRouterMap);
//将路由放入内存中
global.antRouter = store.getters.addRouters;
saveObjArr('routers', store.getters.addRouters);
router.addRoutes(store.getters.addRouters);
registerRouteFresh = false;
next({...to, replace: true});
}else {
next();
// next({path:'',query:{redirect:to.path}});
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
next()
} else {
next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
// 如果当前页面为登录,则每次挂接后都不会触发,因此请手动处理它
}
}
});
router.afterEach(() => {
NProgress.done() // finish progress bar
});
function saveObjArr(name, data) { //localStorage 存储数组对象的方法
localStorage.setItem(name, JSON.stringify(data))
}
function getObjArr(name) { //localStorage 获取数组对象的方法
return JSON.parse(window.localStorage.getItem(name));
}
//菜单栏生成函数
function filterAsyncRouter(asyncRouterMap) { //遍历后台传来的路由字符串,转换为组件对象
const accessed = asyncRouterMap.filter(route => {
if (route.component) {
if (route.component === 'LayoutF') {//Layout组件特殊处理
route.component = LayoutF
} else {
route.component = _import(route.component)
}
}
if (route.children && route.children.length) {
route.children = filterAsyncRouter(route.children)
}
return true
});
return accessed
}
function quit(){
return new Promise(resolve => {
removeToken();
resolve(1);
});
}
2、vuex中代码涉及隐私,这里就不贴出了。
3、前端mock接口数据
//静态访问数据
var arr = [];
var data = {
"data": {
"router": [
{
"path": "",
"component": "LayoutF",
"redirect": "dashboard",
"children": [
{
"path": "dashboard",
"component": "dashboard/index",
"meta": {
"title": "首页",
"icon":"home"
}
}
]
},
{
"path":"/Companys",
"component": "LayoutF",
"redirect": "/Companys/companystosave",
"name":"Companys",
"meta":{
"title":"公司管理", "icon":"building"
},
"children":[
{
"path":"companystosave",
"name":"companystosave",
"component": "Jurisdiction/companyManagement/Addparentcompany",
"meta":{"title":"添加母公司","icon":"plus"}
},
{
"path":"findallCompany",
"name":"findallCompany",
"component": "Jurisdiction/companyManagement/Companylist",
"meta":{"title":"公司列表","icon":"list"}
},
{
"path":"managerform",
"name":"managerform",
"component": "Jurisdiction/companyManagement/RegisteredCompanyAdministrator",
"meta":{"title":"注册公司管理员","icon":"registered"}
},
{
"path":"managerlist",
"name":"managerlist",
"component":"Jurisdiction/companyManagement/Queryparentcompanyadministrator",
"meta":{"title":"查询母公司管理员","icon":"search"}
},
{
"path":"othermanagerlist",
"name":"othermanagerlist",
"component": "Jurisdiction/companyManagement/SearchSubsidiaryAdministrator",
"meta":{"title":"查询子公司管理员","icon":"search"}
},
{
"path":'findByCompanyName',
"name":"findByCompanyName",
"component": "Jurisdiction/companyManagement/Companyinformation",
"meta":{"title":"公司信息","icon":"info"}
},
{
"path":'addSonCompany',
"name":"addSonCompany",
"component": "Jurisdiction/companyManagement/Addparentcompany",
"meta":{"title":"添加子公司","icon":"plus"}
}
]
},
{
"path": "*",
"redirect": "/404",
"hidden": true
}
]
}
};
for (let i in data.data.router) {
arr.push(data.data.router[i]); //属性
//arr.push(obj[i]); //值
}
export default {
loginByUsername: config => {
console.log(config);
return arr;
},
fresh:config=>{
console.log(config);
return arr;
},
logout: () => 'success'
}
f.上面贴出的代码为我测试开发的代码,未经过优化。下面贴出我的错误代码【这样写,刷新路由解析不出,并且陷入死循环】
/*权限管理*/
router.beforeEach((to, from, next) => {
NProgress.start();
if(getToken()){
//vuex中含有token
console.log(store.getters.token);
console.log(getToken());
if(getToken() === store.getters.token && store.getters.token !== null){
console.log('vux中也含有token,并且相等');
saveObjArr('token',store.getters.token);
if(whiteList.indexOf(to.path) !== -1){
next({ path: '/' });
}else {
console.log(store.getters.status);
if(store.getters.status ==='0'){
console.log(store.getters.asyncRouterMap);
GenerateRoutes(store.getters.asyncRouterMap).then(()=>{
console.log(store.getters.addRouters);
//将路由放入内存中
console.log(store.getters.permission_routers);
global.antRouter = store.getters.addRouters;
saveObjArr('store.getters.addRouters',store.getters.addRouters);
router.addRoutes(store.getters.addRouters);
store.dispatch('LoginSetStatus', 1);
localStorage.setItem('status',store.getters.status);
next({ ...to, replace: true });
}).catch((err)=>{
console.log(err);
// store.dispatch('FedLogOut').then(() => {
// Message.error(err);
// console.log(err);
// next({ path: '/' });
// })
});
}else {
console.log('else 3 else');
/*可进行其它操作*/
next();
//next({ path: '/404', replace: true, query: { noGoBack: true }});
}
}
} else {
console.log('重定向');
global.antRouter = getObjArr('store.getters.addRouters');
router.addRoutes(global.antRouter);
store.dispatch('Refresh',getObjArr('token'));
store.dispatch('LoginSetStatus', 1);
store.dispatch('AddRoutes', global.antRouter);
next({ ...to, replace: true });
// next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
}
}else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
next()
} else {
next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
// 如果当前页面为登录,则每次挂接后都不会触发,因此请手动处理它
}
}
router.afterEach(() => {
NProgress.done() // finish progress bar
})
});
感谢阅读此博客~错误的地方还请留言指正,共勉。。。