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 type { App } from 'vue'
|
||||||
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
|
||||||
import { routes } from './routes'
|
import { routes } from './routes.inner'
|
||||||
import { setupRouterGuard } from './guard'
|
import { setupRouterGuard } from './guard'
|
||||||
|
|
||||||
const { VITE_ROUTE_MODE = 'hash', VITE_BASE_URL } = import.meta.env
|
const { VITE_ROUTE_MODE = 'hash', VITE_BASE_URL } = import.meta.env
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { useRouteStore } from './route'
|
import { useRouteStore } from './route'
|
||||||
|
import { useTabStore } from './tab'
|
||||||
import { fetchLogin, fetchUserInfo } from '@/service'
|
import { fetchLogin, fetchUserInfo } from '@/service'
|
||||||
import { router } from '@/router'
|
import { router } from '@/router'
|
||||||
import { local } from '@/utils'
|
import { local } from '@/utils'
|
||||||
@ -34,7 +35,12 @@ export const useAuthStore = defineStore('auth-store', {
|
|||||||
// 清空路由、菜单等数据
|
// 清空路由、菜单等数据
|
||||||
const routeStore = useRouteStore()
|
const routeStore = useRouteStore()
|
||||||
routeStore.resetRouteStore()
|
routeStore.resetRouteStore()
|
||||||
|
// 清空标签栏数据
|
||||||
|
const tabStore = useTabStore()
|
||||||
|
tabStore.tabs.length = 0
|
||||||
|
// 重制当前存储库
|
||||||
this.$reset()
|
this.$reset()
|
||||||
|
// 重定向到登录页
|
||||||
if (route.meta.requiresAuth) {
|
if (route.meta.requiresAuth) {
|
||||||
router.push({
|
router.push({
|
||||||
name: 'login',
|
name: 'login',
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import type { MenuOption } from 'naive-ui'
|
import type { MenuOption } from 'naive-ui'
|
||||||
import { RouterLink } from 'vue-router'
|
import { RouterLink } from 'vue-router'
|
||||||
import { h } from 'vue'
|
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 { arrayToTree, local, renderIcon } from '@/utils'
|
||||||
import { createDynamicRoutes } from '@/router/guard/dynamic'
|
|
||||||
import { router } from '@/router'
|
import { router } from '@/router'
|
||||||
import { fetchUserRoutes } from '@/service'
|
import { fetchUserRoutes } from '@/service'
|
||||||
import { staticRoutes } from '@/router/staticRoutes'
|
import { staticRoutes } from '@/router/routes.static'
|
||||||
import { usePermission } from '@/hooks'
|
import { usePermission } from '@/hooks'
|
||||||
|
import { BasicLayout } from '@/layouts/index'
|
||||||
|
|
||||||
interface RoutesStatus {
|
interface RoutesStatus {
|
||||||
isInitAuthRoute: boolean
|
isInitAuthRoute: boolean
|
||||||
@ -115,6 +116,67 @@ export const useRouteStore = defineStore('route-store', {
|
|||||||
return target
|
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() {
|
async initDynamicRoute() {
|
||||||
// 根据用户id来获取用户的路由
|
// 根据用户id来获取用户的路由
|
||||||
@ -130,7 +192,7 @@ export const useRouteStore = defineStore('route-store', {
|
|||||||
if (!data)
|
if (!data)
|
||||||
return
|
return
|
||||||
// 根据用户返回的路由表来生成真实路由
|
// 根据用户返回的路由表来生成真实路由
|
||||||
const appRoutes = createDynamicRoutes(data)
|
const appRoutes = this.createDynamicRoutes(data)
|
||||||
// 生成侧边菜单
|
// 生成侧边菜单
|
||||||
this.createMenus(data)
|
this.createMenus(data)
|
||||||
// 插入路由表
|
// 插入路由表
|
||||||
@ -139,7 +201,7 @@ export const useRouteStore = defineStore('route-store', {
|
|||||||
/* 初始化静态路由 */
|
/* 初始化静态路由 */
|
||||||
initStaticRoute() {
|
initStaticRoute() {
|
||||||
// 根据静态路由表来生成真实路由
|
// 根据静态路由表来生成真实路由
|
||||||
const appRoutes = createDynamicRoutes(staticRoutes)
|
const appRoutes = this.createDynamicRoutes(staticRoutes)
|
||||||
// 生成侧边菜单
|
// 生成侧边菜单
|
||||||
this.createMenus(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路由模式 */
|
/** hash路由模式 */
|
||||||
readonly VITE_ROUTE_MODE?: 'hash' | 'web'
|
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
|
readonly VITE_HOME_PATH: string
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user