From cf56a6c30fd2d9b8d0ce887f69895c474df818be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=89=E9=B9=8F?= Date: Thu, 30 Apr 2020 17:41:47 +0800 Subject: [PATCH] init role permission --- README-zh.md | 9 ++++- README.md | 6 ++- src/layout/components/Sidebar/index.vue | 6 +-- src/permission.js | 18 +++++++-- src/router/index.js | 8 +++- src/store/getters.js | 4 +- src/store/index.js | 2 + src/store/modules/permission.js | 69 +++++++++++++++++++++++++++++++++ src/store/modules/user.js | 14 ++++++- src/views/dashboard/index.vue | 4 +- 10 files changed, 124 insertions(+), 16 deletions(-) create mode 100644 src/store/modules/permission.js diff --git a/README-zh.md b/README-zh.md index 03c7735..77766d1 100644 --- a/README-zh.md +++ b/README-zh.md @@ -1,5 +1,4 @@ -如果你想要根据用户角色来动态生成侧边栏和 router,你可以使用该分支[permission-control](https://github.com/PanJiaChen/vue-admin-template/tree/permission-control) ## 相关项目 @@ -23,6 +22,7 @@ ```bash + # 进入项目目录 cd gulu-admin @@ -64,3 +64,10 @@ npm run lint npm run lint -- --fix ``` +更多信息请参考 [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/) + +## Demo + +![demo](https://github.com/PanJiaChen/PanJiaChen.github.io/blob/master/images/demo.gif) + + diff --git a/README.md b/README.md index 1e20c74..d5b508e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ + +## Build Setup + + ```bash # enter the project directory @@ -61,6 +65,4 @@ For `typescript` version, you can use [vue-typescript-admin-template](https://gi - [awesome-project](https://github.com/PanJiaChen/vue-element-admin/issues/2312) -## Browsers support -Modern browsers and Internet Explorer 10+. diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue index da39034..fb014a2 100644 --- a/src/layout/components/Sidebar/index.vue +++ b/src/layout/components/Sidebar/index.vue @@ -12,7 +12,7 @@ :collapse-transition="false" mode="vertical" > - + @@ -28,11 +28,9 @@ export default { components: { SidebarItem, Logo }, computed: { ...mapGetters([ + 'permission_routes', 'sidebar' ]), - routes() { - return this.$router.options.routes - }, activeMenu() { const route = this.$route const { meta, path } = route diff --git a/src/permission.js b/src/permission.js index fa1ea19..3d08d6b 100644 --- a/src/permission.js +++ b/src/permission.js @@ -26,15 +26,25 @@ router.beforeEach(async(to, from, next) => { next({ path: '/' }) NProgress.done() } else { - const hasGetUserInfo = store.getters.name - if (hasGetUserInfo) { + // determine whether the user has obtained his permission roles through getInfo + const hasRoles = store.getters.roles && store.getters.roles.length > 0 + if (hasRoles) { next() } else { try { // get user info - await store.dispatch('user/getInfo') + // note: roles must be a object array! such as: ['admin'] or ,['developer','editor'] + const { roles } = await store.dispatch('user/getInfo') - next() + // generate accessible routes map based on roles + const accessRoutes = await store.dispatch('permission/generateRoutes', roles) + + // dynamically add accessible routes + router.addRoutes(accessRoutes) + + // hack method to ensure that addRoutes is complete + // set the replace: true, so the navigation will not leave a history record + next({ ...to, replace: true }) } catch (error) { // remove token and go to login page to re-login await store.dispatch('user/resetToken') diff --git a/src/router/index.js b/src/router/index.js index f091f0f..b572aac 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -88,8 +88,14 @@ export const constantRoutes = [ meta: { title: 'Form', icon: 'form' } } ] - }, + } +] +/** + * asyncRoutes + * the routes that need to be dynamically loaded based on user roles + */ +export const asyncRoutes = [ { path: '/nested', component: Layout, diff --git a/src/store/getters.js b/src/store/getters.js index 5ab7b4c..a108508 100644 --- a/src/store/getters.js +++ b/src/store/getters.js @@ -3,6 +3,8 @@ const getters = { device: state => state.app.device, token: state => state.user.token, avatar: state => state.user.avatar, - name: state => state.user.name + name: state => state.user.name, + roles: state => state.user.roles, + permission_routes: state => state.permission.routes } export default getters diff --git a/src/store/index.js b/src/store/index.js index 6be466a..6ae5dad 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -2,6 +2,7 @@ import Vue from 'vue' import Vuex from 'vuex' import getters from './getters' import app from './modules/app' +import permission from './modules/permission' import settings from './modules/settings' import user from './modules/user' @@ -10,6 +11,7 @@ Vue.use(Vuex) const store = new Vuex.Store({ modules: { app, + permission, settings, user }, diff --git a/src/store/modules/permission.js b/src/store/modules/permission.js new file mode 100644 index 0000000..aeb5ee5 --- /dev/null +++ b/src/store/modules/permission.js @@ -0,0 +1,69 @@ +import { asyncRoutes, constantRoutes } from '@/router' + +/** + * Use meta.role to determine if the current user has permission + * @param roles + * @param route + */ +function hasPermission(roles, route) { + if (route.meta && route.meta.roles) { + return roles.some(role => route.meta.roles.includes(role)) + } else { + return true + } +} + +/** + * Filter asynchronous routing tables by recursion + * @param routes asyncRoutes + * @param roles + */ +export function filterAsyncRoutes(routes, roles) { + const res = [] + + routes.forEach(route => { + const tmp = { ...route } + if (hasPermission(roles, tmp)) { + if (tmp.children) { + tmp.children = filterAsyncRoutes(tmp.children, roles) + } + res.push(tmp) + } + }) + + return res +} + +const state = { + routes: [], + addRoutes: [] +} + +const mutations = { + SET_ROUTES: (state, routes) => { + state.addRoutes = routes + state.routes = constantRoutes.concat(routes) + } +} + +const actions = { + generateRoutes({ commit }, roles) { + return new Promise(resolve => { + let accessedRoutes + if (roles.includes('admin')) { + accessedRoutes = asyncRoutes || [] + } else { + accessedRoutes = filterAsyncRoutes(asyncRoutes, roles) + } + commit('SET_ROUTES', accessedRoutes) + resolve(accessedRoutes) + }) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} diff --git a/src/store/modules/user.js b/src/store/modules/user.js index 8d86b68..3c2a6de 100644 --- a/src/store/modules/user.js +++ b/src/store/modules/user.js @@ -6,7 +6,8 @@ const getDefaultState = () => { return { token: getToken(), name: '', - avatar: '' + avatar: '', + roles: [] } } @@ -24,6 +25,9 @@ const mutations = { }, SET_AVATAR: (state, avatar) => { state.avatar = avatar + }, + SET_ROLES: (state, roles) => { + state.roles = roles } } @@ -53,8 +57,14 @@ const actions = { reject('Verification failed, please Login again.') } - const { name, avatar } = data + const { roles, name, avatar } = data + + // roles must be a non-empty array + if (!roles || roles.length <= 0) { + reject('getInfo: roles must be a non-null array!') + } + commit('SET_ROLES', roles) commit('SET_NAME', name) commit('SET_AVATAR', avatar) resolve(data) diff --git a/src/views/dashboard/index.vue b/src/views/dashboard/index.vue index 33e5ab6..77ae59a 100644 --- a/src/views/dashboard/index.vue +++ b/src/views/dashboard/index.vue @@ -1,6 +1,7 @@ @@ -11,7 +12,8 @@ export default { name: 'Dashboard', computed: { ...mapGetters([ - 'name' + 'name', + 'roles' ]) } } -- 2.0.0