前端
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 }
哈哈哈哈,到此就結束了,能夠生成菜單頁面和操做按鈕,本身記錄一下。