Vue技术Vue Vue

基于vue-element-admin 的权限管理

2021-01-25  本文已影响0人  为光pig

1.项目中要使用到权限管理及左侧菜单动态加载 基于此。
2.项目模板使用的是vue-admin-template
这个模板比较干净,只有框架的实现,要添加权限可以参考 github上vue-element-admin项目
首先路由页面router:
有2个参数
export const constantRouterMap = [] 为初始路由参数,如登录 首页 404等共有页面 不需要权限控制的路由
export const asyncRouterMap = []为动态路由 登录成功后 在router.beforeEach中根据后端权限 加载不同路由 已展示不同的左侧菜单

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

NProgress.configure({ showSpinner: false })// NProgress 页面导航

// 验证是否有权限
function hasPermission(roles, permissionRoles) {
  if (roles.indexOf('admin') >= 0) return true // admin permission passed directly
  if (!permissionRoles) return true
  return roles.some(role => permissionRoles.indexOf(role) >= 0)
}

const whiteList = ['/login', '/auth-redirect']//白名单

router.beforeEach((to, from, next) => {
  NProgress.start() // start progress bar
  if (getToken()) { // determine if there has token
    /* has token*/
    if (to.path === '/login') { //如果是进入登录页面 则不需要权限 直接进入
      next({ path: '/' })
      NProgress.done() // 页面导航结束
    } else {
      if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
        store.dispatch('GetInfo').then(res => { // 拉取user_info
          store.dispatch('GenerateRoutes', res).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
          })
        }).catch((err) => {
          store.dispatch('FedLogOut').then(() => {
            Message.error(err || 'Verification failed, please login again')
            next({ path: '/' })
          })
        })
      } else {
        // next()
        // 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
        if (hasPermission(store.getters.perms, to.meta.perms)) {
          next()
        } else {
          next({ path: '/401', replace: true, query: { noGoBack: true }})
        }
        // 可删 ↑
      }
    }
  } else {
    /* has no token*/
    if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
      next()
    } else {
      next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
      NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
    }
  }
})

router.afterEach(() => {
  NProgress.done() // finish progress bar
})

下面为加载路由代码

import { asyncRouterMap, constantRouterMap } from '@/router'
// import store from '@/store'
/**
 * 通过meta.role判断是否与当前用户权限匹配
 * @param roles
 * @param route
 */
function hasPermission(perms, route) {  //判断是否有权限
  if (route.meta && route.meta.perms) {
    return perms.some(perm => route.meta.perms.includes(perm))
  } else {
    return true
  }
}

/**
 * 递归过滤异步路由表,返回符合用户角色权限的路由表
 * @param routes asyncRouterMap
 * @param roles
 */
function filterAsyncRouter(routes, perms) {  //过滤没有权限的列表 循环过滤 直到没有子路由
  const res = []
  routes.forEach(route => {
    const tmp = { ...route }
    if (hasPermission(perms, tmp)) {
      if (tmp.children) {
        tmp.children = filterAsyncRouter(tmp.children, perms)
      }
      res.push(tmp)
    }
  })
  return res
}

const permission = {
  state: {
    routers: constantRouterMap,
    addRouters: []
  },
  mutations: {
    SET_ROUTERS: (state, routers) => {  //保存动态路由时 将静态路由和动态路由合并
      state.addRouters = routers
      state.routers = constantRouterMap.concat(routers)   
    }
  },
  actions: {
    GenerateRoutes({ commit }, data) {
      return new Promise(resolve => {
        const roles = data.roles
        const perms = data.perms
        let accessedRouters
        if (perms) {
          if (roles.includes(1)) {  //如果未admin角色 加载所有动态路由
            accessedRouters = asyncRouterMap
          } else {  //如果不是admin角色 则加载过滤后的动态路由
            accessedRouters = filterAsyncRouter(asyncRouterMap, perms)
          }
          commit('SET_ROUTERS', accessedRouters)  //保存路由
        }
        resolve()
      })
    }
  }
}

export default permission

此时已经获得了想要的路由列表,定义一个获取路由的getter方法

const getters = {
 sidebar: state => state.app.sidebar,
 device: state => state.app.device,
 token: state => state.user.token,
 avatar: state => state.user.avatar,
 userInfo: state => state.user.userInfo,
 roles: state => state.user.roles,
 perms: state => state.user.perms,
 permission_routers: state => state.permission.routers,  //路由列表
 addRouters: state => state.permission.addRouters
}
export default getters

目录layout/sidebar/index.vue 这里是展示左侧列表的页面

<template>
  <el-scrollbar wrap-class="scrollbar-wrapper">
    <el-menu
      :default-active="$route.path"
      :collapse="isCollapse"
      :background-color="variables.menuBg"
      :text-color="variables.menuText"
      :active-text-color="variables.menuActiveText"
      :collapse-transition="false"
      mode="vertical"
    >
      <sidebar-item v-for="route in permission_routers" :key="route.path" :item="route" :base-path="route.path"/>   //加载这个路由就行
    </el-menu>
  </el-scrollbar>
</template>

<script>
import { mapGetters } from 'vuex'
import variables from '@/styles/variables.scss'
import SidebarItem from './SidebarItem'
import store from '@/store'

export default {
  components: { SidebarItem },
  created(){
    this.perms = JSON.stringify(this.$store.state.user.perms);
  },
  data(){
    return {
      perms: ''
    };
  },
  computed: {
    ...mapGetters([
      'permission_routers',
      'sidebar'
    ]),
    variables() {
      return variables
    },
    isCollapse() {
      return !this.sidebar.opened
    }
  },
  methods: {
      
  }
}
</script>

最后 来个router页面

{
    path: '/customerInfo',
    component: Layout,
    meta: { title: '客户信息管理', perms: ['customerPage'] },  //perms是后端返回的权限标识  写到这里可以使其所有子类都加上权限 也可以分别给子类添加
    children: [
      {
        path: '/customer',
        name: 'customer',  //name必须有 面包屑导航的显示
        component: () => import('@/views/page/customerInfo/index'),  //懒加载
        meta: { title: '客户信息管理', icon: 'customerInfo' } //title左侧菜单与面包屑导航显示文字 icon为左侧菜单图标
      },
      {
        path: '/customeraddressInfo',
        hidden: true,
        name: 'customeraddress',
        component: () => import('@/views/page/customerInfo/customeraddress'),
        meta: { title: '客户公司管理', icon: 'customerInfo' }
      }
    ]
  },
  {
    path: '/fleetGroup',
    component: Layout,
    meta: { title: '车队编组管理', perms: ['fleetgroupPage'] },
    children: [
      {
        path: '',
        name: 'fleet',
        component: () => import('@/views/page/fleetGroup/index'),
        meta: { title: '车队编组管理', icon: 'fleetGroup' }
      }
    ]
  },
上一篇下一篇

猜你喜欢

热点阅读