1.修改路由导航守卫,判断何时获取路由数据,并处理获取数据的js,阻止通过网址想直接进入后台进行权限管理
路由导航守卫文件在scr/permission.js中,下面是修改好之后的代码
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken, getNickName } from '@/utils/auth' // get token from cookie
import { resetRouter } from '@/router'
import getPageTitle from '@/utils/get-page-title'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/login', '/auth-redirect'] // no redirect whitelist
router.beforeEach(async(to, from, next) => {
// start progress bar
NProgress.start()
// set page title
document.title = getPageTitle(to.meta.title)
// determine whether the user has logged in
const hasToken = getToken() //获取登录token
const nickName = getNickName(); //获取刚才在模块页使用cookie保存的模块名称
// 判断是否有token
if (hasToken) {
// 判断当前路径是否为登录页
if (to.path === '/login') {
// 进入模块页
next({ path: '/cache' })
NProgress.done() // hack: https://github.com/PanJiaChen/vue-element-admin/pull/2939
// 判断当前是否为根路径
} else if (to.path === '/') {
//进入模块页
next({ path: '/cache' })
// 判断当前是否为模块页
} else if (to.path === '/cache') {
// 清除cookie保存的模块名称
await store.dispatch("user/cache");
next();
// 判断当前是否为重定项页,首页
} else if (to.path === '/dashboard') {
resetRouter() //重置路由
if (nickName == undefined) { //判断名称是否为空
//为空直接跳回模块页
next({ path: '/cache' });
NProgress.done()
} else {
// 获取路由
const { roles } = await store.dispatch('user/getInfo')
const accessRoutes = await store.dispatch('permission/generateRoutes', { roles, nickName });
//动态添加
router.addRoutes(accessRoutes);
next();
}
// 解决动态页面,强制刷新,路径丢失,和退出登录,重新登录,路径丢失问题
} else if (to.path != '/dashboard') {
// 先放行
next();
// 判断动态路由是否有数据,没有重新动态添加
if (store.getters.permission_routes.length == 0) {
const { roles } = await store.dispatch('user/getInfo')
const accessRoutes = await store.dispatch('permission/generateRoutes', { roles, nickName });
router.addRoutes(accessRoutes)
next();
// 名称为空,直接跳转到模块页面
} else if (nickName == undefined) {
next({ path: '/cache' });
NProgress.done()
}
} else {
const hasRoles = store.getters.roles && store.getters.roles.length > 0
if (hasRoles) {
next()
} else {
try {
// get user info
// 注意:角色必须是对象数组!例如:['admin']或,['developer','editor']
const { roles } = await store.dispatch('user/getInfo')
const accessRoutes = await store.dispatch('permission/generateRoutes', { roles, nickName })
router.addRoutes(accessRoutes);
// 黑客方法,以确保addRoutes是完整的
// 设置replace:true,这样导航就不会留下历史记录
next({...to, replace: true })
} catch (error) { // 获取用户信息失败,进入以下一级
await store.dispatch('user/resetToken') // 获取用户信息失败后,就删除token
Message.error(error || 'Has Error') // 提示相应的错误
//next(`/login?redirect=${to.path}`) // 并跳转回登录界面,重新登录
next('login')
NProgress.done()
}
}
}
} else {
/* 没有令牌*/
if (whiteList.indexOf(to.path) !== -1) {
// 在免费登录白名单中,直接进入
next()
} else {
// 其他没有访问权限的页面将被重定向到登录页面。
// next(`/login?redirect=${to.path}`)
next('/login')
NProgress.done()
}
}
})
router.afterEach(() => {
// 完成进度条
NProgress.done()
})
判断当前路径为/cache时删除cookie中保存的模块名称,不然通过网址进入后台管理页面时就会出现导航守卫死循环bug,其中await store.dispatch(‘user/cache’);是使用vuex中的方法,这个方法在上一篇博客01---vue+element+admin将本地路由修改为动态路由_天尘不打野的博客-CSDN博客中的src/store/modules/user.js文件中已经定义了,这里就不贴代码了
判断当前路径为/dashboard,并且模块名称不能为空时,获取路由数据,并动态添加进去。其中await store.dispatch(‘permission/generateRoutes’, nickName);是使用vuex中的方法,给permission.js中的generateRoutes,传递一个模块名称。
2.在src/store/modules/permission.js文件中定义generateRoutes方法
定义方法之前先将scr/router/modules中的四个路由文件里面的路由数据改为数组对象形式,因为动态路由需要数组对象形式,否则报错
修改src/store/modules/permission.js文件
import { asyncRoutes, constantRoutes } from '@/router'
import Layout from '@/layout'
// 引入刚才改好的四个路由文件
import componentsRouter from '@/router/modules/components'
import chartsRouter from '@/router/modules/charts'
import tableRouter from '@/router/modules/table'
import nestedRouter from '@/router/modules/nested'
/**
* Use meta.role to determine if the current user has permission
* @param roles
* @param route
*/
function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
}
/**
* Filter asynchronous routing tables by recursion
* @param routes asyncRoutes
* @param roles
*/
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = {...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
const state = {
routes: [],
addRoutes: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
// 懒加载
export const loadView = (view) => {
return (resolve) => require([`@/views${view}`], resolve)
}
/**
*
* @param { * 后台查询的菜单数据拼装成路由格式的数据
* @param routes} routes
* @param {*} data
*/
export function generaMenu(routes, data) {
data.forEach(item => {
const menu = {
path: item.path === '#' ? item.menuId + '_key' : item.path,
// component: item.component === '#' ? Layout : loadView(item.component),
/**
* 因为引入的路由文件里已经做了路由懒加载,这里就直接赋值就行
* 如果是调用后台接口获取的数组对象,把下面的代码注释,上面的代码解开就可以了
*/
component: item.component,
hidden: item.hidden,
children: [],
//在4.4.0中路由父节点允许name属性存在值否则出现VUE之Router命令行警告:Named Route 'Home' has a default child route 警告
name: item.component === '#' ? "" : item.name,
meta: item.meta
}
if (item.children) {
generaMenu(menu.children, item.children)
}
// 当存在单路由时要将children为空的属性删除否则无法在左侧菜单中展示独立的路由菜单
if (menu.children.length <= 0) delete menu.children
routes.push(menu)
})
}
/**
* 删除子节点children 为空数组
* @param {*} data
*/
export function deleteChildren(data) {
let childs = data
for (let i = childs.length; i--; i > 0) {
//if(childs[i].meta != null){
// childs[i].meta = (childs[i].meta).replace("\"","").replace("\"","");
//}
if (childs[i].children) {
if (childs[i].children.length) {
deleteChildren(childs[i].children)
} else {
delete childs[i].children
}
}
}
return data
}
const actions = {
// 自己封装一个js
//这个就为刚才在导航守卫,条件判断通过后,调用的获取数据的方法
generateRoutes({ commit }, obj) {
return new Promise(resolve => {
const loadMenuData = [];
let response = [];
// 在这里可以调取后端接口,获取动态路由数组对象并把数据添加到路由
if (obj.nickName === "组件系统") {
response = deleteChildren(componentsRouter);
} else if (obj.nickName === "图表系统") {
response = deleteChildren(chartsRouter);
} else if (obj.nickName === "表格系统") {
response = deleteChildren(tableRouter);
} else if (obj.nickName === "多级路由") {
response = deleteChildren(nestedRouter);
}
Object.assign(loadMenuData, response)
const tempAsyncRoutes = Object.assign([], asyncRoutes)
generaMenu(tempAsyncRoutes, loadMenuData)
let accessedRoutes
if (obj.roles.includes('admin')) {
accessedRoutes = tempAsyncRoutes || []
} else {
accessedRoutes = filterAsyncRoutes(tempAsyncRoutes, obj.roles)
}
commit('SET_ROUTES', accessedRoutes);
//抛出路由,导航守卫获取数据要使用
resolve(accessedRoutes)
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
注:因为内容太多,后面的内容在我另外的几个博客里: