1、做法
在项目接手时候想的还是根据花裤衩大佬的方法进行,后端返回异步路由进行加载。后来和后端一起确认并且查阅了其他项目做法后,采用的是所有路由在前端的异步路由中进行对比查找赋值,再通过动态路由添加。
2、思路
在用户进行登录时候,在路由守卫中调用后端接口返回该用户的相关权限列表,权限按钮、权限名称。再根据相关权限进行查找赋与addrouters
3、判断逻辑:
所有的都在路由守卫中进行,不存储在本地缓存中
3.1首先判断有无token,
如果不存在token 判断是不是在白名单中 如果在白名单中就放行,不然就跳转到登录页
3.2如果存在token,判断有无权限名称。
存在权限名 放行(这一步是在用户登录成功后的所有逻辑)
不存在权限名称,进行查询,调用vuex中的获取用户权限登录数据
查询到数据后,将权限名称、菜单列表、按钮列表存入vuex中
因为有一个管理员,如果他的权限名称为管理员,赋予本地的全部asyncRouters,不进行判断。
如果是其他权限名称。就将拿到的菜单列表与异步路由进行对比赋值,再添加到动态路由中
4、代码
// 导航守卫
router.beforeEach(async (to, from, next) => {// document.title = getTitle(to.meta.title)//如果去登录页进行跳转if (to.path === '/login') {// 如果login 页面没有重定向 手动重定向if (!Object.keys(to.query).length) {next({path: '/login',query: {redirect: '/'}})} else {next()}} else {//查询有没有tokenif (store.getters.token) {const roleName = store.getters.roleName//这里判断是否有一级菜单导航//判断有无权限名 有就放行if (roleName) {next()} else {// 没有权限名进行api调用,获取当前用户所拥有权限try {//try执行一些可能会抛出的错误// 这是一个错误处理机制,用于捕获 JavaScript 代码中的错误并执行相应的处理程序,catch() 方法接受一个函数作为参数,该函数在发生错误时被调用并传递错误消息作为参数// 将接口查询到的东西都存入vuex中const data = await store.dispatch('user/_getInfo')const menuList = store.getters.menuList// 将获取到的菜单列表与本地的路由进行对比赛选const addRoutes = await store.dispatch('permission/getAsyncRoutes',menuList)router.addRoutes(addRoutes)next({ ...to, replace: true })} catch (error) {// 抛出错误if (error) {Message.error(error)}}}} else if (whiteList.indexOf(to.path) !== -1) {next()} else {next({path: '/login',query: {redirect: to.fullPath}})}}
})
user的vuex
//获取后端接口得到用户登录权限数据_getInfo({ commit }) {return new Promise((resolve, reject) => {getInfo().then(res => {if (res.data) {// 如果查询成功了跳转到主页router.push('/home')//结构权限名与菜单列表、按钮列表const { roleName, menuList, permissionList } = res.data//2将用户数据储存到vuex里面用于使用时取值commit('SET_ROLE_NAME', roleName)commit('SET_MENU_LIST', menuList)commit('SET_PERMISSION_LIST', permissionList)} else {Message.error(res.msg)}resolve(res.data)}).catch(error => {if (error.msg) {Message.error(error.msg)}reject(error)})})}
SET_ROLE_NAME(state, payload) {state.roleName = payload},SET_INTRODUCE(state, payload) {state.introduce = payload},SET_MENU_LIST(state, payload) {state.menuList = payload},SET_PERMISSION_LIST(state, payload) {state.permissionList = payload}
最重要的路由处理因为后端返回的都是权限名称,所以用不到树状结构数据
import { asyncRoutes, currencyRoutes } from '@/router'const state = {routes: [],addRoutes: []
}
const mutations = {SET_ROUTES(state, payload) {// 存入整合后的所有路由方便后续使用state.routes = [...currencyRoutes, ...payload]// 需要动态路由添加的路由state.addRoutes = payload}
}
//遍历asyncRoutes动态路由
function forSearchArr(route, routerName) {let arrNew = []for (let item of route) {// 将其复制到一个新的对象中,从而实现对象的浅拷贝(只拷贝对象中的属性,而不包括属性所引用的对象)let itemNew = { ...item } // includes() 是 JavaScript 中字符串、数组和类数组对象内置的方法之一,用于判断一个对象中是否包含指定的元素。// 对于字符串和数组对象,该方法会遍历整个对象,判断其中是否包含指定的元素,返回一个布尔值用于表示结果。// 对于类数组对象,该方法会将其转换为数组对象后再进行比较if (routerName.includes(itemNew.name)) {// 如果存在子元素,进行递归调用if (itemNew.children) {itemNew.children = forSearchArr(itemNew.children, routerName)}arrNew.push(itemNew)}}return arrNew
}const actions = {getAsyncRoutes({ commit, rootGetters }, menuList) {return new Promise(resolve => {let routes = []// 管理员拥有所有的路由,菜单权限if (rootGetters.roleName === '管理员') {routes = asyncRoutes || ''} else {// 不是管理员,进行异步路由和获取的菜单列表进行对比重组routes = forSearchArr(asyncRoutes, menuList)}commit('SET_ROUTES', routes)resolve(routes)})}
}
export default {namespaced: true,state,mutations,actions
}