我们在日常开发中,经常会遇到项目需要权限管理的,当项目越来越大的时候,我们还是非常需要做动态路由这一块的,现在我们一起来了解一下如果制作vue的动态路由;
1.基础路由的配置
// router.config.js
import { BasicLayout } from '@/layouts'
import { bxAnaalyse } from '@/core/icons'
/**
* 基础路由
* @type { *[] }
*/
export const constantRouterMap = [
{
path: '/user',
component: BlankLayout,
redirect: '/user/login',
hidden: true,
children: [
{
path: 'login',
name: 'login',
component: () => import(/* webpackChunkName: "user" */ '@/views/user/Login')
}
]
},
{
path: '/404',
component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/404')
}
]
2.实现路由的主体,动态路由做配置
// router index.js
import Vue from 'vue'
import Router from 'vue-router'
import { constantRouterMap } from '@/config/router.config'
// hack router push callback
// 重写vue-router的push方法 避免相同路由报错
const originalPush = Router.prototype.push
Router.prototype.push = function push (location, onResolve, onReject) {
if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)
return originalPush.call(this, location).catch(err => err)
}
Vue.use(Router)
/**
* 防止 指向路由重复 报错问题
* reset-router 文件中 门户切换 重定向到首页(如果当前是在首页中切换就会出现router报错,指向路由重复错误, 使用下面解决函数解决)
*/
const RouterReplace = Router.prototype.replace
Router.prototype.replace = function replace (location) {
return RouterReplace.call(this, location).catch(err => err)
}
/*
* 创建路由实例子函数 用于动态路由重置
*/
const createRouter = () => {
return new Router({
mode: 'history',
base: process.env.BASE_URL,
scrollBehavior: () => ({ y: 0 }),
routes: constantRouterMap // 基础的路由 比如 登录页面 404页面等
})
}
/**
* 初始化路由
*/
const router = createRouter()
/**
* 重置路由matcher 函数, 避免相同路由重复警告
* 切换不同角色时 重置对应路由时使用
*/
const resetRouter = () => {
const newRouter = createRouter()
router && (router.matcher = newRouter.matcher)
}
export { resetRouter }
export default router
3.路由请求与存储
/**
* 向后端请求用户路由,动态生成路由
*/
import { constantRouterMap } from '@/config/router.config'
import { generatorDynamicRouter } from '@/router/generator-routers'
const permission = {
state: {
routers: constantRouterMap,
addRouters: []
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers
state.routers = constantRouterMap.concat(routers)
}
},
actions: {
GenerateRoutes ({ commit }, data) {
return new Promise(resolve => {
const { portalId } = data
generatorDynamicRouter(portalId).then(routers => {
console.log(routers)
commit('SET_ROUTERS', routers)
resolve()
})
})
}
}
}
export default permission
4.创建路由守卫
// permission.js
import Vue from 'vue'
import router from './router'
import store from './store'
import i18n from './common/index'
import Cookies from 'js-cookie'
import NProgress from 'nprogress' // progress bar
import '@/components/NProgress/nprogress.less' // progress bar custom style
import notification from 'ant-design-vue/es/notification'
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
import { ACCESS_TOKEN, TOKEN, PORTAL_ID, TO_DO, SIAMJWT, USER_INFO } from '@/store/mutation-types'
import { idmLogin } from '@/api/login'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['login', 'register', 'registerResult'] // no redirect whitelist
const defaultRoutePath = '/'
const isProd = process.env.NODE_ENV === 'production' && process.env.VUE_APP_PREVIEW === 'false'
router.beforeEach(async (to, from, next) => {
NProgress.start() // start progress bar
to.meta && (typeof to.meta.title !== 'undefined' && setDocumentTitle(`${to.meta.isI18n ? i18n.t(to.meta.title) : to.meta.title} - ${i18n.t(domTitle)}`)) // 设置页面title
if (Vue.ls.get(ACCESS_TOKEN)) { // 判断是否登录
/* has token */
if (to.path === '/user/login') {
next({ path: defaultRoutePath }) // 跳转到首页
NProgress.done()
} else {
// 是否有权限
if (store.getters.roles.length === 0) {
store
.dispatch('GetInfo')
.then(res => {
const roles = res && res.role
const portalId = Vue.ls.get(PORTAL_ID)
store.dispatch('GenerateRoutes', { roles, portalId }).then(() => {
// 根据roles权限生成可访问的路由表
// 动态添加可访问路由表
router.addRoutes(store.getters.addRouters)
// 请求带有 redirect 重定向时,登录自动重定向到该地址
const redirect = decodeURIComponent(from.query.redirect || to.path)
if (to.path === redirect) {
// hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
next({ ...to, replace: true })
} else {
// 跳转到目的路由
next({ path: redirect })
}
})
})
.catch(() => {
notification.error({
key: 'loginPageError',
message: i18n.t('basicLayout.error'),
description: i18n.t('basicLayout.requestUserInfoError')
})
})
} else {
if (to.matched.length === 0) {
next({ path: '/404' })
}
next()
}
}
} else {
if (whiteList.includes(to.name)) {
// 在免登录白名单,直接进入
next()
} else {
next({ path: '/user/login', query: { redirect: to.fullPath } })
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
}
}
})
router.afterEach(() => {
NProgress.done() // finish progress bar
})
5.在mian.js中引入permission.js路由守卫文件
⚠ vue是单页面应用程序,所以页面一刷新数据部分数据也会跟着丢失,所以我们需要将store中的数据存储到本地,我们项目中是vue-ls 来做存储,这里可以去找一些符合自己项目的插件或者自己编写存储库