feature[Permission]: add role permission management page (#1605)
This commit is contained in:
		
							
								
								
									
										38
									
								
								src/api/role.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/api/role.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
import request from '@/utils/request'
 | 
			
		||||
 | 
			
		||||
export function getRoutes() {
 | 
			
		||||
  return request({
 | 
			
		||||
    url: '/routes',
 | 
			
		||||
    method: 'get'
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getRoles() {
 | 
			
		||||
  return request({
 | 
			
		||||
    url: '/roles',
 | 
			
		||||
    method: 'get'
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function addRole(data) {
 | 
			
		||||
  return request({
 | 
			
		||||
    url: '/roles/add',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    data
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function updateRole(id, data) {
 | 
			
		||||
  return request({
 | 
			
		||||
    url: `/roles/update/${id}`,
 | 
			
		||||
    method: 'put',
 | 
			
		||||
    data
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function deleteRole(id) {
 | 
			
		||||
  return request({
 | 
			
		||||
    url: `/roles/delete/${id}`,
 | 
			
		||||
    method: 'delete'
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
@@ -36,8 +36,8 @@ export default {
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    routers() {
 | 
			
		||||
      return this.$store.getters.permission_routers
 | 
			
		||||
    routes() {
 | 
			
		||||
      return this.$store.getters.permission_routes
 | 
			
		||||
    },
 | 
			
		||||
    lang() {
 | 
			
		||||
      return this.$store.getters.language
 | 
			
		||||
@@ -45,10 +45,10 @@ export default {
 | 
			
		||||
  },
 | 
			
		||||
  watch: {
 | 
			
		||||
    lang() {
 | 
			
		||||
      this.searchPool = this.generateRouters(this.routers)
 | 
			
		||||
      this.searchPool = this.generateRoutes(this.routes)
 | 
			
		||||
    },
 | 
			
		||||
    routers() {
 | 
			
		||||
      this.searchPool = this.generateRouters(this.routers)
 | 
			
		||||
    routes() {
 | 
			
		||||
      this.searchPool = this.generateRoutes(this.routes)
 | 
			
		||||
    },
 | 
			
		||||
    searchPool(list) {
 | 
			
		||||
      this.initFuse(list)
 | 
			
		||||
@@ -62,7 +62,7 @@ export default {
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    this.searchPool = this.generateRouters(this.routers)
 | 
			
		||||
    this.searchPool = this.generateRoutes(this.routes)
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    click() {
 | 
			
		||||
@@ -103,10 +103,10 @@ export default {
 | 
			
		||||
    },
 | 
			
		||||
    // Filter out the routes that can be displayed in the sidebar
 | 
			
		||||
    // And generate the internationalized title
 | 
			
		||||
    generateRouters(routers, basePath = '/', prefixTitle = []) {
 | 
			
		||||
    generateRoutes(routes, basePath = '/', prefixTitle = []) {
 | 
			
		||||
      let res = []
 | 
			
		||||
 | 
			
		||||
      for (const router of routers) {
 | 
			
		||||
      for (const router of routes) {
 | 
			
		||||
        // skip hidden router
 | 
			
		||||
        if (router.hidden) { continue }
 | 
			
		||||
 | 
			
		||||
@@ -128,11 +128,11 @@ export default {
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // recursive child routers
 | 
			
		||||
        // recursive child routes
 | 
			
		||||
        if (router.children) {
 | 
			
		||||
          const tempRouters = this.generateRouters(router.children, data.path, data.title)
 | 
			
		||||
          if (tempRouters.length >= 1) {
 | 
			
		||||
            res = [...res, ...tempRouters]
 | 
			
		||||
          const tempRoutes = this.generateRoutes(router.children, data.path, data.title)
 | 
			
		||||
          if (tempRoutes.length >= 1) {
 | 
			
		||||
            res = [...res, ...tempRoutes]
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ export default {
 | 
			
		||||
    guide: 'Guide',
 | 
			
		||||
    permission: 'Permission',
 | 
			
		||||
    pagePermission: 'Page Permission',
 | 
			
		||||
    rolePermission: 'Role Permission',
 | 
			
		||||
    directivePermission: 'Directive Permission',
 | 
			
		||||
    icons: 'Icons',
 | 
			
		||||
    components: 'Components',
 | 
			
		||||
@@ -86,9 +87,14 @@ export default {
 | 
			
		||||
    github: 'Github Repository'
 | 
			
		||||
  },
 | 
			
		||||
  permission: {
 | 
			
		||||
    addRole: 'New Role',
 | 
			
		||||
    editPermission: 'Edit Permission',
 | 
			
		||||
    roles: 'Your roles',
 | 
			
		||||
    switchRoles: 'Switch roles',
 | 
			
		||||
    tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.'
 | 
			
		||||
    tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.',
 | 
			
		||||
    delete: 'Delete',
 | 
			
		||||
    confirm: 'Confirm',
 | 
			
		||||
    cancel: 'Cancel'
 | 
			
		||||
  },
 | 
			
		||||
  guide: {
 | 
			
		||||
    description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ',
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ export default {
 | 
			
		||||
    documentation: 'Documentación',
 | 
			
		||||
    guide: 'Guía',
 | 
			
		||||
    permission: 'Permisos',
 | 
			
		||||
    rolePermission: 'Permisos de rol',
 | 
			
		||||
    pagePermission: 'Permisos de la página',
 | 
			
		||||
    directivePermission: 'Permisos de la directiva',
 | 
			
		||||
    icons: 'Iconos',
 | 
			
		||||
@@ -86,9 +87,14 @@ export default {
 | 
			
		||||
    github: 'Repositorio Github'
 | 
			
		||||
  },
 | 
			
		||||
  permission: {
 | 
			
		||||
    addRole: 'Nuevo rol',
 | 
			
		||||
    editPermission: 'Permiso de edición',
 | 
			
		||||
    roles: 'Tus permisos',
 | 
			
		||||
    switchRoles: 'Cambiar permisos',
 | 
			
		||||
    tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.'
 | 
			
		||||
    tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.',
 | 
			
		||||
    delete: 'Borrar',
 | 
			
		||||
    confirm: 'Confirmar',
 | 
			
		||||
    cancel: 'Cancelar'
 | 
			
		||||
  },
 | 
			
		||||
  guide: {
 | 
			
		||||
    description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ',
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ export default {
 | 
			
		||||
    documentation: '文档',
 | 
			
		||||
    guide: '引导页',
 | 
			
		||||
    permission: '权限测试页',
 | 
			
		||||
    rolePermission: '角色权限',
 | 
			
		||||
    pagePermission: '页面权限',
 | 
			
		||||
    directivePermission: '指令权限',
 | 
			
		||||
    icons: '图标',
 | 
			
		||||
@@ -86,9 +87,14 @@ export default {
 | 
			
		||||
    github: 'Github 地址'
 | 
			
		||||
  },
 | 
			
		||||
  permission: {
 | 
			
		||||
    addRole: '新增角色',
 | 
			
		||||
    editPermission: '编辑权限',
 | 
			
		||||
    roles: '你的权限',
 | 
			
		||||
    switchRoles: '切换权限',
 | 
			
		||||
    tips: '在某些情况下,不适合使用 v-permission。例如:Element-UI 的 Tab 组件或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。'
 | 
			
		||||
    tips: '在某些情况下,不适合使用 v-permission。例如:Element-UI 的 Tab 组件或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。',
 | 
			
		||||
    delete: '删除',
 | 
			
		||||
    confirm: '确定',
 | 
			
		||||
    cancel: '取消'
 | 
			
		||||
  },
 | 
			
		||||
  guide: {
 | 
			
		||||
    description: '引导页对于一些第一次进入项目的人很有用,你可以简单介绍下项目的功能。本 Demo 是基于',
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
      :collapse-transition="false"
 | 
			
		||||
      mode="vertical"
 | 
			
		||||
    >
 | 
			
		||||
      <sidebar-item v-for="route in permission_routers" :key="route.path" :item="route" :base-path="route.path" />
 | 
			
		||||
      <sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
 | 
			
		||||
    </el-menu>
 | 
			
		||||
  </el-scrollbar>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -23,7 +23,7 @@ export default {
 | 
			
		||||
  components: { SidebarItem },
 | 
			
		||||
  computed: {
 | 
			
		||||
    ...mapGetters([
 | 
			
		||||
      'permission_routers',
 | 
			
		||||
      'permission_routes',
 | 
			
		||||
      'sidebar'
 | 
			
		||||
    ]),
 | 
			
		||||
    variables() {
 | 
			
		||||
 
 | 
			
		||||
@@ -54,8 +54,8 @@ export default {
 | 
			
		||||
    visitedViews() {
 | 
			
		||||
      return this.$store.state.tagsView.visitedViews
 | 
			
		||||
    },
 | 
			
		||||
    routers() {
 | 
			
		||||
      return this.$store.state.permission.routers
 | 
			
		||||
    routes() {
 | 
			
		||||
      return this.$store.state.permission.routes
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  watch: {
 | 
			
		||||
@@ -102,7 +102,7 @@ export default {
 | 
			
		||||
      return tags
 | 
			
		||||
    },
 | 
			
		||||
    initTags() {
 | 
			
		||||
      const affixTags = this.affixTags = this.filterAffixTags(this.routers)
 | 
			
		||||
      const affixTags = this.affixTags = this.filterAffixTags(this.routes)
 | 
			
		||||
      for (const tag of affixTags) {
 | 
			
		||||
        // Must have tag name
 | 
			
		||||
        if (tag.name) {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,41 +2,49 @@ import router from './router'
 | 
			
		||||
import store from './store'
 | 
			
		||||
import { Message } from 'element-ui'
 | 
			
		||||
import NProgress from 'nprogress' // progress bar
 | 
			
		||||
import 'nprogress/nprogress.css'// progress bar style
 | 
			
		||||
import { getToken } from '@/utils/auth' // getToken from cookie
 | 
			
		||||
import 'nprogress/nprogress.css' // progress bar style
 | 
			
		||||
import { getToken } from '@/utils/auth' // get token from cookie
 | 
			
		||||
 | 
			
		||||
NProgress.configure({ showSpinner: false })// NProgress Configuration
 | 
			
		||||
NProgress.configure({ showSpinner: false }) // NProgress Configuration
 | 
			
		||||
 | 
			
		||||
// permission judge function
 | 
			
		||||
function hasPermission(roles, permissionRoles) {
 | 
			
		||||
  if (roles.indexOf('admin') >= 0) return true // admin permission passed directly
 | 
			
		||||
  if (roles.includes('admin')) return true // admin permission passed directly
 | 
			
		||||
  if (!permissionRoles) return true
 | 
			
		||||
  return roles.some(role => permissionRoles.indexOf(role) >= 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const whiteList = ['/login', '/auth-redirect']// no redirect whitelist
 | 
			
		||||
const whiteList = ['/login', '/auth-redirect'] // no redirect whitelist
 | 
			
		||||
 | 
			
		||||
router.beforeEach((to, from, next) => {
 | 
			
		||||
  NProgress.start() // start progress bar
 | 
			
		||||
  if (getToken()) { // determine if there has token
 | 
			
		||||
  if (getToken()) {
 | 
			
		||||
    // determine if there has token
 | 
			
		||||
 | 
			
		||||
    /* has token*/
 | 
			
		||||
    if (to.path === '/login') {
 | 
			
		||||
      next({ path: '/' })
 | 
			
		||||
      NProgress.done() // if current page is dashboard will not trigger	afterEach hook, so manually handle it
 | 
			
		||||
    } else {
 | 
			
		||||
      if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
 | 
			
		||||
        store.dispatch('GetUserInfo').then(res => { // 拉取user_info
 | 
			
		||||
          const roles = res.data.roles // note: roles must be a array! such as: ['editor','develop']
 | 
			
		||||
          store.dispatch('GenerateRoutes', { roles }).then(() => { // 根据roles权限生成可访问的路由表
 | 
			
		||||
            router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
 | 
			
		||||
            next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
 | 
			
		||||
      if (store.getters.roles.length === 0) {
 | 
			
		||||
        // 判断当前用户是否已拉取完user_info信息
 | 
			
		||||
        store
 | 
			
		||||
          .dispatch('GetUserInfo')
 | 
			
		||||
          .then(res => {
 | 
			
		||||
            // 拉取user_info
 | 
			
		||||
            const roles = res.data.roles // note: roles must be a object array! such as: [{id: '1', name: 'editor'}, {id: '2', name: 'developer'}]
 | 
			
		||||
            store.dispatch('GenerateRoutes', { roles }).then(accessRoutes => {
 | 
			
		||||
              // 根据roles权限生成可访问的路由表
 | 
			
		||||
              router.addRoutes(accessRoutes) // 动态添加可访问路由表
 | 
			
		||||
              next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
 | 
			
		||||
            })
 | 
			
		||||
          })
 | 
			
		||||
        }).catch((err) => {
 | 
			
		||||
          store.dispatch('FedLogOut').then(() => {
 | 
			
		||||
            Message.error(err)
 | 
			
		||||
            next({ path: '/' })
 | 
			
		||||
          .catch(err => {
 | 
			
		||||
            store.dispatch('FedLogOut').then(() => {
 | 
			
		||||
              Message.error(err)
 | 
			
		||||
              next({ path: '/' })
 | 
			
		||||
            })
 | 
			
		||||
          })
 | 
			
		||||
        })
 | 
			
		||||
      } else {
 | 
			
		||||
        // 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
 | 
			
		||||
        if (hasPermission(store.getters.roles, to.meta.roles)) {
 | 
			
		||||
@@ -49,7 +57,8 @@ router.beforeEach((to, from, next) => {
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    /* has no token*/
 | 
			
		||||
    if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
 | 
			
		||||
    if (whiteList.indexOf(to.path) !== -1) {
 | 
			
		||||
      // 在免登录白名单,直接进入
 | 
			
		||||
      next()
 | 
			
		||||
    } else {
 | 
			
		||||
      next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ import nestedRouter from './modules/nested'
 | 
			
		||||
    affix: true                  if true, the tag will affix in the tags-view
 | 
			
		||||
  }
 | 
			
		||||
**/
 | 
			
		||||
export const constantRouterMap = [
 | 
			
		||||
export const constantRoutes = [
 | 
			
		||||
  {
 | 
			
		||||
    path: '/redirect',
 | 
			
		||||
    component: Layout,
 | 
			
		||||
@@ -81,7 +81,6 @@ export const constantRouterMap = [
 | 
			
		||||
  {
 | 
			
		||||
    path: '/documentation',
 | 
			
		||||
    component: Layout,
 | 
			
		||||
    redirect: '/documentation/index',
 | 
			
		||||
    children: [
 | 
			
		||||
      {
 | 
			
		||||
        path: 'index',
 | 
			
		||||
@@ -109,10 +108,10 @@ export const constantRouterMap = [
 | 
			
		||||
export default new Router({
 | 
			
		||||
  // mode: 'history', // require service support
 | 
			
		||||
  scrollBehavior: () => ({ y: 0 }),
 | 
			
		||||
  routes: constantRouterMap
 | 
			
		||||
  routes: constantRoutes
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const asyncRouterMap = [
 | 
			
		||||
export const asyncRoutes = [
 | 
			
		||||
  {
 | 
			
		||||
    path: '/permission',
 | 
			
		||||
    component: Layout,
 | 
			
		||||
@@ -141,6 +140,15 @@ export const asyncRouterMap = [
 | 
			
		||||
          title: 'directivePermission'
 | 
			
		||||
          // if do not set roles, means: this page does not require permission
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: 'role',
 | 
			
		||||
        component: () => import('@/views/permission/role'),
 | 
			
		||||
        name: 'RolePermission',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: 'rolePermission',
 | 
			
		||||
          roles: ['admin']
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -12,8 +12,8 @@ const getters = {
 | 
			
		||||
  status: state => state.user.status,
 | 
			
		||||
  roles: state => state.user.roles,
 | 
			
		||||
  setting: state => state.user.setting,
 | 
			
		||||
  permission_routers: state => state.permission.routers,
 | 
			
		||||
  addRouters: state => state.permission.addRouters,
 | 
			
		||||
  permission_routes: state => state.permission.routes,
 | 
			
		||||
  addRoutes: state => state.permission.addRoutes,
 | 
			
		||||
  errorLogs: state => state.errorLog.logs
 | 
			
		||||
}
 | 
			
		||||
export default getters
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										283
									
								
								src/views/permission/role.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										283
									
								
								src/views/permission/role.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,283 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="app-container">
 | 
			
		||||
    <el-button type="primary" @click="handleAddRole">
 | 
			
		||||
      {{ $t('permission.addRole') }}
 | 
			
		||||
    </el-button>
 | 
			
		||||
 | 
			
		||||
    <el-table :data="rolesList" style="width: 100%;margin-top:30px;" border>
 | 
			
		||||
      <el-table-column align="center" label="Role Key" width="220">
 | 
			
		||||
        <template slot-scope="scope">
 | 
			
		||||
          {{ scope.row.key }}
 | 
			
		||||
        </template>
 | 
			
		||||
      </el-table-column>
 | 
			
		||||
      <el-table-column align="center" label="Role Name" width="220">
 | 
			
		||||
        <template slot-scope="scope">
 | 
			
		||||
          {{ scope.row.name }}
 | 
			
		||||
        </template>
 | 
			
		||||
      </el-table-column>
 | 
			
		||||
      <el-table-column align="header-center" label="Description">
 | 
			
		||||
        <template slot-scope="scope">
 | 
			
		||||
          {{ scope.row.description }}
 | 
			
		||||
        </template>
 | 
			
		||||
      </el-table-column>
 | 
			
		||||
      <el-table-column align="center" label="Operations">
 | 
			
		||||
        <template slot-scope="scope">
 | 
			
		||||
          <el-button type="primary" size="small" @click="handleEdit(scope)">
 | 
			
		||||
            {{ $t('permission.editPermission') }}
 | 
			
		||||
          </el-button>
 | 
			
		||||
          <el-button type="danger" size="small" @click="handleDelete(scope)">
 | 
			
		||||
            {{ $t('permission.delete') }}
 | 
			
		||||
          </el-button>
 | 
			
		||||
        </template>
 | 
			
		||||
      </el-table-column>
 | 
			
		||||
    </el-table>
 | 
			
		||||
 | 
			
		||||
    <el-dialog :visible.sync="dialogVisible" :title="dialogType==='edit'?'Edit Role':'New Role'">
 | 
			
		||||
      <el-form :model="role" label-width="80px" label-position="left">
 | 
			
		||||
        <el-form-item label="Name">
 | 
			
		||||
          <el-input v-model="role.name" placeholder="Role Name" />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
        <el-form-item label="Desc">
 | 
			
		||||
          <el-input
 | 
			
		||||
            v-model="role.description"
 | 
			
		||||
            :autosize="{ minRows: 2, maxRows: 4}"
 | 
			
		||||
            type="textarea"
 | 
			
		||||
            placeholder="Role Description"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
        <el-form-item label="Menus">
 | 
			
		||||
          <el-tree ref="tree" :check-strictly="checkStrictly" :data="routesData" :props="defaultProps" show-checkbox node-key="path" class="permission-tree" />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </el-form>
 | 
			
		||||
      <div style="text-align:right;">
 | 
			
		||||
        <el-button type="danger" @click="dialogVisible=false">
 | 
			
		||||
          {{ $t('permission.cancel') }}
 | 
			
		||||
        </el-button>
 | 
			
		||||
        <el-button type="primary" @click="confirmRole">
 | 
			
		||||
          {{ $t('permission.confirm') }}
 | 
			
		||||
        </el-button>
 | 
			
		||||
      </div>
 | 
			
		||||
    </el-dialog>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import path from 'path'
 | 
			
		||||
import { deepClone } from '@/utils'
 | 
			
		||||
import { getRoutes, getRoles, addRole, deleteRole, updateRole } from '@/api/role'
 | 
			
		||||
import i18n from '@/lang'
 | 
			
		||||
 | 
			
		||||
const defaultRole = {
 | 
			
		||||
  key: '',
 | 
			
		||||
  name: '',
 | 
			
		||||
  description: '',
 | 
			
		||||
  routes: []
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      role: Object.assign({}, defaultRole),
 | 
			
		||||
      routes: [],
 | 
			
		||||
      rolesList: [],
 | 
			
		||||
      dialogVisible: false,
 | 
			
		||||
      dialogType: 'new',
 | 
			
		||||
      checkStrictly: false,
 | 
			
		||||
      defaultProps: {
 | 
			
		||||
        children: 'children',
 | 
			
		||||
        label: 'title'
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    routesData() {
 | 
			
		||||
      return this.routes
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  created() {
 | 
			
		||||
    // Mock: get all routes and roles list from server
 | 
			
		||||
    this.getRoutes()
 | 
			
		||||
    this.getRoles()
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    async getRoutes() {
 | 
			
		||||
      const res = await getRoutes()
 | 
			
		||||
      this.serviceRoutes = res.data
 | 
			
		||||
      const routes = this.generateRoutes(res.data)
 | 
			
		||||
      this.routes = this.i18n(routes)
 | 
			
		||||
    },
 | 
			
		||||
    async getRoles() {
 | 
			
		||||
      const res = await getRoles()
 | 
			
		||||
      this.rolesList = res.data
 | 
			
		||||
    },
 | 
			
		||||
    i18n(routes) {
 | 
			
		||||
      const app = routes.map(route => {
 | 
			
		||||
        route.title = i18n.t(`route.${route.title}`)
 | 
			
		||||
        if (route.children) {
 | 
			
		||||
          route.children = this.i18n(route.children)
 | 
			
		||||
        }
 | 
			
		||||
        return route
 | 
			
		||||
      })
 | 
			
		||||
      return app
 | 
			
		||||
    },
 | 
			
		||||
    // Reshape the routes structure so that it looks the same as the sidebar
 | 
			
		||||
    generateRoutes(routes, basePath = '/') {
 | 
			
		||||
      const res = []
 | 
			
		||||
 | 
			
		||||
      for (let route of routes) {
 | 
			
		||||
        // skip some route
 | 
			
		||||
        if (route.hidden) { continue }
 | 
			
		||||
 | 
			
		||||
        const onlyOneShowingChild = this.onlyOneShowingChild(route.children, route)
 | 
			
		||||
 | 
			
		||||
        if (route.children && onlyOneShowingChild && !route.alwaysShow) {
 | 
			
		||||
          route = onlyOneShowingChild
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const data = {
 | 
			
		||||
          path: path.resolve(basePath, route.path),
 | 
			
		||||
          title: route.meta && route.meta.title
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // recursive child routes
 | 
			
		||||
        if (route.children) {
 | 
			
		||||
          data.children = this.generateRoutes(route.children, data.path)
 | 
			
		||||
        }
 | 
			
		||||
        res.push(data)
 | 
			
		||||
      }
 | 
			
		||||
      return res
 | 
			
		||||
    },
 | 
			
		||||
    generateArr(routes) {
 | 
			
		||||
      let data = []
 | 
			
		||||
      routes.forEach(route => {
 | 
			
		||||
        data.push(route)
 | 
			
		||||
        if (route.children) {
 | 
			
		||||
          const temp = this.generateArr(route.children)
 | 
			
		||||
          if (temp.length > 0) {
 | 
			
		||||
            data = [...data, ...temp]
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
      return data
 | 
			
		||||
    },
 | 
			
		||||
    handleAddRole() {
 | 
			
		||||
      this.role = Object.assign({}, defaultRole)
 | 
			
		||||
      if (this.$refs.tree) {
 | 
			
		||||
        this.$refs.tree.setCheckedNodes([])
 | 
			
		||||
      }
 | 
			
		||||
      this.dialogType = 'new'
 | 
			
		||||
      this.dialogVisible = true
 | 
			
		||||
    },
 | 
			
		||||
    handleEdit(scope) {
 | 
			
		||||
      this.dialogType = 'edit'
 | 
			
		||||
      this.dialogVisible = true
 | 
			
		||||
      this.checkStrictly = true
 | 
			
		||||
      this.role = deepClone(scope.row)
 | 
			
		||||
      this.$nextTick(() => {
 | 
			
		||||
        const routes = this.generateRoutes(this.role.routes)
 | 
			
		||||
        this.$refs.tree.setCheckedNodes(this.generateArr(routes))
 | 
			
		||||
        // set checked state of a node not affects its father and child nodes
 | 
			
		||||
        this.checkStrictly = false
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    handleDelete({ $index, row }) {
 | 
			
		||||
      this.$confirm('Confirm to remove the role?', 'Warning', {
 | 
			
		||||
        confirmButtonText: 'Confirm',
 | 
			
		||||
        cancelButtonText: 'Cancel',
 | 
			
		||||
        type: 'warning'
 | 
			
		||||
      })
 | 
			
		||||
        .then(async() => {
 | 
			
		||||
          await deleteRole(row.key)
 | 
			
		||||
          this.rolesList.splice($index, 1)
 | 
			
		||||
          this.$message({
 | 
			
		||||
            type: 'success',
 | 
			
		||||
            message: 'Delete succed!'
 | 
			
		||||
          })
 | 
			
		||||
        })
 | 
			
		||||
        .catch(err => { console.error(err) })
 | 
			
		||||
    },
 | 
			
		||||
    generateTree(routes, basePath = '/', checkedKeys) {
 | 
			
		||||
      const res = []
 | 
			
		||||
 | 
			
		||||
      for (const route of routes) {
 | 
			
		||||
        const routePath = path.resolve(basePath, route.path)
 | 
			
		||||
 | 
			
		||||
        // recursive child routes
 | 
			
		||||
        if (route.children) {
 | 
			
		||||
          route.children = this.generateTree(route.children, routePath, checkedKeys)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (checkedKeys.includes(routePath) || (route.children && route.children.length >= 1)) {
 | 
			
		||||
          res.push(route)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return res
 | 
			
		||||
    },
 | 
			
		||||
    async confirmRole() {
 | 
			
		||||
      const isEdit = this.dialogType === 'edit'
 | 
			
		||||
 | 
			
		||||
      const checkedKeys = this.$refs.tree.getCheckedKeys()
 | 
			
		||||
      this.role.routes = this.generateTree(deepClone(this.serviceRoutes), '/', checkedKeys)
 | 
			
		||||
 | 
			
		||||
      if (isEdit) {
 | 
			
		||||
        await updateRole(this.role.key, this.role)
 | 
			
		||||
        for (let index = 0; index < this.rolesList.length; index++) {
 | 
			
		||||
          if (this.rolesList[index].key === this.role.key) {
 | 
			
		||||
            this.rolesList.splice(index, 1, Object.assign({}, this.role))
 | 
			
		||||
            break
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        const { data } = await addRole(this.role)
 | 
			
		||||
        this.role.key = data
 | 
			
		||||
        this.rolesList.push(this.role)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const { description, key, name } = this.role
 | 
			
		||||
      this.dialogVisible = false
 | 
			
		||||
      this.$notify({
 | 
			
		||||
        title: 'Success',
 | 
			
		||||
        dangerouslyUseHTMLString: true,
 | 
			
		||||
        message: `
 | 
			
		||||
            <div>Role Key: ${key}</div>
 | 
			
		||||
            <div>Role Nmae: ${name}</div>
 | 
			
		||||
            <div>Description: ${description}</div>
 | 
			
		||||
          `,
 | 
			
		||||
        type: 'success'
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    // reference: src/view/layout/components/Sidebar/SidebarItem.vue
 | 
			
		||||
    onlyOneShowingChild(children = [], parent) {
 | 
			
		||||
      let onlyOneChild = null
 | 
			
		||||
      const showingChildren = children.filter(item => !item.hidden)
 | 
			
		||||
 | 
			
		||||
      // When there is only one child route, the child route is displayed by default
 | 
			
		||||
      if (showingChildren.length === 1) {
 | 
			
		||||
        onlyOneChild = showingChildren[0]
 | 
			
		||||
        onlyOneChild.path = path.resolve(parent.path, onlyOneChild.path)
 | 
			
		||||
        return onlyOneChild
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Show parent if there are no child route to display
 | 
			
		||||
      if (showingChildren.length === 0) {
 | 
			
		||||
        onlyOneChild = { ... parent, path: '', noShowingChildren: true }
 | 
			
		||||
        return onlyOneChild
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return false
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.app-container {
 | 
			
		||||
  .roles-table {
 | 
			
		||||
    margin-top: 30px;
 | 
			
		||||
  }
 | 
			
		||||
  .permission-tree {
 | 
			
		||||
    margin-bottom: 30px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
		Reference in New Issue
	
	Block a user