Files
monisuo/admin-web/src/router/guards.ts

227 lines
7.8 KiB
TypeScript
Raw Normal View History

2026-03-22 01:07:31 +08:00
import type { Router, RouteRecordRaw } from 'vue-router'
import { useNProgress } from '@vueuse/integrations/useNProgress'
import { asyncRoutes, asyncRoutesByFilesystem } from './routes'
import '@/assets/styles/nprogress.css'
function setupRoutes(router: Router) {
router.beforeEach(async (to, _from, next) => {
const settingsStore = useSettingsStore()
const userStore = useUserStore()
const routeStore = useRouteStore()
const menuStore = useMenuStore()
// 是否已登录
if (userStore.isLogin) {
// 是否已根据权限动态生成并注册路由
if (routeStore.isGenerate) {
// 导航栏如果不是 single 模式,则需要根据 path 定位主导航的选中状态
settingsStore.settings.menu.mode !== 'single' && menuStore.setActived(to.path)
// 如果已登录状态下,进入登录页会强制跳转到主页
if (to.name === 'login') {
next({
path: settingsStore.settings.home.fullPath,
replace: true,
})
}
// 如果未开启主页,但进入的是主页,则会进入侧边栏导航第一个模块
else if (!settingsStore.settings.home.enable && to.fullPath === settingsStore.settings.home.fullPath) {
if (menuStore.sidebarMenus.length > 0) {
next({
path: menuStore.sidebarMenusFirstDeepestPath,
replace: true,
})
}
// 如果侧边栏导航第一个模块均无法命中,则还是进入主页
else {
next()
}
}
// 正常访问页面
else {
next()
}
}
else {
try {
// 获取用户权限
settingsStore.settings.app.enablePermission && await userStore.getPermissions()
// 生成动态路由
switch (settingsStore.settings.app.routeBaseOn) {
case 'frontend':
routeStore.generateRoutesAtFront(asyncRoutes)
break
case 'backend':
await routeStore.generateRoutesAtBack()
break
case 'filesystem':
routeStore.generateRoutesAtFilesystem(asyncRoutesByFilesystem)
// 文件系统生成的路由,需要手动生成导航数据
switch (settingsStore.settings.menu.baseOn) {
case 'frontend':
menuStore.generateMenusAtFront()
break
case 'backend':
await menuStore.generateMenusAtBack()
break
}
break
}
// 注册并记录路由数据
// 记录的数据会在登出时会使用到,不使用 router.removeRoute 是考虑配置的路由可能不一定有设置 name ,则通过调用 router.addRoute() 返回的回调进行删除
const removeRoutes: (() => void)[] = []
routeStore.routes.forEach((route) => {
if (!/^(?:https?:|mailto:|tel:)/.test(route.path)) {
removeRoutes.push(router.addRoute(route as RouteRecordRaw))
}
})
if (settingsStore.settings.app.routeBaseOn !== 'filesystem') {
routeStore.systemRoutes.forEach((route) => {
removeRoutes.push(router.addRoute(route as RouteRecordRaw))
})
}
routeStore.setCurrentRemoveRoutes(removeRoutes)
}
catch {
userStore.logout()
}
// 动态路由生成并注册后,重新进入当前路由
next({
path: to.path,
query: to.query,
replace: true,
})
}
}
else {
if (to.name !== 'login') {
next({
name: 'login',
query: {
redirect: to.fullPath !== settingsStore.settings.home.fullPath ? to.fullPath : undefined,
},
})
}
else {
next()
}
}
})
}
// 当父级路由未配置重定向时,自动重定向到有访问权限的子路由
function setupRedirectAuthChildrenRoute(router: Router) {
router.beforeEach((to, _from, next) => {
const { auth } = useAuth()
const currentRoute = router.getRoutes().find(route => route.path === (to.matched.at(-1)?.path ?? ''))
if (!currentRoute?.redirect) {
const findAuthRoute = currentRoute?.children?.find(route => route.meta?.menu !== false && auth(route.meta?.auth ?? ''))
if (findAuthRoute) {
next(findAuthRoute)
}
else {
next()
}
}
else {
next()
}
})
}
// 进度条
function setupProgress(router: Router) {
const { isLoading } = useNProgress()
router.beforeEach((_to, _from, next) => {
const settingsStore = useSettingsStore()
if (settingsStore.settings.app.enableProgress) {
isLoading.value = true
}
next()
})
router.afterEach(() => {
const settingsStore = useSettingsStore()
if (settingsStore.settings.app.enableProgress) {
isLoading.value = false
}
})
}
// 标题
function setupTitle(router: Router) {
router.afterEach((to) => {
const settingsStore = useSettingsStore()
if (settingsStore.settings.app.routeBaseOn !== 'filesystem') {
settingsStore.setTitle(to.matched?.at(-1)?.meta?.title ?? to.meta.title)
}
else {
settingsStore.setTitle(to.meta.title)
}
})
}
// 页面缓存
function setupKeepAlive(router: Router) {
router.afterEach(async (to, from) => {
const keepAliveStore = useKeepAliveStore()
if (to.fullPath !== from.fullPath) {
if (to.meta.cache) {
const componentName = to.matched.at(-1)?.components?.default.name
if (componentName) {
// 缓存当前页面前,先判断是否需要进行清除缓存,判断依据:
// 1. 如果 to.meta.cache 为 boolean 类型,并且不为 true则需要清除缓存
// 2. 如果 to.meta.cache 为 string 类型,并且与 from.name 不一致,则需要清除缓存
// 3. 如果 to.meta.cache 为 array 类型,并且不包含 from.name则需要清除缓存
// 4. 如果 to.meta.noCache 为 string 类型,并且与 from.name 一致,则需要清除缓存
// 5. 如果 to.meta.noCache 为 array 类型,并且包含 from.name则需要清除缓存
// 6. 如果是刷新页面,则需要清除缓存
let shouldClearCache = false
if (typeof to.meta.cache === 'boolean') {
shouldClearCache = !to.meta.cache
}
else if (typeof to.meta.cache === 'string') {
shouldClearCache = to.meta.cache !== from.name
}
else if (Array.isArray(to.meta.cache)) {
shouldClearCache = !to.meta.cache.includes(from.name as string)
}
if (to.meta.noCache) {
if (typeof to.meta.noCache === 'string') {
shouldClearCache = to.meta.noCache === from.name
}
else if (Array.isArray(to.meta.noCache)) {
shouldClearCache = to.meta.noCache.includes(from.name as string)
}
}
if (from.name === 'reload') {
shouldClearCache = true
}
if (shouldClearCache) {
keepAliveStore.remove(componentName)
await nextTick()
}
keepAliveStore.add(componentName)
}
else {
// turbo-console-disable-next-line
console.warn('[Fantastic-admin] 该页面组件未设置组件名,会导致缓存失效,请检查')
}
}
}
})
}
// 其他
function setupOther(router: Router) {
router.afterEach(() => {
document.documentElement.scrollTop = 0
})
}
export default function setupGuards(router: Router) {
setupRoutes(router)
setupRedirectAuthChildrenRoute(router)
setupProgress(router)
setupTitle(router)
setupKeepAlive(router)
setupOther(router)
}