一、引言
动态路由,很多场景是,有些页面必须是某些用户或者某些角色才能访问,所以呢,前端的代码即使不给页面的入口,但是要是直接在地址栏敲入相关地址页面还是可以被访问到。为了杜绝这种情况,就用到了动态路由,没有权限的页面直接在路由里不注册,及时输入地址也将无效。路由信息这东西有点大,放到以上说的本地存储方式里,貌似都不合适
这个动态的配置呢主要是通过vue-router的addRoutes方法。但add的内容呢,是前端去动态配置呢,还是根据后台的接口返回呢。主要看具体使用情况,不过还是后台控制稳妥一些。这种动态的情况,实现权限控制。
一、router的beforeEach
import { get, isEmpty, replace } from 'lodash'const getCurrentUri = base => { // 获取当前地址let protocol = get(location, 'protocol')let host = get(location, 'host')let href = get(location, 'href')return replace(href, `${protocol}//${host}${base}`, '/')
}export const initRouterGuard = (router, store) => {const { dispatch, getters } = storerouter.beforeEach((to, from, next) => {console.log('\n====== In router.beforeEach ======')let {isLoggedin,menuList,activeMenu} = get(getters, 'user')// router匹配的是除设定base之外的路由,获取当前let uri = getCurrentUri(get(router, 'options.base'))console.log(`At path: ${uri}, From ${from.path} to ${to.path}`)// home为登录页,isLoggedin是一个持久化的数据,如果没有登录,访问非登录页,则跳转至登录页if (!isLoggedin && to.path !== '/home') {next('/home')// 如果登录了,但没有拿到动态路由信息,则取获取路由信息,该操作为刷新} else if (isEmpty(menuList) && isLoggedin) {console.log('Before dispatch "setMenu"')dispatch('setMenu').then(() => { // setMenu一个请求let matched = router.matcher.match(uri) // 拿到权限路由后,看当前路由是否在其中if (activeMenu && matched.name && matched.name !== 'Home') {console.log(`Current uri ${uri} get matched route`)next({ replace: true, path: uri }) // 如果有就跳转} else {matched = router.matcher.match('/dashboard')next({ replace: true, path: '/dashboard' }) // 如果没有就回到主页}matched.meta.showMenu && dispatch('switchActiveMenu', matched.name)})} else {console.log('Before default next()')to.meta.showMenu && dispatch('switchActiveMenu', to.name)next()}})
}
二、如果没有统一主页
import { get, replace, isEmpty } from 'lodash'const locationPrefix = process.env.ROUTE_BASEconst getCurrentUri = () => {let protocol = get(location, 'protocol')let host = get(location, 'host')let href = get(location, 'href')console.log('getCurrentUri', href, `${protocol}//${host}${locationPrefix}`)return replace(href, `${protocol}//${host}${locationPrefix}`, '')
}export const initRouterGuard = (router, store) => {const { dispatch, getters } = storerouter.beforeEach((to, from, next) => {console.log('to', to.path, 'from', from.path)// 首先从vuex store中获取当前用户的登录状态、菜单列表、当前激活的菜单const { isLoggedin, menuList } = get(getters, 'user')// 特殊处理,手机浏览器内核会导致isLoggedin为null而且cookie也拿不到let pendingUri = getCurrentUri()const matchedRoute = router.matcher.match(to.path)// const needRedirect = includes(pendingUri, '/detail') && !get(matchedRoute, name)console.log('matchedRoute', get(matchedRoute, name), pendingUri)// 如果用户没登录,而且即将去的页面不是登录页面,则直接重定向到登录页面if (!isLoggedin && to.name !== 'Login' && !(to.name === 'resetPwd' && to.params.type === '0')) {return next({ replace: true, name: 'Login' })}// 如果用户登录了, 但是菜单数据是空的,说明是刷新页面,需要去一下index做跳转if (isLoggedin && isEmpty(menuList) && !['Index'].includes(matchedRoute.name)) {if ((pendingUri === '/login') || (pendingUri === '/index')) {pendingUri = '/user/list'}dispatch('setPendingUri', pendingUri) // 将要去的页面存储起来return next({ replace: true, path: '/index' })}dispatch('switchActiveMenu', to.name)next()})
}
三、虚拟公共主页
<template><div>主页</div>
</template><script>
import { mapGetters, mapActions } from 'vuex'export default {name: 'Index',computed: {...mapGetters(['user','common'])},mounted () {this.loadEntries()},methods: {...mapActions(['setMenu']),async loadEntries () {try {if (this._isEmpty(this.user.menuList)) {await this.setMenu()// Redirect to pending uriconst matched = this.common.pendingUri ? this.$router.match(this.common.pendingUri) : nullconst matchedName = this._get(matched, 'name')console.log('loadEntries', this.common.pendingUri, this.$route.name, matchedName)if (this.$route.name !== matchedName && matchedName && matchedName !== 'Login') {this.$router.push(matched)}}} catch (error) {// this.$toast(error.message)}}}
}
</script>
这样就实现了一个动态路由的流转及路由的权限控制~