动态路由

后台管理系统,大部分都会涉及到权限控制这一项需求,即:根据不同登录角色渲染不同页面功能

现在主流有两种方式:

  1. 前端控制

    逻辑简单,上手快

  2. 后端控制

    相对安全,需要后期改动

后端控制

后端路由时大部分后台管理项目的解决方案

核心:用户登录以后,后端根据该角色生成可访问的路由数据,前端根据这个路由数据转换成自己需要的路由结构

具体代码结构:

  1. router 文件中,只放一些静态路由和公共路由

    代码参考:https://github.com/PanJiaChen/vue-element-admin/blob/master/src/router/index.js

  2. 在 vuex 中写一个 state,把路由和获取到的角色进行匹配,控制菜单栏的显示隐藏

    import { constantRoutes } from '@/router'
    import { getRoutes } from '@/api/menu'
    import Layout from '@/layout/index'
    
    // 遍历后台传来的路由字符串,转换为组件对象
    function filterAsyncRoutes(asyncRouterMap) {
      return asyncRouterMap.filter(route => {
        if (route.component) {
          // Layout组件特殊处理
          if (route.component === 'Layout') {
            route.component = Layout
          } else {
            route.component = loadView(route.component)
          }
        }
        if (route.children && route.children.length) {
          route.children = filterAsyncRoutes(route.children)
        }
        return true
      })
    }
    function loadView(view) {
      // 注意:webpack4动态import不支持变量方式,如下写法是不行的
      // return () => import(`@/views/${view}`)
      return resolve => require([`@/views/${view}`], resolve)
    }
    
    const permission = {
      state: {
        routes: [],
        addRoutes: [],
      },
      mutations: {
        SET_ROUTES: (state, routes) => {
          state.addRoutes = routes
          state.routes = constantRoutes.concat(routes)
        },
      },
      actions: {
        GenerateRoutes({ commit }) {
          const Id = sessionStorage.getItem('SESSION_KEY')
          return new Promise(resolve => {
            getRoutes(Id).then(res => {
              const accessedRoutes = filterAsyncRoutes(res.data)
              accessedRoutes.push({ path: '*', redirect: '/404', hidden: true })
              commit('SET_ROUTES', accessedRoutes)
              resolve(accessedRoutes)
            })
          })
        },
      },
    }
    
  3. 给后端整理一份前端需要的 router 数据结构

    一般必须有的参数:id、path、name、title、children

    path: # 路由地址
    name: # 路由名称
    id: # id
    component: # 组件路径
    meta->title: # 菜单名称(和path同级就可以)
    meta->icon: # 菜单图标(和path同级就可以)
    meta->type: # 菜单类型,用于区分模块、目录、菜单、按钮
    meta->hidden: # 是否全局隐藏此菜单
    children: # 子集集合
    

    如果后端传的不是 children,是 parentId 那种类型,则需要写一个转换方法

    方法可以参考这篇文章:Vue 封装无限层级树形菜单组件(后台传的是扁平数组)

  4. 在导航守卫中,使用 router.beforeEach 进行拦截,可以动态添加可访问的路由表(使用 addRoutes 添加)

    import NProgress from 'nprogress'
    import 'nprogress/nprogress.css'
    
    NProgress.configure({ showSpinner: false })
    const whiteList = ['/login', '/auth-redirect', '/bind', '/register']
    
    router.beforeEach((to, from, next) => {
      NProgress.start()
      // 判断是否有token
      if (store.getters.token) {
        if (to.path === '/login') {
          next({ path: '/' })
        } else {
          // 判断当前用户是否已拉取完user_info信息
          if (store.getters.roles.length === 0) {
            // 获取info信息
            store.dispatch('GetInfo').then(res => {
              const roles = res.roles
              store
                .dispatch('GenerateRoutes', { roles })
                .then(accessRoutes => {
                  // 动态添加可访问路由表
                  router.addRoutes(accessRoutes)
                  // hack方法 确保addRoutes已完成
                  next({ ...to, replace: true })
                })
                .catch(err => {
                  console.log(err)
                })
            })
          } else {
            // 当有用户权限的时候,说明可访问路由表已生成
            next()
          }
        }
      } else {
        if (whiteList.includes(to.path)) {
          // 在免登录白名单,直接进入
          next()
        } else {
          // 否则全部重定向到登录页
          next('/login')
        }
      }
    })
    
  5. 从 router 中取出可用的路由对象,来进行侧边栏的渲染

也可以参考这篇文章:后端控制路由

前端控制

可以参考花裤衩的文章,手摸手,带你用vue撸后台 系列二(登录权限篇)

核心:通过 token 获取用户的 role,根据 role 动态跟路由表 meta.role 进行匹配,形成可访问的路由再通过 router.addRotes 动态挂载路由

具体代码结构:

  1. 把动态路由和静态路由分别写在 router 文件中(asyncRoutes/constantRoutes)。在动态路由的 meta 元信息中添加 roles 权限

    代码参考:https://github.com/PanJiaChen/vue-element-admin/blob/master/src/router/index.js

  2. 在 vuex 中写一个 state,把路由和获取到的角色进行匹配,控制菜单栏的显示隐藏

    代码参考:https://github.com/PanJiaChen/vue-element-admin/blob/master/src/store/modules/permission.js

  3. 在导航守卫中,使用 router.beforeEach 进行拦截,可以动态添加可访问的路由表(使用 addRoutes 添加)

    https://github.com/PanJiaChen/vue-element-admin/blob/master/src/permission.js

  4. 从 router 中取出可用的路由对象,来进行侧边栏的渲染

    https://github.com/PanJiaChen/vue-element-admin/blob/master/src/layout/components/Sidebar/index.vue

    https://github.com/PanJiaChen/vue-element-admin/blob/master/src/layout/components/Sidebar/SidebarItem.vue

Logo

前往低代码交流专区

更多推荐