1. Analysis of dynamic routing
- The generation logic of dynamic routing is as follows:
- From
@router
readingasyncRoutes
andconstantRoutes
acquiring user rolesroles
, determineroles
whether to includeadmin
. - If
roles
it is containingadmin
the filteredasyncRoutes
saved tovuex
the. - If
roles
not includedadmin
, then traversedroutes
to determine whether the access routes. If not, continue to traverseroutes
. If so, determine whether the route is includedchildren
. If includedchildren
, traversechildren
, filterchildren
, updatetmp.children
, and then pass in to determine whether the route is includedchildren
. If it does notchildren
, the route intores
the filteredasyncRoutes
saved tovuex
the. - The filtered
asyncRoutes
saved tovuex
the middle,asyncRoutes
andconstantRoutes
merge.
Second, the realization of dynamic routing
- The key is a method of generating a dynamic routing
premission.js
in thegenerateRoutes
method, 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)
})
}
}
mutations
InSET_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)
}
}
- 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
}
- 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
}
}
- 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
}