VUE中使用router.addRoutes进行管理菜单权限

20 篇文章 1 订阅

addRoutes

vue 动态添加路由 官方文档

需求:不同用户登陆后台,针对用户角色添加不同的权限

思路:在路由全局前置守卫中,从后台获取用户最新的权限列表,使用 addRoutes 方法,动态更新用户权限

// src/permission.js

import router from './router'

import { getToken } from '@/utils/auth' // get token from cookie

const whiteList = ['/login', '/auth-redirect'] // no redirect whitelist

router.beforeEach(async (to, from, next) => {

  const hasToken = getToken()

  if (hasToken) {
    if (to.path === '/login') {
      // if is logged in, redirect to the home page
      next({ path: '/' })
    } else {
      // determine whether the user has obtained his permission roles through getInfo
      const hasRoles = store.getters.roles && store.getters.roles.length > 0
      if (hasRoles) {
        next()
      } else {
        try {
            
          // 获取用户信息
          const userInfo = await store.dispatch('user/getInfo')
          const { role_id } = userInfo
          // 根据角色生成可访问的路由表
          const accessRoutes = await store.dispatch('permission/generateRoutes', role_id)

          // 动态添加可访问的路由
          router.addRoutes(accessRoutes)

          // hack 方法,确保addRoutes是完整的
          // replace设为true
          next({ ...to, replace: true })

        } catch (error) {
          // 删除token-提示错误-重定向到登录页面
          await store.dispatch('user/resetToken')
          Message.error(error || 'Has Error')
          next(`/login?redirect=${to.path}`)
        }
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      next(`/login?redirect=${to.path}`)
    }
  }
})

router.afterEach(() => {
  ...
})

对路由进行二次处理,加入 Layout 组件,不同项目不同的处理方式,最终返回 vue-router 能正确解析的格式

// store/modules/permission.js

import { constantRoutes } from '@/router'
import Layout from '@/layout'

// 获取用户权限的路由接口
import { getRoutes, getQueryPermission } from '@/api/role'

const componentsMap = { // 定义好的路径
    '/': () =>
        import('@/views/dashboard'),
    '/setting/menu': () =>
        import('@/views/setting/menu'),
    '/setting/user': () =>
        import('@/views/setting/user'),
    '/setting/role': () =>
        import('@/views/setting/role'),
    // ...
}

/**
 * 递归处理后端返回路由,最后返回 vue-router 能正确解析的格式
 * @param routes 后端返回的路由表
 * @param isFilterBtn 是否过滤掉按钮
 * @param isParent 是否是最外层的节点
 * @returns {[]}
 */
function filterRouters(routes, isFilterBtn, isParent = true) {
    const res = []

    routes.forEach(route => {
        const tmp = {
            ...route
        }
        const router = generateRouter(tmp, isParent)

        // 过滤按钮类型菜单
        if (isFilterBtn) {
            if (tmp.menu_type !== 3) {
                if (router.children) {
                    router.children = filterRouters(router.children, isFilterBtn, false)
                }

                res.push(router)
            }
        } else {
            if (router.children) {
                router.children = filterRouters(router.children, isFilterBtn, false)
            }

            res.push(router)
        }
    })

    return res
}

/**
 * 替换后端返回路由表中字段
 * @param item
 * @param isParent 是否是最外层的节点
 * @returns {{path: *, component: (*), hidden: *, meta: *, name: *}}
 */
function generateRouter(item, isParent) {
    let hidden = false

    // 将后端返回的 roles 字符串类型转换为数组类型(暂时干掉了这个字段,后端只返回有权限的菜单,前端不判断)
    if (item.meta && item.meta.roles) {
        item.meta.roles = [item.meta.roles]
    }
    item.meta.noCache = item.is_cache !== 1

    if (item.is_visible === 1) {
        hidden = false
    } else {
        hidden = true
    }

    const meta = Object.assign({}, item.meta)
    delete meta.icon // 暂时删掉 icon
    const router = {
        menu_type: item.menu_type,
        menu_id: item.menu_id,
        path: item.path,
        name: item.cache_name,
        title_name: item.name,
        hidden: hidden,
        meta: meta,
        component: isParent ? Layout : componentsMap[item.path]
    }

    if (item.children && item.children.length) router.children = item.children
    return router
}

const state = {
    routes: [], // 当前角色权限的菜单
    addRoutes: [],
    allRoutes: [], // 所有的菜单
    assessedBtnRoutes: []
}

const mutations = {
    SET_ROUTES: (state, routes) => {
        state.addRoutes = routes
        state.routes = constantRoutes.concat(routes)
    },
    SET_ALL_ROUTES: (state, routes) => {
        state.allRoutes = routes
    },
    SET_ASSESSED_BTN_ROUTES: (state, routes) => {
        state.assessedBtnRoutes = routes
    }
}

const actions = {
    generateRoutes({
        commit,
        dispatch
    }, role_id) {
        return new Promise(resolve => {
            let accessedRoutes
            let allRoutes
            let accessedBtnRoutes
            // 如果是 admin 角色,拥有所有权限 asyncRoutes

            // 本地路由 asyncRoutes 过滤对应权限
            // if (roles.includes('admin')) {
            //   accessedRoutes = asyncRoutes || []
            // } else {
            //   accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
            // }
            // commit('SET_ROUTES', accessedRoutes)
            // resolve(accessedRoutes)

            // 服务端路由
            getRoutes().then(response => {
                const {
                    data
                } = response

                // 服务端未做路由权限,全部返回,filterAsyncRoutes 过滤对应权限
                // 如果是 admin 角色,拥有所有权限 asyncRoutes
                // if (roles.includes('admin')) {
                //   accessedRoutes = convertRouter(data) || []
                // } else {
                //   accessedRoutes = filterAsyncRoutes(convertRouter(data), roles)
                // }

                // TODO  add 404 router
                // 服务端做了路由权限,返回当前角色下的路由 直接用服务端返回路由,整理下格式就可以
                // let lastRoutes = filterRouters(data) || []
                // accessedRoutes = lastRoutes.push({ path: '*', redirect: '/404', hidden: true })
                getQueryPermission({
                    role_id
                }).then(response => {
                    // 只有权限的路由(无按钮)
                    accessedRoutes = filterRouters(data, response.data, true) || []

                    // console.log(accessedRoutes, '---accessedRoutes')

                    // 全部路由
                    allRoutes = filterRouters(data, false) || []

                    // 只有权限的路由(有按钮)
                    accessedBtnRoutes = filterRouters(data, response.data, false) || []
                    commit('SET_ROUTES', accessedRoutes)
                    commit('SET_ALL_ROUTES', allRoutes)
                    commit('SET_ASSESSED_BTN_ROUTES', accessedBtnRoutes)
                    resolve(accessedRoutes)
                })
            })
        })
    }
}

export default {
    namespaced: true,
    state,
    mutations,
    actions
}

main.js 文件中引入权限管理文件

import Vue from 'vue'

import App from './App'
import store from './store'
import router from './router'

import './permission' // 权限控制

Vue.config.productionTip = false

new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App)
})

这样,我们在每一次登录及刷新页面的时候,都会从后台获取最新的用户信息,实时权限列表更新

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值