vue2动态路由,路由权限控制思路

先定义项目静态路由

route/index.js

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

/* Layout */
import Layout from '@/layout'
// 使用俩个独立的数组分别管理动态路由和静态路由
// 所有的动态路由 不同用户看到的数量不一致

// 静态路由 每个用户都可以看到
export const constantRoutes = [
  {
    path: '/login',
    component: () => import('@/views/Login/index'),
    hidden: true
  },

  {
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true
  },

  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [{
      path: 'dashboard',
      name: 'Dashboard',
      component: () => import('@/views/Dashboard/index'),
      meta: { title: '首页', icon: 'dashboard' }
    }]
  },
  {
    path: '/import',
    component: Layout,
    hidden: true,
    children: [{
      path: '',
      name: 'import',
      component: () => import('@/views/ImportExcel/index.vue'),
      meta: { title: '导入' }
    }]
  },
  {
    path: '/detail',
    component: Layout,
    hidden: true,
    children: [{
      path: '',
      name: 'detail',
      component: () => import('@/views/Employee/detail.vue'),
      meta: { title: '员工详情' }
    }]
  }

]

const createRouter = () => new Router({
  // mode: 'history', // require service support
  mode: 'history', // 路由模式 默认是hash
  scrollBehavior: () => ({ y: 0 }), // 路由的滚动行为
  routes: [...constantRoutes]
})

const router = createRouter() // 每次调用都会拿到一个新的router实例对象

// 这个方法就是用来重置路由的方法 只要调用此方法就可以把先前的路由记录清空
export function resetRouter() {
    
  // 得到一个全新的router实例对象
  const newRouter = createRouter()
  
  // 使用新的路由记录覆盖掉老的路由记录
  // 包含了所有的路由记录 path - component
  router.matcher = newRouter.matcher // reset router
}

export default router

在定义动态路由

router/asyncRouters.js

import Layout from '../layout'
// 所有的动态路由表
// Layout组件作用: 提供一个外层的框架 左侧 右边 顶部
// 二级路由path置空: 如果当前的二级路由path为空 那么它将作为默认的二级路由渲染
// 访问的是一级路由的路径 但是二级也会跟着一起渲染

export const asyncRoutes = [
  // 组织架构
  {
    path: '/department',
    component: Layout,
    children: [{
      path: '',
      name: 'departments',
      component: () => import('@/views/Department/index'),
      meta: { title: '组织架构', icon: 'tree' }
    }]
  },
  // 角色管理
  {
    path: '/setting',
    component: Layout,
    children: [{
      path: '',
      name: 'settings',
      component: () => import('@/views/Setting/index'),
      meta: { title: '角色管理', icon: 'setting' }
    }]
  },
  // 员工管理
  {
    path: '/employee',
    component: Layout,
    children: [{
      path: '',
      name: 'employees',
      component: () => import('@/views/Employee/index'),
      meta: { title: '员工管理', icon: 'people' }
    }]
  },
  // 权限点
  {
    path: '/permission',
    component: Layout,
    children: [{
      path: '',
      name: 'permissions',
      component: () => import('@/views/Permission/index'),
      meta: { title: '权限点管理', icon: 'lock' }
    }]
  },

  {
    path: '/salarys',
    component: Layout,
    children: [{
      path: '', // 如果children path置空的话 当前这个路由会作为一级渲染的默认路由
      name: 'salarys',
      component: () => import('@/views/Salary'),
      meta: { title: '工资管理', icon: 'money' }
    }]
  }
]

路由前置守卫

// 权限控制
// 1. 路由跳转权限控制
// 2. 菜单权限控制

import router from '@/router'
import store from '@/store'
import { getToken } from './utils/auth'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'

// 白名单
const WHITELIST = ['/login', '/404']

// 导入动态路由表
import { asyncRoutes } from '@/router/asyncRouters'

router.beforeEach(async(to, from, next) => {
  // to: 到哪里去 目标路由对象
  // from: 从哪里来 来源路由对象
  // next: 放行函数[在分支中都应该有且只有一个next]
  NProgress.start() // 开启进度条
  
  const token = getToken()
  
  if (token) {
    // 有token
    if (to.path === '/login') {
      next('/')
    } else {
      next()
      // 最合适的位置 满足以上三个条件 调用action函数
      // 如果当前已经调用过了 vuex已经存在数据 就不需要重复调用
      if (!store.state.user.userInfo.userId) {

        // 获取用户信息的接口
        const res = await store.dispatch('user/fetchGetUserInfo')
        
        // 权限控制在这里进行实现
        // 1. 拿到俩份数据 一份menus标识数据 一份动态路由表数据
        const menus = res.roles.menus
       
        // 2. 以menus为主对asyncRoutes做过滤 条件是: 需要判断动态路由表中的每一项
        // route.chilren[0].name 尝试去menus中找 如果找到了代表有资格 如果找不到就代表没有资格
        const filterRoutes = asyncRoutes.filter(route => menus.includes(route.children[0].name))
        
        // 3. 把过滤之后的路由加入到路由系统中
        // 路由系统: 浏览器中访问一个路径path 可以渲染对应的组件
        // 本地的动态路由默认情况下 不加入到路由系统中 而是把这些符合条件的加入到路由系统中
        // vue-router提供了一个方法专门做这件事儿 addRoutes
        // 如果你配置了兜底路由 path * 必须把它放到整个路由表的末尾 [静态 + 动态 + *]
        // 有问题的是这样的 [静态的 + * + 动态]
        router.addRoutes([...filterRoutes, { path: '*', redirect: '/404', hidden: true }])
        
        // 4. 把filterRoutes显示到左侧的菜单中
        // 实现思路: 之前渲染左侧菜单时使用的数据是静态的 是永远不变的 哪怕我们把新的动态路由
        // 加入到了路由系统中 它也不会自动变化
        // 让渲染左侧菜单的路由变成可变的? -> 什么东西是响应式的呢? data computed vuex
        // 结论:我们需要单独通过vuex维护我们左侧菜单的数据
        store.commit('menu/setMenuList', filterRoutes)
      }
    }
  } else {
    // 没有token 数组中是否找得到某项 数组方法?includes
    if (WHITELIST.includes(to.path)) {
      // 在白名单内
      next()
    } else {
      next('/login')
    }
  }
  NProgress.done() // 结束进度条
})

vuex

import { constantRoutes } from '@/router'

export default {
  // 开启模块命名空间 才算是严格的模块化管理
  // 访问 mutation action函数的时候 都需要在前面加上模块名称才可以
  namespaced: true,
  
  // constantRoutes是静态路由
  state: () => ({
    menuList: [...constantRoutes] // 路由表(菜单表)
  }),
  
  mutations: {
    // 只需要调用这个方法 传入过滤之后的动态路由表 完成静态+动态的组合
    setMenuList(state, filterAsyncRoutes) {
      state.menuList = [...constantRoutes, ...filterAsyncRoutes]
    }
  }
}

html

 <!-- 左侧菜单组件 -->
      <el-menu
        :default-active="activeMenu"
        :collapse="isCollapse"
        :background-color="variables.menuBg"
        :text-color="variables.menuText"
        :unique-opened="false"
        :active-text-color="variables.menuActiveText"
        :collapse-transition="false"
        mode="vertical"
      >
        <!-- 菜单中的每一项 -->
        <sidebar-item
          v-for="route in routes"
          :key="route.path"
          :item="route"
          :base-path="route.path"
        />
      </el-menu>
      
      
       computed: {
            routes() {
              // 路由使用的数组
              // router.options对象内置的属性 固定的
              // 里面拿到的是我们在router.js中实例化路由对象的时候传入给routes选项的数组
              // 这里拿到的就是初始化路由的所有路由表 [静态也包含动态]
              // 特点就是静态的: 一旦项目启动起来就不会再发生变化
              return this.$store.state.menu.menuList
            },
                         
       }

退出登录重置路由

现存问题
假如A用户先进行的登录 它的菜单权限是 ['工资','社保'],退出登录之后使用B用户登录,此时B用户的菜单权限为['工资'],
但是因为上一个路由记录还在,所以B用户同样可以访问到社保 模块,这显然是不对的,
所以要求我们在退出登录的时候先清除一下先前的路由记录,本质上是因为addRoutes方法是一个累加的方法

解决方案
vue-router 提供了addRoute来进行添加路由,但是却没有移除路由的相关选项,
当切换用户的时候,想要移除路由再添加怎么办呢?可以另外建一个router实例来替换之前的实例。


我们的router/index.js文件,发现一个重置路由方法

// 重置路由
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // 重新设置路由的可匹配路径
}

这个方法就是将路由重新实例化,相当于换了一个新的路由,之前加的路由就不存在了,需要在登出的时候, 调用一下即可
import { resetRouter } from '@/router'

// 退出的action操作
logout(context) {
  // 1. 移除vuex个人信息
  context.commit('removeUserInfo')
  // 2. 移除token信息
  context.commit('removeToken')
  // 3. 重置路由系统
  resetRouter()
}

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值