Commit cf56a6c30fd2d9b8d0ce887f69895c474df818be

Authored by 吉鹏
1 parent 80a28914e8
Exists in master

init role permission

1 1  
2   -如果你想要根据用户角色来动态生成侧边栏和 router,你可以使用该分支[permission-control](https://github.com/PanJiaChen/vue-admin-template/tree/permission-control)
3 2  
4 3 ## 相关项目
5 4  
... ... @@ -23,6 +22,7 @@
23 22  
24 23 ```bash
25 24  
  25 +
26 26 # 进入项目目录
27 27 cd gulu-admin
28 28  
... ... @@ -64,3 +64,10 @@ npm run lint
64 64 npm run lint -- --fix
65 65 ```
66 66  
  67 +更多信息请参考 [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/)
  68 +
  69 +## Demo
  70 +
  71 +![demo](https://github.com/PanJiaChen/PanJiaChen.github.io/blob/master/images/demo.gif)
  72 +
  73 +
... ...
1 1  
  2 +
  3 +## Build Setup
  4 +
  5 +
2 6 ```bash
3 7  
4 8 # enter the project directory
... ... @@ -61,6 +65,4 @@ For `typescript` version, you can use [vue-typescript-admin-template](https://gi
61 65  
62 66 - [awesome-project](https://github.com/PanJiaChen/vue-element-admin/issues/2312)
63 67  
64   -## Browsers support
65 68  
66   -Modern browsers and Internet Explorer 10+.
... ...
src/layout/components/Sidebar/index.vue
... ... @@ -12,7 +12,7 @@
12 12 :collapse-transition="false"
13 13 mode="vertical"
14 14 >
15   - <sidebar-item v-for="route in routes" :key="route.path" :item="route" :base-path="route.path" />
  15 + <sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
16 16 </el-menu>
17 17 </el-scrollbar>
18 18 </div>
... ... @@ -28,11 +28,9 @@ export default {
28 28 components: { SidebarItem, Logo },
29 29 computed: {
30 30 ...mapGetters([
  31 + 'permission_routes',
31 32 'sidebar'
32 33 ]),
33   - routes() {
34   - return this.$router.options.routes
35   - },
36 34 activeMenu() {
37 35 const route = this.$route
38 36 const { meta, path } = route
... ...
src/permission.js
... ... @@ -26,15 +26,25 @@ router.beforeEach(async(to, from, next) =&gt; {
26 26 next({ path: '/' })
27 27 NProgress.done()
28 28 } else {
29   - const hasGetUserInfo = store.getters.name
30   - if (hasGetUserInfo) {
  29 + // determine whether the user has obtained his permission roles through getInfo
  30 + const hasRoles = store.getters.roles && store.getters.roles.length > 0
  31 + if (hasRoles) {
31 32 next()
32 33 } else {
33 34 try {
34 35 // get user info
35   - await store.dispatch('user/getInfo')
  36 + // note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
  37 + const { roles } = await store.dispatch('user/getInfo')
36 38  
37   - next()
  39 + // generate accessible routes map based on roles
  40 + const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
  41 +
  42 + // dynamically add accessible routes
  43 + router.addRoutes(accessRoutes)
  44 +
  45 + // hack method to ensure that addRoutes is complete
  46 + // set the replace: true, so the navigation will not leave a history record
  47 + next({ ...to, replace: true })
38 48 } catch (error) {
39 49 // remove token and go to login page to re-login
40 50 await store.dispatch('user/resetToken')
... ...
src/router/index.js
... ... @@ -88,8 +88,14 @@ export const constantRoutes = [
88 88 meta: { title: 'Form', icon: 'form' }
89 89 }
90 90 ]
91   - },
  91 + }
  92 +]
92 93  
  94 +/**
  95 + * asyncRoutes
  96 + * the routes that need to be dynamically loaded based on user roles
  97 + */
  98 +export const asyncRoutes = [
93 99 {
94 100 path: '/nested',
95 101 component: Layout,
... ...
src/store/getters.js
... ... @@ -3,6 +3,8 @@ const getters = {
3 3 device: state => state.app.device,
4 4 token: state => state.user.token,
5 5 avatar: state => state.user.avatar,
6   - name: state => state.user.name
  6 + name: state => state.user.name,
  7 + roles: state => state.user.roles,
  8 + permission_routes: state => state.permission.routes
7 9 }
8 10 export default getters
... ...
src/store/index.js
... ... @@ -2,6 +2,7 @@ import Vue from &#39;vue&#39;
2 2 import Vuex from 'vuex'
3 3 import getters from './getters'
4 4 import app from './modules/app'
  5 +import permission from './modules/permission'
5 6 import settings from './modules/settings'
6 7 import user from './modules/user'
7 8  
... ... @@ -10,6 +11,7 @@ Vue.use(Vuex)
10 11 const store = new Vuex.Store({
11 12 modules: {
12 13 app,
  14 + permission,
13 15 settings,
14 16 user
15 17 },
... ...
src/store/modules/permission.js
... ... @@ -0,0 +1,69 @@
  1 +import { asyncRoutes, constantRoutes } from '@/router'
  2 +
  3 +/**
  4 + * Use meta.role to determine if the current user has permission
  5 + * @param roles
  6 + * @param route
  7 + */
  8 +function hasPermission(roles, route) {
  9 + if (route.meta && route.meta.roles) {
  10 + return roles.some(role => route.meta.roles.includes(role))
  11 + } else {
  12 + return true
  13 + }
  14 +}
  15 +
  16 +/**
  17 + * Filter asynchronous routing tables by recursion
  18 + * @param routes asyncRoutes
  19 + * @param roles
  20 + */
  21 +export function filterAsyncRoutes(routes, roles) {
  22 + const res = []
  23 +
  24 + routes.forEach(route => {
  25 + const tmp = { ...route }
  26 + if (hasPermission(roles, tmp)) {
  27 + if (tmp.children) {
  28 + tmp.children = filterAsyncRoutes(tmp.children, roles)
  29 + }
  30 + res.push(tmp)
  31 + }
  32 + })
  33 +
  34 + return res
  35 +}
  36 +
  37 +const state = {
  38 + routes: [],
  39 + addRoutes: []
  40 +}
  41 +
  42 +const mutations = {
  43 + SET_ROUTES: (state, routes) => {
  44 + state.addRoutes = routes
  45 + state.routes = constantRoutes.concat(routes)
  46 + }
  47 +}
  48 +
  49 +const actions = {
  50 + generateRoutes({ commit }, roles) {
  51 + return new Promise(resolve => {
  52 + let accessedRoutes
  53 + if (roles.includes('admin')) {
  54 + accessedRoutes = asyncRoutes || []
  55 + } else {
  56 + accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
  57 + }
  58 + commit('SET_ROUTES', accessedRoutes)
  59 + resolve(accessedRoutes)
  60 + })
  61 + }
  62 +}
  63 +
  64 +export default {
  65 + namespaced: true,
  66 + state,
  67 + mutations,
  68 + actions
  69 +}
... ...
src/store/modules/user.js
... ... @@ -6,7 +6,8 @@ const getDefaultState = () =&gt; {
6 6 return {
7 7 token: getToken(),
8 8 name: '',
9   - avatar: ''
  9 + avatar: '',
  10 + roles: []
10 11 }
11 12 }
12 13  
... ... @@ -24,6 +25,9 @@ const mutations = {
24 25 },
25 26 SET_AVATAR: (state, avatar) => {
26 27 state.avatar = avatar
  28 + },
  29 + SET_ROLES: (state, roles) => {
  30 + state.roles = roles
27 31 }
28 32 }
29 33  
... ... @@ -53,8 +57,14 @@ const actions = {
53 57 reject('Verification failed, please Login again.')
54 58 }
55 59  
56   - const { name, avatar } = data
  60 + const { roles, name, avatar } = data
  61 +
  62 + // roles must be a non-empty array
  63 + if (!roles || roles.length <= 0) {
  64 + reject('getInfo: roles must be a non-null array!')
  65 + }
57 66  
  67 + commit('SET_ROLES', roles)
58 68 commit('SET_NAME', name)
59 69 commit('SET_AVATAR', avatar)
60 70 resolve(data)
... ...
src/views/dashboard/index.vue
1 1 <template>
2 2 <div class="dashboard-container">
3 3 <div class="dashboard-text">name: {{ name }}</div>
  4 + <div class="dashboard-text">roles: <span v-for="role in roles" :key="role">{{ role }}</span></div>
4 5 </div>
5 6 </template>
6 7  
... ... @@ -11,7 +12,8 @@ export default {
11 12 name: 'Dashboard',
12 13 computed: {
13 14 ...mapGetters([
14   - 'name'
  15 + 'name',
  16 + 'roles'
15 17 ])
16 18 }
17 19 }
... ...