前端
vue
OKR中每一个页面的权限都是动态从后端配置的,能够在后台经过一个 tree 或者其它展示形式给每个页面动态配置权限,以后将这份路由表存储到后端。该项目中是使用tree实现路由控制。git
github
1 //返回的JSON格式 2 { 3 data:{ 4 ...//基本信息 5 permissions:[{ 6 permissionId:"页面权限id/系统id", 7 title:'HolliOne', 8 children:[{ 9 permissionId:'user', //页面及权限 10 title:'用户管理', 11 children:[{ 12 action: "resetPassword" //资源按钮级权限 13 title:'重置密码', 14 .... 15 },...] 16 },...] 17 }, 18 message:'请求成功', 19 success:true, 20 }
当用户登陆后获得 permissions,动态生成可访问页面vuex
1 // 前端路由表 2 const constantRouterComponents = { 3 // 基础页面 layout 必须引入 4 BasicLayout: BasicLayout, 5 BlankLayout: BlankLayout, 6 RouteView: RouteView, 7 PageView: PageView, 8 9 // 须要动态引入的页面组件 10 analysis: () => import('@/views/dashboard/Analysis'), 11 workplace: () => import('@/views/dashboard/Workplace'), 12 monitor: () => import('@/views/dashboard/Monitor') 13 // ...more 14 }
1 getInfo().then(response => { 2 const result = response.data 3 result.role = {} 4 if (result.permissions[0].children.length > 0) { 5 let temp = [] 6 const role = result.role 7 role.permissions = util.findActionArr(result.permissions[0].children,temp) 8 // const role = result.role 9 // role.permissions = result.permissions[0].children 10 role.permissions.map(per => { 11 if (per.children != null && per.children.length > 0) { 12 const action = per.children.map(action => { return action.action }) 13 per.actionList = action 14 } 15 }) 16 // role.permissionList = role.permissions.map(permission => { return permission.permissionId }) 17 let permissionList = []; 18 role.permissionList = util.findArr(result.permissions[0].children, permissionList, 'permissionId') 19 commit('SET_ROLES', result.role) 20 commit('SET_INFO', result) 21 } else { 22 reject(new Error('getInfo: roles must be a non-null array !')) 23 }commit('SET_AVATAR', result.avatar) 24 resolve(response) 25 }).catch(error => { 26 reject(error) 27 })
1 //动态生成菜单 2 router.beforeEach((to, from, next) => { 3 NProgress.start() // start progress bar 4 5 if (Vue.ls.get(ACCESS_TOKEN)) { 6 /* has token */ 7 if (to.path === '/user/login') { 8 next({ path: '/user/userList' }) 9 NProgress.done() 10 } else { 11 if (store.getters.roles.length === 0) { 12 store 13 .dispatch('GetInfo') 14 .then(res => { 15 const roles = res.data && res.data.role 16 store.dispatch('GenerateRoutes', { roles }).then(() => { 17 // 根据roles权限生成可访问的路由表 18 // 动态添加可访问路由表 19 router.addRoutes(store.getters.addRouters) 20 const redirect = decodeURIComponent(from.query.redirect || to.path) 21 if (to.path === redirect) { 22 // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record 23 next({ ...to, replace: true }) 24 } else { 25 // 跳转到目的路由 26 next({ path: '/'+ roles.permissionList[0] }) 27 } 28 }) 29 }) 30 .catch((err) => { 31 console.log(err); 32 notification.error({ 33 message: '错误', 34 description: '您暂时没有权限,请联系管理员开启权限' 35 }) 36 store.dispatch('Logout').then(() => { 37 next({ path: '/user/login', query: { redirect: to.fullPath } }) 38 }) 39 }) 40 } else { 41 next() 42 } 43 } 44 } else { 45 if (whiteList.includes(to.name)) { 46 // 在免登陆白名单,直接进入 47 next() 48 } else { 49 next({ path: '/user/login', query: { redirect: to.fullPath } }) 50 NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it 51 } 52 } 53 }) 54 55 //vuex store modules 56 /** 57 * 过滤帐户是否拥有某一个权限,并将菜单从加载列表移除 58 * 59 * @param permission 60 * @param route 61 * @returns {boolean} 62 */ 63 function hasPermission (permission, route) { 64 if (route.meta && route.meta.permission) { 65 let flag = false 66 for (let i = 0, len = permission.length; i < len; i++) { 67 flag = route.meta.permission.includes(permission[i]) 68 if (flag) { 69 return true 70 } 71 } 72 return false 73 } 74 return true 75 } 76 //格式化 后端 结构信息并递归生成层级路由表 77 function filterAsyncRouter (routerMap, roles) { 78 const accessedRouters = routerMap.filter(route => { 79 if (hasPermission(roles.permissionList, route)) { 80 if (route.children && route.children.length) { 81 route.children = filterAsyncRouter(route.children, roles) 82 } 83 return true 84 } 85 return false 86 }) 87 return accessedRouters 88 } 89 actions: { 90 GenerateRoutes ({ commit }, data) { 91 return new Promise(resolve => { 92 const { roles } = data 93 const accessedRouters = filterAsyncRouter(asyncRouterMap, roles) 94 commit('SET_ROUTERS', accessedRouters) 95 resolve() 96 }) 97 } 98 }
后端
1 <!-- eg: 当前页面为 user --> 2 3 <template> 4 <!-- 校验是否有 user 权限下的 add 操做权限 --> 5 <a-button v-action:add >添加用户</a-button> 6 7 <!-- 校验是否有 user 权限下的 del 操做权限 --> 8 <a-button v-action:del>删除用户</a-button> 9 10 <!-- 校验是否有 user 权限下的 edit 操做权限 --> 11 <a v-action:edit @click="edit(record)">修改</a> 12 </template>
须要注意的是,指令权限默认从 store 中获取当前已经登录的用户的角色和权限信息进行比对,因此也要对指令权限的获取和校验 Action 权限部分进行自定义。数组
cookie
1 <template> 2 <a-tabs> 3 <a-tab-pane v-if="$auth('user.add')" tab="Tab 1"> 4 some context.. 5 </a-tab-pane> 6 <a-tab-pane v-if="$auth('user.del')" tab="Tab 2"> 7 some context.. 8 </a-tab-pane> 9 <a-tab-pane v-if="$auth('user.edit')" tab="Tab 3"> 10 some context.. 11 </a-tab-pane> 12 </a-tabs> 13 </template>
数据结构
app
1 //接口返回的JSON格式数据 2 { 3 data:{ 4 btnSet:['按钮级权限集合(例如增删该查以及各类操做)'], 5 menuSet:['页面级权限集合,返回字段和前端路由配置一致的页面就有权限'], 6 soleSet:['角色集合,该用户是什么角色'], 7 user:{'登陆用户的基本信息'}, 8 }, 9 msg:'操做成功', 10 state:1, 11 success:true 12 }
前端登陆时调用userInfo
接口,将权限信息分开存入本地localStorage或者cookies/vuex-store,(就是便于拿到信息的地方)
第一步:过滤页面权限
1 /** 2 * 对一个对象或者数组进行深复制,保证不是以引用赋值 3 */ 4 cloneObj(obj) { 5 var str = obj.constructor === Array ? [] : {}; 6 var newobj = obj.constructor === Array ? [] : {}; 7 if (typeof obj !== 'object') { 8 return; 9 } else if (window.JSON) { 10 str = JSON.stringify(obj); // 首先将对象序列化 11 newobj = JSON.parse(str); // 而后将对象还原 12 } 13 return newobj; 14 }, 15 //过滤路由 16 updateMenulist (state) { 17 // 权限过滤 、 18 let permissions = Cookies.get('permissions'); 19 if (permissions === undefined) { 20 return; 21 } 22 debugger 23 let menuList = []; 24 appRouter.forEach((child, index) => { 25 // 深复制对象 26 var item = Utils.cloneObj(child); 27 let len = menuList.push(item); 28 // 父类是否存在 29 let parent = JSON.parse(permissions).filter(p => { 30 if (p === item.name) { 31 return p; 32 } 33 }); 34 if (parent.length > 0) { 35 let childrenArr = []; 36 childrenArr = item.children.filter(child => { 37 var p = JSON.parse(permissions); 38 for (var i = 0; i < p.length; ++i) { 39 // 放开测试页面 40 if (p[i] === child.name || child.name === 'hollsysTestPage') { 41 return child; 42 } 43 } 44 }); 45 item.children = childrenArr; 46 // if (childrenArr === undefined || childrenArr.length === 0) { 47 // menuList.splice(len - 1, 1); 48 // } 49 } else { 50 menuList.splice(len - 1, 1); 51 } 52 }); 53 let resut = []; 54 let pArr = JSON.parse(permissions); 55 /**循环遍历menuList*/ 56 if (menuList != null && menuList.length>0) { 57 for (var i=0;i<menuList.length;i++) { 58 var temp = menuList[i]; 59 let childarr = []; 60 childarr = temp.children;/**二级*/ 61 for (var j=0;j<childarr.length;j++) { 62 var tempj = childarr[j]; 63 /**遍历三级路由*/ 64 var sanArr = []; 65 sanArr = tempj.children; 66 if (sanArr !== undefined) { 67 68 for (var k=0;k<sanArr.length;k++) { 69 var tempK = sanArr[k]; 70 var flag = 0; 71 for (var q = 0; q < pArr.length; ++q) { 72 // 放开测试页面 73 if (pArr[q] === tempK.name) { 74 /**删除sanArr中这个权限*/ 75 flag=1; 76 } 77 } 78 if (flag == 0) { 79 sanArr.splice(k,1); 80 } 81 } 82 } 83 } 84 } 85 } 86 state.menuList = menuList; 87 // 上面routers加全部的appRouters报错 改成过滤链接时动态添加 88 // state.routers.push(...appRouter); 89 state.routers.push(...menuList); 90 }
页面路由生成,生成按钮及的权限
在每一个页面中经过if/v-if判断按钮是否显示
1 //此角色有什么操做按钮权限功能 2 permissionsBtnSet (name) { 3 var permissionsBtnSet = Cookies.get('permissionsBtnSet'); 4 var p = JSON.parse(permissionsBtnSet); 5 for (var i = 0; i < p.length; ++i) { 6 if (p[i] === name) { 7 return true; 8 } 9 } 10 return false; 11 }
哈哈哈哈,到此就结束了,能够生成菜单页面和操做按钮,本身记录一下。