前端路由权限的设计思路及具体实现

前言

        接到的需求是在现有的后台系统上,增加一个可以给用户配置路由权限的功能,用户登录系统后根据用户路由权限展示有权限的路由菜单,无权限的路由菜单即使在浏览器地址栏手动输入也无法访问。同时也可以在后台系统动态添加自定义路由,路由指向一个任意http开头的地址,用户访问这个自定义路由会在系统内用iframe打开对应的页面。

        项目使用的是vue2.x和vue-router3.x。

功能分析

        配置路由权限和添加自定义路由需要一个路由管理界面,这是一个基础的功能没什么需要说的。根据登录用户展示不同路由地址,这里可以用router.addRoute根据登录用户的路由权限动态添加路由。

        上述功能涉及到的路由可以分为两种,一种是指向项目现有页面的路由,另一种是指向配置的任意地址的路由。接口返回的有权限的路由地址只是一个字符串,需要确定该地址对应项目中的哪个component;另一种路由可以直接用iframe展示。

具体实现

        在store里保存一个状态routeInit,标识路由是否已根据权限初始化

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    // Define your initial state here
    routeInit: false // 路由是否已初始化
  },
  mutations: {
    // Define your mutations here
    setRouteInit (state, payload) {
      state.routeInit = payload
    }
  },
  actions: {
    // Define your actions here
  },
  getters: {
    // Define your getters here
  }
})

export default store

        前端存储一份路由数据routes,routes包含路由的基础信息(path、component、children)。注册一个router的全局前置守卫,在beforeEach里做路由数据的初始化。

router.beforeEach((to, from, next) => intercept(to, from, next))

intercept函数是一个async函数,需要在intercept调用接口,根据接口返回的有权限的路由地址去和前端存储的routes数据比对,主要是找路由地址和component的对应关系,然后组合成新的路由数据,最后用router.addRoute动态添加处理过的路由。

import store from '../store'
import routeList from './routes'
// 模板-用于新增的路由,对应打开页面
import tempFirst from '@/views/temp/first'
import tempSecond from '@/views/temp/second'
import tempThird from '@/views/temp/third'
import tempForth from '@/views/temp/forth'
import { queryAuthRouter } from '@/api'

async function intercept (to, from, next) {
  // 根据store存储的状态判断是否需要初始化路由
  if (!store.state.routeInit) { // 路由未初始化时,初始化路由
    const res = await queryAuthRouter()
    let resList = []
    if (res.success) {
      resList = res?.data || []
    }
    /**
    *
    * @param {*} authArr 接口返回的权限数据
    * @param {*} originArr 前端路由数据,固定的路由数据
    * @param {*} fPath 当前路径父级path
    * @param {*} level 当前层级
    * @returns
    */
    const filterLoop = (authArr, originArr, fPath, level) => {
      // 实际应用时路由最多四级,因为是可随意配置的路由,所以在此加了判断
      if (level > 4) {
        return []
      }
      return authArr.map(a => {
        const oItem = originArr.find(o => o.path.toLowerCase() === a.path.toLowerCase())

        // 当前路由完整路径
        const path = `${fPath}/${a.path}`

        // 路由数据
        // meta
        let meta
        // 路由对应的组件
        let component
        if (!oItem?.component) { // 如果当前路由不在原始数据中(新增的路由)
          switch (level) {
            case 1:
              component = tempFirst
              break
            case 2:
              component = tempSecond
              break
            case 3:
              component = tempThird
              break
            case 4:
              component = tempForth
              break
          }
          meta = { title: a.label }
        } else {
          component = oItem.component
          meta = { ...oItem.meta, title: a.label }
        }
        const routeItem = {
          path: a.path,
          redirect: '',
          component,
          meta,
          children: []
        }

        // 一级菜单设置重定向
        if (level === 1) {
          const findRedirect = (list, curPath) => {
            if (list?.[0]?.path) {
              curPath = `${curPath}/${list[0].path}`
            }
            if (list?.[0]?.children?.length > 0) {
              curPath = findRedirect(list[0].children, curPath)
            }
            return curPath
          }
          routeItem.redirect = findRedirect(a.children || [], path)
        }

        if (a.children?.length > 0) {
          routeItem.children = filterLoop(a.children, oItem?.children || [], path, level + 1)
        }

        // 如果没有子项
        if (!(routeItem?.children?.length > 0)) {

          if (a.url) { // 有打开地址
            // 路由的props数据在组件中也是通过props接收
            routeItem.props = {
              url: a.url
            }

          }
        }

        return routeItem
      })
    }

    const filterList = filterLoop(resList, routeList, '', 1)

    // 动态添加路由
    filterList.forEach(item => {
      router.addRoute('layout', item)
    })


    store.commit('setRouteInit', true)

    next({ ...to, replace: true })
  } else {
    next()
  }
}

总结

        前端路由权限的处理主要是根据接口返回的有权限的路由地址,通过router.addRoute动态添加路由。

        原理比较简单,实际在做这个需求的时候,应用场景比较复杂,比如需要根据路由生成一个导航菜单;实际的导航配置中还有操作权限、自定义表单等;后端接口返回的是导航的所有配置数据,需要根据是否有权限去过滤等等。写这篇文章时把原有功能剔除的剔除、能简化的简化,最后写完后发现自己好像水了一篇文章,😄。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值