Analysis and implementation of dynamic routing in Element UI

1. Analysis of dynamic routing

  1. The generation logic of dynamic routing is as follows:
  • From @routerreading asyncRoutesand constantRoutesacquiring user roles roles, determine roleswhether to include admin.
  • If rolesit is containing adminthe filtered asyncRoutessaved to vuexthe.
  • If rolesnot included admin, then traversed routesto determine whether the access routes. If not, continue to traverse routes. If so, determine whether the route is included children. If included children, traverse children, filter children, update tmp.children, and then pass in to determine whether the route is included children. If it does not children, the route into resthe filtered asyncRoutessaved to vuexthe.
  • The filtered asyncRoutessaved to vuexthe middle, asyncRoutesand constantRoutesmerge.

Second, the realization of dynamic routing

  1. The key is a method of generating a dynamic routing premission.jsin the generateRoutesmethod, the code is as follows:
const actions = {
    
    
  // 生成动态路由的关键方法
  generateRoutes({
    
     commit }, roles) {
    
    
    // 返回 Promise 对象
    return new Promise(resolve => {
    
    
      let accessedRoutes
      if (roles.includes('admin')) {
    
    
        // 如果角色中包含 admin,则直接跳过判断,直接将 asyncRoutes 全部返回
        accessedRoutes = asyncRoutes || []
      } else {
    
    
        // 如果角色中不包含 admin,则调用 filterAsyncRoutes 过滤路由
        accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
      }
      // 将路由保存到 vuex 中
      commit('SET_ROUTES', accessedRoutes)
      resolve(accessedRoutes)
    })
  }
}
  1. mutationsIn SET_ROUTES, the code is as follows:
const mutations = {
    
    
  SET_ROUTES: (state, routes) => {
    
    
    // 将 routes 保存到 state 中的 addRoutes
    state.addRoutes = routes
    // 将 routes 集成到 src/router/index.js 中的 constantRoutes 中
    state.routes = constantRoutes.concat(routes)
  }
}
  1. The method of route filtering filterAsyncRoutes, the code is as follows:
/**
 * @param routes 异步加载的路由
 * @param roles  用户的角色,数组形式
 */
// 路由过滤
export function filterAsyncRoutes(routes, roles) {
    
    
  const res = []

  // 遍历全部的路由
  routes.forEach(route => {
    
    
    // 对路由进行浅拷贝,注意 children 不会拷贝,因为不需要对 children 进行判断,所有可以直接使用
    const tmp = {
    
     ...route }
    // 检查用户角色是否具备访问路由的权限
    if (hasPermission(roles, tmp)) {
    
    
      // 当路由具备访问的权限时,判断路由是否具备 children 属性
      if (tmp.children) {
    
    
        // 当路由包含 children 时,对 children 迭代调用 filterAsyncRoutes 方法
        tmp.children = filterAsyncRoutes(tmp.children, roles)
      }
      // 当路由具有访问权限时,将 tmp 保存到 res 中
      res.push(tmp)
    }
  })

  return res
}
  1. The method of checking permissions hasPermission, the code is as follows:
// 检查权限的方法
function hasPermission(roles, route) {
    
    
  // 检查路由是否包含 meta 和 meta.roles 属性
  if (route.meta && route.meta.roles) {
    
    
    // 判断 route.meta.roles 中是否包含用户角色 roles 中的任何一个权限,如果包含则返回 true
    return roles.some(role => route.meta.roles.includes(role))
  } else {
    
    
    // 如果路由没有 meta 或 meta.roles 属性,则视为该路由不需要进行权限控制,所有用户对该路由可访问
    return true
  }
}
  1. The complete core code is as follows:
    premission.js
import {
    
     asyncRoutes, constantRoutes } from '@/router'

// 检查权限的方法
function hasPermission(roles, route) {
    
    
  // 检查路由是否包含 meta 和 meta.roles 属性
  if (route.meta && route.meta.roles) {
    
    
    // 判断 route.meta.roles 中是否包含用户角色 roles 中的任何一个权限,如果包含则返回 true
    return roles.some(role => route.meta.roles.includes(role))
  } else {
    
    
    // 如果路由没有 meta 或 meta.roles 属性,则视为该路由不需要进行权限控制,所有用户对该路由可访问
    return true
  }
}


// 路由过滤
export function filterAsyncRoutes(routes, roles) {
    
    
  const res = []

  // 遍历全部的路由
  routes.forEach(route => {
    
    
    // 对路由进行浅拷贝,注意 children 不会拷贝,因为不需要对 children 进行判断,所有可以直接使用
    const tmp = {
    
     ...route }
    // 检查用户角色是否具备访问路由的权限
    if (hasPermission(roles, tmp)) {
    
    
      // 当路由具备访问的权限时,判断路由是否具备 children 属性
      if (tmp.children) {
    
    
        // 当路由包含 children 时,对 children 迭代调用 filterAsyncRoutes 方法
        tmp.children = filterAsyncRoutes(tmp.children, roles)
      }
      // 当路由具有访问权限时,将 tmp 保存到 res 中
      res.push(tmp)
    }
  })

  return res
}

const state = {
    
    
  routes: [],
  addRoutes: []
}

const mutations = {
    
    
  SET_ROUTES: (state, routes) => {
    
    
    // 将 routes 保存到 state 中的 addRoutes
    state.addRoutes = routes
    // 将 routes 集成到 src/router/index.js 中的 constantRoutes 中
    state.routes = constantRoutes.concat(routes)
  }
}

const actions = {
    
    
  // 生成动态路由的关键方法
  generateRoutes({
    
     commit }, roles) {
    
    
    // 返回 Promise 对象
    return new Promise(resolve => {
    
    
      let accessedRoutes
      if (roles.includes('admin')) {
    
    
        // 如果角色中包含 admin,则直接跳过判断,直接将 asyncRoutes 全部返回
        accessedRoutes = asyncRoutes || []
      } else {
    
    
        // 如果角色中不包含 admin,则调用 filterAsyncRoutes 过滤路由
        accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
      }
      // 将路由保存到 vuex 中
      commit('SET_ROUTES', accessedRoutes)
      resolve(accessedRoutes)
    })
  }
}

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

user.js

import {
    
     login, logout, getInfo } from '@/api/user'
import {
    
     getToken, setToken, removeToken } from '@/utils/auth'
import router, {
    
     resetRouter } from '@/router'

const state = {
    
    
  token: getToken(),
  name: '',
  avatar: '',
  introduction: '',
  roles: []
}

const mutations = {
    
    
  SET_TOKEN: (state, token) => {
    
    
    state.token = token
  },
  SET_INTRODUCTION: (state, introduction) => {
    
    
    state.introduction = introduction
  },
  SET_NAME: (state, name) => {
    
    
    state.name = name
  },
  SET_AVATAR: (state, avatar) => {
    
    
    state.avatar = avatar
  },
  SET_ROLES: (state, roles) => {
    
    
    state.roles = roles
  }
}

const actions = {
    
    
  login({
    
     commit }, userInfo) {
    
    
    const {
    
     username, password } = userInfo
    return new Promise((resolve, reject) => {
    
    
      login({
    
     username: username.trim(), password: password }).then(response => {
    
    
        const {
    
     data } = response
        commit('SET_TOKEN', data.token)
        setToken(data.token)
        resolve()
      }).catch(error => {
    
    
        reject(error)
      })
    })
  },

  getInfo({
    
     commit, state }) {
    
    
    return new Promise((resolve, reject) => {
    
    
      getInfo(state.token).then(response => {
    
    
        const {
    
     data } = response

        if (!data) {
    
    
          reject('Verification failed, please Login again.')
        }

        const {
    
     roles, name, avatar, introduction } = data

        if (!roles || roles.length <= 0) {
    
    
          reject('getInfo: roles must be a non-null array!')
        }

        commit('SET_ROLES', roles)
        commit('SET_NAME', name)
        commit('SET_AVATAR', avatar)
        commit('SET_INTRODUCTION', introduction)
        resolve(data)
      }).catch(error => {
    
    
        reject(error)
      })
    })
  },

  logout({
    
     commit, state, dispatch }) {
    
    
    return new Promise((resolve, reject) => {
    
    
      logout(state.token).then(() => {
    
    
        commit('SET_TOKEN', '')
        commit('SET_ROLES', [])
        removeToken()
        resetRouter()

        dispatch('tagsView/delAllViews', null, {
    
     root: true })

        resolve()
      }).catch(error => {
    
    
        reject(error)
      })
    })
  },

  resetToken({
    
     commit }) {
    
    
    return new Promise(resolve => {
    
    
      commit('SET_TOKEN', '')
      commit('SET_ROLES', [])
      removeToken()
      resolve()
    })
  },

  async changeRoles({
    
     commit, dispatch }, role) {
    
    
    const token = role + '-token'

    commit('SET_TOKEN', token)
    setToken(token)

    const {
    
     roles } = await dispatch('getInfo')

    resetRouter()

    const accessRoutes = await dispatch('permission/generateRoutes', roles, {
    
     root: true })
    router.addRoutes(accessRoutes)

    dispatch('tagsView/delAllViews', null, {
    
     root: true })
  }
}

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

Guess you like

Origin blog.csdn.net/weixin_42614080/article/details/107829515