mirror of
https://github.com/chansee97/nova-admin.git
synced 2025-05-22 09:33:30 +08:00
perf: router system
This commit is contained in:
parent
1a2635af58
commit
35b4be9cfb
73
src/router/guard.ts
Normal file
73
src/router/guard.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import type { Router } from 'vue-router'
|
||||
import { useRouteStore, useTabStore } from '@/store'
|
||||
import { local } from '@/utils'
|
||||
|
||||
const title = import.meta.env.VITE_APP_NAME
|
||||
|
||||
export function setupRouterGuard(router: Router) {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
// 判断是否是外链,如果是直接打开网页并拦截跳转
|
||||
if (to.meta.herf) {
|
||||
window.open(to.meta.herf)
|
||||
return false
|
||||
}
|
||||
// 开始 loadingBar
|
||||
window.$loadingBar?.start()
|
||||
|
||||
// 权限操作
|
||||
const routeStore = useRouteStore()
|
||||
// 判断有无TOKEN,登录鉴权
|
||||
const isLogin = Boolean(local.get('token'))
|
||||
if (!isLogin) {
|
||||
if (to.name === 'login')
|
||||
next()
|
||||
|
||||
if (to.name !== 'login') {
|
||||
const redirect = to.name === '404' ? undefined : to.fullPath
|
||||
next({ path: '/login', query: { redirect } })
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 判断路由有无进行初始化
|
||||
if (!routeStore.isInitAuthRoute) {
|
||||
await routeStore.initAuthRoute()
|
||||
// 动态路由加载完回到根路由
|
||||
if (to.name === '404') {
|
||||
// 等待权限路由加载好了,回到之前的路由,否则404
|
||||
next({
|
||||
path: to.fullPath,
|
||||
replace: true,
|
||||
query: to.query,
|
||||
hash: to.hash,
|
||||
})
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 判断当前页是否在login,则定位去首页
|
||||
if (to.name === 'login') {
|
||||
next({ path: '/' })
|
||||
return false
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
router.beforeResolve((to) => {
|
||||
const routeStore = useRouteStore()
|
||||
const tabStore = useTabStore()
|
||||
// 设置菜单高亮
|
||||
routeStore.setActiveMenu(to.meta.activeMenu ?? to.fullPath)
|
||||
// 添加tabs
|
||||
tabStore.addTab(to)
|
||||
// 设置高亮标签;
|
||||
tabStore.setCurrentTab(to.name as string)
|
||||
})
|
||||
|
||||
router.afterEach((to) => {
|
||||
// 修改网页标题
|
||||
document.title = `${to.meta.title} - ${title}`
|
||||
// 结束 loadingBar
|
||||
window.$loadingBar?.finish()
|
||||
})
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import { clone, construct, min } from 'radash'
|
||||
import { BasicLayout } from '@/layouts/index'
|
||||
import { useRouteStore } from '@/store'
|
||||
import { usePermission } from '@/hooks'
|
||||
import { arrayToTree } from '@/utils'
|
||||
|
||||
// 引入所有页面
|
||||
const modules = import.meta.glob('../../views/**/*.vue')
|
||||
|
||||
/* 含有子级的路由重定向到第一个子级 */
|
||||
function setRedirect(routes: AppRoute.Route[]) {
|
||||
routes.forEach((route) => {
|
||||
if (route.children) {
|
||||
if (!route.redirect) {
|
||||
// 过滤出没有隐藏的子元素集
|
||||
const visibleChilds = route.children.filter(child => !child.meta.hide)
|
||||
|
||||
// 过滤出含有order属性的页面
|
||||
const orderChilds = visibleChilds.filter(child => child.meta.order)
|
||||
|
||||
// 重定向页默认第一个子元素的路径
|
||||
let target = route.children[0]
|
||||
if (orderChilds.length > 0)
|
||||
// 有order则取最小者重定向
|
||||
target = min(orderChilds, i => i.meta.order as number) as AppRoute.Route
|
||||
|
||||
route.redirect = target.path
|
||||
}
|
||||
|
||||
setRedirect(route.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
export function createDynamicRoutes(routes: AppRoute.RowRoute[]) {
|
||||
const { hasPermission } = usePermission()
|
||||
// 结构化meta字段
|
||||
let resultRouter = clone(routes).map(i => construct(i)) as AppRoute.Route[]
|
||||
// 路由权限过滤
|
||||
resultRouter = resultRouter.filter(i => hasPermission(i.meta.roles))
|
||||
|
||||
// 生成需要keepAlive的路由name数组
|
||||
const routeStore = useRouteStore()
|
||||
routeStore.cacheRoutes = resultRouter.filter((i) => {
|
||||
return i.meta.keepAlive
|
||||
})
|
||||
.map(i => i.name)
|
||||
|
||||
// 生成路由,有redirect的不需要引入文件
|
||||
resultRouter = resultRouter.map((item: any) => {
|
||||
if (item.componentPath && !item.redirect)
|
||||
item.component = modules[`../../views${item.componentPath}`]
|
||||
return item
|
||||
})
|
||||
|
||||
resultRouter = arrayToTree(resultRouter) as AppRoute.Route[]
|
||||
setRedirect(resultRouter)
|
||||
const appRootRoute: RouteRecordRaw = {
|
||||
path: '/appRoot',
|
||||
name: 'appRoot',
|
||||
redirect: import.meta.env.VITE_HOME_PATH,
|
||||
component: BasicLayout,
|
||||
meta: {
|
||||
title: '首页',
|
||||
icon: 'icon-park-outline:home',
|
||||
},
|
||||
children: [],
|
||||
}
|
||||
// 根据角色过滤后的插入根路由中
|
||||
appRootRoute.children = resultRouter as unknown as RouteRecordRaw[]
|
||||
return appRootRoute
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
import type { Router } from 'vue-router'
|
||||
import { createPermissionGuard } from './permission'
|
||||
import { useRouteStore, useTabStore } from '@/store'
|
||||
|
||||
const title = import.meta.env.VITE_APP_NAME
|
||||
|
||||
export function setupRouterGuard(router: Router) {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
// 判断是否是外链,如果是直接打开网页并拦截跳转
|
||||
if (to.meta.herf) {
|
||||
window.open(to.meta.herf)
|
||||
return false
|
||||
}
|
||||
// 开始 loadingBar
|
||||
window.$loadingBar?.start()
|
||||
// 权限操作
|
||||
await createPermissionGuard(to, from, next)
|
||||
})
|
||||
|
||||
router.beforeResolve((to) => {
|
||||
const routeStore = useRouteStore()
|
||||
const tabStore = useTabStore()
|
||||
// 设置菜单高亮
|
||||
routeStore.setActiveMenu(to.meta.activeMenu ?? to.fullPath)
|
||||
// 添加tabs
|
||||
tabStore.addTab(to)
|
||||
// 设置高亮标签;
|
||||
tabStore.setCurrentTab(to.name as string)
|
||||
})
|
||||
|
||||
router.afterEach((to) => {
|
||||
// 修改网页标题
|
||||
document.title = `${to.meta.title} - ${title}`
|
||||
// 结束 loadingBar
|
||||
window.$loadingBar?.finish()
|
||||
})
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
import type { NavigationGuardNext, RouteLocationNormalized } from 'vue-router'
|
||||
import { local } from '@/utils'
|
||||
import { useRouteStore } from '@/store'
|
||||
|
||||
export async function createPermissionGuard(
|
||||
to: RouteLocationNormalized,
|
||||
from: RouteLocationNormalized,
|
||||
next: NavigationGuardNext,
|
||||
) {
|
||||
const routeStore = useRouteStore()
|
||||
// 判断有无TOKEN,登录鉴权
|
||||
const isLogin = Boolean(local.get('token'))
|
||||
if (!isLogin) {
|
||||
if (to.name === 'login')
|
||||
next()
|
||||
|
||||
if (to.name !== 'login') {
|
||||
const redirect = to.name === '404' ? undefined : to.fullPath
|
||||
next({ path: '/login', query: { redirect } })
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 判断路由有无进行初始化
|
||||
if (!routeStore.isInitAuthRoute) {
|
||||
await routeStore.initAuthRoute()
|
||||
// 动态路由加载完回到根路由
|
||||
if (to.name === '404') {
|
||||
// 等待权限路由加载好了,回到之前的路由,否则404
|
||||
next({
|
||||
path: to.fullPath,
|
||||
replace: true,
|
||||
query: to.query,
|
||||
hash: to.hash,
|
||||
})
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 权限路由已经加载,仍然未找到,重定向到404
|
||||
// 若是从404再次跳转,则跳过判断
|
||||
if (to.name === '404' && to?.redirectedFrom?.name !== '404') {
|
||||
next({ name: '404', replace: true })
|
||||
return false
|
||||
}
|
||||
|
||||
// 判断当前页是否在login,则定位去首页
|
||||
if (to.name === 'login') {
|
||||
next({ path: '/' })
|
||||
return false
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import type { App } from 'vue'
|
||||
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
|
||||
import { routes } from './routes'
|
||||
import { routes } from './routes.inner'
|
||||
import { setupRouterGuard } from './guard'
|
||||
|
||||
const { VITE_ROUTE_MODE = 'hash', VITE_BASE_URL } = import.meta.env
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { useRouteStore } from './route'
|
||||
import { useTabStore } from './tab'
|
||||
import { fetchLogin, fetchUserInfo } from '@/service'
|
||||
import { router } from '@/router'
|
||||
import { local } from '@/utils'
|
||||
@ -34,7 +35,12 @@ export const useAuthStore = defineStore('auth-store', {
|
||||
// 清空路由、菜单等数据
|
||||
const routeStore = useRouteStore()
|
||||
routeStore.resetRouteStore()
|
||||
// 清空标签栏数据
|
||||
const tabStore = useTabStore()
|
||||
tabStore.tabs.length = 0
|
||||
// 重制当前存储库
|
||||
this.$reset()
|
||||
// 重定向到登录页
|
||||
if (route.meta.requiresAuth) {
|
||||
router.push({
|
||||
name: 'login',
|
||||
|
@ -1,13 +1,14 @@
|
||||
import type { MenuOption } from 'naive-ui'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { h } from 'vue'
|
||||
import { clone, construct } from 'radash'
|
||||
import { clone, construct, min } from 'radash'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import { arrayToTree, local, renderIcon } from '@/utils'
|
||||
import { createDynamicRoutes } from '@/router/guard/dynamic'
|
||||
import { router } from '@/router'
|
||||
import { fetchUserRoutes } from '@/service'
|
||||
import { staticRoutes } from '@/router/staticRoutes'
|
||||
import { staticRoutes } from '@/router/routes.static'
|
||||
import { usePermission } from '@/hooks'
|
||||
import { BasicLayout } from '@/layouts/index'
|
||||
|
||||
interface RoutesStatus {
|
||||
isInitAuthRoute: boolean
|
||||
@ -115,6 +116,67 @@ export const useRouteStore = defineStore('route-store', {
|
||||
return target
|
||||
})
|
||||
},
|
||||
setRedirect(routes: AppRoute.Route[]) {
|
||||
routes.forEach((route) => {
|
||||
if (route.children) {
|
||||
if (!route.redirect) {
|
||||
// 过滤出没有隐藏的子元素集
|
||||
const visibleChilds = route.children.filter(child => !child.meta.hide)
|
||||
|
||||
// 过滤出含有order属性的页面
|
||||
const orderChilds = visibleChilds.filter(child => child.meta.order)
|
||||
|
||||
// 重定向页默认第一个子元素的路径
|
||||
let target = route.children[0]
|
||||
if (orderChilds.length > 0)
|
||||
// 有order则取最小者重定向
|
||||
target = min(orderChilds, i => i.meta.order as number) as AppRoute.Route
|
||||
|
||||
route.redirect = target.path
|
||||
}
|
||||
|
||||
this.setRedirect(route.children)
|
||||
}
|
||||
})
|
||||
},
|
||||
createDynamicRoutes(routes: AppRoute.RowRoute[]) {
|
||||
const { hasPermission } = usePermission()
|
||||
// 结构化meta字段
|
||||
let resultRouter = clone(routes).map(i => construct(i)) as AppRoute.Route[]
|
||||
// 路由权限过滤
|
||||
resultRouter = resultRouter.filter(i => hasPermission(i.meta.roles))
|
||||
|
||||
// 生成需要keepAlive的路由name数组
|
||||
this.cacheRoutes = resultRouter.filter((i) => {
|
||||
return i.meta.keepAlive
|
||||
})
|
||||
.map(i => i.name)
|
||||
|
||||
// 生成路由,有redirect的不需要引入文件
|
||||
const modules = import.meta.glob('@/views/**/*.vue')
|
||||
resultRouter = resultRouter.map((item: any) => {
|
||||
if (item.componentPath && !item.redirect)
|
||||
item.component = modules[`/src/views${item.componentPath}`]
|
||||
return item
|
||||
})
|
||||
|
||||
resultRouter = arrayToTree(resultRouter) as AppRoute.Route[]
|
||||
this.setRedirect(resultRouter)
|
||||
const appRootRoute: RouteRecordRaw = {
|
||||
path: '/appRoot',
|
||||
name: 'appRoot',
|
||||
redirect: import.meta.env.VITE_HOME_PATH,
|
||||
component: BasicLayout,
|
||||
meta: {
|
||||
title: '首页',
|
||||
icon: 'icon-park-outline:home',
|
||||
},
|
||||
children: [],
|
||||
}
|
||||
// 根据角色过滤后的插入根路由中
|
||||
appRootRoute.children = resultRouter as unknown as RouteRecordRaw[]
|
||||
return appRootRoute
|
||||
},
|
||||
/* 初始化动态路由 */
|
||||
async initDynamicRoute() {
|
||||
// 根据用户id来获取用户的路由
|
||||
@ -130,7 +192,7 @@ export const useRouteStore = defineStore('route-store', {
|
||||
if (!data)
|
||||
return
|
||||
// 根据用户返回的路由表来生成真实路由
|
||||
const appRoutes = createDynamicRoutes(data)
|
||||
const appRoutes = this.createDynamicRoutes(data)
|
||||
// 生成侧边菜单
|
||||
this.createMenus(data)
|
||||
// 插入路由表
|
||||
@ -139,7 +201,7 @@ export const useRouteStore = defineStore('route-store', {
|
||||
/* 初始化静态路由 */
|
||||
initStaticRoute() {
|
||||
// 根据静态路由表来生成真实路由
|
||||
const appRoutes = createDynamicRoutes(staticRoutes)
|
||||
const appRoutes = this.createDynamicRoutes(staticRoutes)
|
||||
// 生成侧边菜单
|
||||
this.createMenus(staticRoutes)
|
||||
// 插入路由表
|
||||
|
2
src/typings/env.d.ts
vendored
2
src/typings/env.d.ts
vendored
@ -31,7 +31,7 @@ interface ImportMetaEnv {
|
||||
/** hash路由模式 */
|
||||
readonly VITE_ROUTE_MODE?: 'hash' | 'web'
|
||||
/** 路由加载模式 */
|
||||
readonly VITE_AUTH_ROUTE_MODE?: 'static' | 'dynamic'
|
||||
readonly VITE_AUTH_ROUTE_MODE: 'static' | 'dynamic'
|
||||
/** 首次加载页面 */
|
||||
readonly VITE_HOME_PATH: string
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user