vue权限管理(动态路由)

技术栈:vue2.x + vuex +element-ui + vue-router2.x 使用vue-router的addRoutes,优化公司后台项目的权限管理

🧐 权限管理(动态路由)

## 现状:
-router.js 所有路由
-resource.js 资源码文件
步骤:

路由拦截器中根据是否存在token来请求接口,获取用户所有资源码,若无token,返回到登录页
获取到资源码后,使用vuex储存有权限的路由并生成 menu菜单导航

缺点:
只是单纯控制了菜单,没有对路由进行权限控制
解决方案:
使用vue-router的 addRouters 对路由进行动态添加,
步骤: 

路由

在路由中添加字段 btnCodes 表示 该页面下的权限按钮的内容 code表示路由权限码

javascript 复制代码
btncodes:{code:'',name:''}

路由守卫

使用`vuex`对获取到资源码进行处理,获取到最新的addRouters 然后添加到路由中

在使用动态添加路由addRoutes()会遇到下面的情况:

在addRoutes()之后第一次访问被添加的路由会白屏,这是因为刚刚addRoutes()就立刻访问被添加的路由,然而此时addRoutes()没有执行结束,因而找不到刚刚被添加的路由导致白屏。因此需要从新访问一次路由才行。

该如何解决这个问题 ? 此时就要使用next({ ...to, replace: true })来确保addRoutes()时动态添加的路由已经被完全加载上去。

next({ ...to, replace: true })中的replace: true只是一个设置信息,告诉VUE本次操作后,不能通过浏览器后退按钮,返回前一个路由。

因此next({ ...to, replace: true })可以写成next({ ...to }),不过你应该不希望用户在addRoutes()还没有完成的时候,可以点击浏览器回退按钮.

javascript 复制代码
import store from '../store/index'
import { getResources} from '@/api'
import { Message } from 'ELEMENT'
import { errorRouter, homeRouter } from './routes'

// 路由守卫
export default function guards(router) {
  router.beforeEach(async (to, from, next) => {
    const token = localStorage.getItem('token')
    console.log(to)
    if (to.path === '/user/login') {
      next()
    } else {
      if (token) {
        if (store.state.permission.registerRouteFresh) {
            const result =await getResources()
              
              if (result.code === 200) {
                const resources = result.data || []
                store.dispatch('GenerateRoutes', resources).then(() => {
                  const currentAddRouter = [{...homeRouter,children:[...store.getters.addRouters]},{...errorRouter}]
                  router.addRoutes([...currentAddRouter])
                  store.commit('SET_REGISTER_ROUTE_FRESH', false)
                  next({ ...to, replace: true })
                })
              } else {
                Message.error('菜单数据获取失败, 请刷新页面')
              }
          
        }
        next()
      } else {
        next('/user/login')
      }
    }
    // 兜底执行
    next()
  })
}

store

使用vuex 对路由权限 和权限码进行存储

import {asyncRouterMap, constantRouterMap } from '@/pages/mall/router/routes'
import { deepCopy,getPermissionRouter } from '@/utils'

function contrast(a, b) {
  if (a.code == b.code && a.path && !a.isProhibit) {
    a.display = true
  }
  if (a.children && a.children.length > 0) {
    a.children.forEach(item => {
      contrast(item, b)
    })
  }
}

function setFlagRouter(routerMap, resources) {
  resources.forEach(res => {
    routerMap.forEach(route => {
      contrast(route, res)
    })
  })
}

function filterRouter(routers) {
  if (!routers.length) {
    return
  }

  let i = routers.length
  while (i--) {
    if (!routers[i].display) {
      continue
    }

    if (routers[i].children && routers[i].children.length > 0) {
      if (!routers[i].hasChildren) {
        continue
      }
      let arr = routers[i].children
      let flag = true
      for (let k = 0; k < arr.length; k++) {
        if (arr[k].display) {
          flag = false
          break
        }
      }
      if (flag) {
        routers[i].display = false
      } else {
        filterRouter(routers[i].children)
      }
    }
  }
}

const permission = {
  state: {
    routers: constantRouterMap,
    resources: [],
    registerRouteFresh: true,
    addRouters: null
  },
  mutations: {
    SET_REGISTER_ROUTE_FRESH: (state, flag) => {
      state.registerRouteFresh = flag
    },
    SET_RESOURCES: (state, resources) => {
      state.resources = resources
    },
    SET_ROUTERS: (state, routers) => {
      state.addRouters = routers
      state.routers = constantRouterMap.concat(routers)
    },
  },
  actions: {
    GenerateRoutes({ commit }, data) {
      return new Promise(resolve => {
        let routerMap = deepCopy(asyncRouterMap )
        if (data.length) {
          setFlagRouter(routerMap, data)
          filterRouter(routerMap)
        }
        const accessedRouters = getPermissionRouter(routerMap, data)
        commit('SET_RESOURCES', data)
        commit('SET_ROUTERS', accessedRouters)
        resolve()
      })
    }
  }
}

export default permission

按钮权限

自定义指令v-permission

javascript 复制代码
import store from '../store'

const permissionDirective = {
  componentUpdated: function (el, binding, vnode) {
    let timer = null
    timer = setInterval(() => {
      if (!store.state.permission.registerRouteFresh) {
        const { value } = binding
        // 获取没有权限资源码列表数据
        const points = store.state.permission.resources
        if (value && value instanceof Array) {
          console.log(value);
          
          匹配对应的指令
          const hasPermission = points.some((point) => {
            return value.includes(point)
          })
          // 如果匹配, 则表示当前用户无该权限, 那么删除对应的功能按钮
          if (!hasPermission) {
            el.parentNode && el.parentNode.removeChild(el)
          }
        } else {
          throw new Error('v-premission Error')
        }
        clearInterval(timer)
      }
    }, 100)
  },
  unbind: function (el, binding) {}
}

export function setPermissionDirective(app) {
  app.directive('permission', permissionDirective)
}

export default permissionDirective
相关推荐
奇舞精选30 分钟前
AI Agent自动化操作浏览器的发展与应用
前端·openai
桔子爱笑33 分钟前
vuex的基本使用
前端·javascript·vue.js
一路向前的月光44 分钟前
React(7)
前端·javascript·react.js
helloweilei1 小时前
Webpack中使用@svgr/webpack处理SVG文件
前端
Nyingchi_X1 小时前
js运行机制(事件循环Event Loop、宏任务与微任务、浏览器事件循环与Nodejs事件循环的区别)
前端
2301_793069821 小时前
前后端分离的网页游戏,后端spring boot,前端vite+vue
前端·后端·vue·springboot·全栈
袋鼠云数栈UED团队1 小时前
Monaco Editor 实现在线版 Copilot
前端·react.js·aigc
刺客-Andy1 小时前
React 第二十五节 <Fragment></Fragment> 的用途以及使用注意事项详解
前端·react.js·前端框架
今朝有熊1 小时前
10-8 根据选中类别动态生成首页模块的顶部标签导航器和modal
前端
GitCode官方1 小时前
Flutter 双屏双引擎通信插件加入 GitCode:解锁双屏开发新潜能
前端·flutter·gitcode