You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

149 lines
4.5 KiB

/*
* 路由配置
*
* 需要鉴权的路由:!meta.noAuth
* 左侧菜单显示:name && !meta.hidden && meta.title
* 左侧菜单排序:能在左侧菜单中显示 && meta.sort,升序排列
* 左侧菜单不折叠只有一个children的路由:meta.alwaysShow
* 面包屑显示:meta.title || meta.dynamicTitle
* tab栏显示:meta.title || meta.dynamicTitle
* tab栏固定显示:meta.title && meta.affix
* 页面不缓存:!name && !meta.usePathKey && !meta.useFullPathKey || meta.noCache
* 打开iframe:meta.iframe,不会重复打开相同src的iframe
* */
import Vue from 'vue'
import Router from 'vue-router'
import {pathToRegexp} from 'path-to-regexp'
import NProgress from 'nprogress'
import {contextPath, route as routeConfig, title} from '@/config'
import defaultRoutes from '@/router/define'
import store from "@/store"
import {stringifyRoutes, parseRoutes, generateRoutes} from './util'
import {isEmpty} from "@/util"
import {auth, needAuth} from "@/util/auth"
import {isUserExist} from "@/util/storage"
Vue.use(Router)
NProgress.configure({showSpinner: false})
const whiteList = transformWhiteList(['/login', '/register', '/404', '/403'])
const router = new Router({
base: contextPath,
mode: routeConfig.mode,
scrollBehavior: () => ({y: 0}),
routes: defaultRoutes
})
router.beforeEach(async (to, from, next) => {
//使用redirect进行跳转时不显示进度条
!to.path.startsWith('/redirect') && NProgress.start()
if (needExtraRedirect(to, from)) {
//这里vue-router会报redirect错误,已在main.js中忽略
return next(`/redirect${to.fullPath}`)
}
specifyRouteTitle(to, from)
setPageTitle(to)
//白名单内不需要进行权限控制
if (whiteList.some(reg => reg.test(to.path))) {
return next()
}
//未登录时返回登录页
if (!isUserExist()) {
return next({path: '/login', query: {redirect: to.fullPath}})
}
setActiveRootMenu(to)
//初始化路由和菜单权限
if (!store.state.resource.init) {
await store.dispatch('resource/init', {...store.state.user, addRoutes: true})
return next({...to, replace: true})
}
//页面不需要鉴权或有访问权限时通过
if (!needAuth(to) || auth(getAuthorizedPath(to))) {
iframeControl(to, from)
return next()
}
//用户无权限访问时的动作
next('/403')
})
router.afterEach(() => NProgress.done())
//将给定的白名单url转换为正则
function transformWhiteList(list) {
return list.map(pathToRegexp)
}
//拼接页面标题
function setPageTitle(route) {
const pageTitle = route.meta.title
document.title = pageTitle ? `${pageTitle} - ${title}` : title
}
//确定路由的标题
function specifyRouteTitle(to, from) {
const {meta} = to
if (typeof meta.dynamicTitle === 'function') {
meta.title = meta.dynamicTitle(to, from)
}
}
//判断是否需要一次额外的redirect跳转
function needExtraRedirect(to, from) {
//若是共用组件的路由页面之间的跳转,借助redirect避免组件复用
const a = to.meta.commonModule, b = from.meta.commonModule
return !isEmpty(a) && a === b
}
//设置顶部菜单的激活状态
function setActiveRootMenu(to) {
const {path, matched} = to
//使用/redirect跳转 或 无匹配路由 时跳过
if (path.startsWith('/redirect') || matched.length === 0) {
return
}
store.commit('resource/activeRootMenu', matched[0].path || '/')
}
//获取进行权限验证的路由地址,使用了动态路由的会用到
function getAuthorizedPath(route) {
const {params, path, matched} = route
const isDynamic = Object.values(params).some(v => path.includes(v))
return isDynamic ? matched[matched.length - 1].path : path
}
//判断是否需要打开iframe
function iframeControl(to, from) {
//从iframe页面离开时,判断是否需要删除iframe
if (from.meta.iframe) {
let del = false
//如果设置了无缓存或是进行了刷新,那么移除iframe
if (from.meta.noCache || to.path === `/redirect${from.path}`) {
del = true
}
store.dispatch(`iframe/close`, {src: from.meta.iframe, del})
}
//跳转至iframe页面时,打开iframe
if (to.meta.iframe) {
store.dispatch(`iframe/open`, {src: to.meta.iframe})
}
}
export default router
export function addDynamicRoutes(json) {
json = parseRoutes(stringifyRoutes(json))
const endRoute = {path: '*', redirect: '/404'}
router.addRoutes([...generateRoutes(json), endRoute])
}