From 1b28285b04f31d6eee99e11a36f3810a16938344 Mon Sep 17 00:00:00 2001 From: chansee97 Date: Mon, 8 Sep 2025 00:44:44 +0800 Subject: [PATCH] refactor: move app initialization from AppMain to main.ts and improve router guards --- src/App.vue | 49 ++++++++++--- src/AppMain.vue | 73 ------------------- src/main.ts | 42 ++++++++++- src/router/guard.ts | 174 ++++++++++++++++++++++++++++---------------- 4 files changed, 188 insertions(+), 150 deletions(-) delete mode 100644 src/AppMain.vue diff --git a/src/App.vue b/src/App.vue index 00d98e3..a6717f2 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,18 +1,43 @@ diff --git a/src/AppMain.vue b/src/AppMain.vue deleted file mode 100644 index 1a2dfc5..0000000 --- a/src/AppMain.vue +++ /dev/null @@ -1,73 +0,0 @@ - - - diff --git a/src/main.ts b/src/main.ts index 54ddfab..3980d9e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,41 @@ +import type { App as AppType } from 'vue' import App from './App.vue' +import AppLoading from './components/common/AppLoading.vue' +import { installPinia } from '@/store' +import { installRouter } from '@/router' -// 创建应用实例并挂载 -const app = createApp(App) -app.mount('#app') +async function bootstrap() { + // 显示加载动画 + const loadingApp = createApp(AppLoading) + loadingApp.mount('#app') + + try { + // 创建应用实例 + const app = createApp(App) + + // 注册模块 Pinia + await installPinia(app) + + // 注册模块 Vue-router + await installRouter(app) + + // 注册模块 指令/静态资源 + const modules = import.meta.glob<{ install: (app: AppType) => void }>('./modules/*.ts', { + eager: true, + }) + + Object.values(modules).forEach(module => app.use(module)) + + // 卸载加载动画并挂载主应用 + loadingApp.unmount() + app.mount('#app') + } + catch (error) { + // 如果初始化失败,卸载加载动画并显示错误 + loadingApp.unmount() + console.error('Application initialization failed:', error) + throw error + } +} + +bootstrap().catch(console.error) diff --git a/src/router/guard.ts b/src/router/guard.ts index ebbd98b..769d2a4 100644 --- a/src/router/guard.ts +++ b/src/router/guard.ts @@ -1,92 +1,142 @@ -import type { Router } from 'vue-router' +import type { NavigationGuardNext, RouteLocationNormalized, Router } from 'vue-router' import { useAppStore, useRouteStore, useTabStore } from '@/store' import { local } from '@/utils' const title = import.meta.env.VITE_APP_NAME +// 路由上下文 +interface RouteContext { + appStore: ReturnType + routeStore: ReturnType + tabStore: ReturnType + isLogin: boolean +} + +// 外链处理 +function handleExternalLink(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) { + if (to.meta.isLink) { + window.open(to.meta.linkPath as string) + next(false) + return true + } + return false +} + +// 根路径重定向 +function handleRootPath(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext, context: RouteContext) { + if (to.path === '/') { + if (context.isLogin) { + next({ path: import.meta.env.VITE_HOME_PATH, replace: true }) + } + else { + next({ path: '/login', replace: true }) + } + return true + } + return false +} + +// 登录页面访问处理 +function handleLoginPage(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext, context: RouteContext) { + if (to.name === 'login' && context.isLogin) { + next({ path: import.meta.env.VITE_HOME_PATH, replace: true }) + return true + } + return false +} + +// 认证检查 +function handleAuthentication(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext, context: RouteContext) { + if (!context.isLogin && to.name !== 'login' && to.meta?.requiresAuth !== false) { + const redirect = to.name === 'not-found' ? undefined : to.fullPath + next({ path: '/login', query: { redirect } }) + return true + } + return false +} + +// 路由初始化 +async function handleRouteInitialization(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext, context: RouteContext) { + if (context.isLogin && to.name !== 'login' && !context.routeStore.isInitAuthRoute) { + try { + await context.routeStore.initAuthRoute() + + if (to.name === 'not-found') { + next({ + path: to.fullPath, + replace: true, + query: to.query, + hash: to.hash, + }) + return true + } + } + catch (error) { + console.error('Route initialization failed:', error) + local.remove('accessToken') + context.routeStore.resetRouteStore() + const redirect = to.fullPath !== '/' && to.fullPath !== '/login' ? to.fullPath : undefined + next({ path: '/login', query: redirect ? { redirect } : undefined, replace: true }) + return true + } + } + return false +} + +// 路由处理器列表 +const routeHandlers = [ + handleExternalLink, + handleRootPath, + handleLoginPage, + handleAuthentication, + handleRouteInitialization, +] + +// 处理路由 +async function processRoute(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext, context: RouteContext) { + for (const handler of routeHandlers) { + const handled = await handler(to, from, next, context) + if (handled) + return + } + + // 如果没有任何处理器处理该路由,则继续导航 + next() +} + export function setupRouterGuard(router: Router) { const appStore = useAppStore() const routeStore = useRouteStore() const tabStore = useTabStore() router.beforeEach(async (to, from, next) => { - // 判断是否是外链,如果是直接打开网页并拦截跳转 - if (to.meta.isLink) { - window.open(to.meta.linkPath) - next(false) // 取消当前导航 - return - } // 开始 loadingBar appStore.showProgress && window.$loadingBar?.start() - // 判断有无TOKEN,登录鉴权 - const isLogin = Boolean(local.get('accessToken')) - - // 处理根路由重定向 - if (to.path === '/') { - if (isLogin) { - // 已登录,重定向到首页 - next({ path: import.meta.env.VITE_HOME_PATH, replace: true }) - } - else { - // 未登录,重定向到登录页 - next({ path: '/login', replace: true }) - } - return + // 创建路由上下文 + const context: RouteContext = { + appStore, + routeStore, + tabStore, + isLogin: Boolean(local.get('accessToken')), } - // 如果用户未登录,重定向到登录页 - if (!isLogin) { - const redirect = to.name === 'not-found' ? undefined : to.fullPath - next({ path: '/login', query: { redirect } }) - return - } - - // 如果用户已登录且访问login页面,重定向到首页 - if (to.name === 'login' && isLogin) { - next({ path: '/' }) - return - } - - // 判断路由有无进行初始化 - if (!routeStore.isInitAuthRoute && to.name !== 'login') { - try { - await routeStore.initAuthRoute() - // 动态路由加载完回到根路由 - if (to.name === 'not-found') { - // 等待权限路由加载好了,回到之前的路由,否则404 - next({ - path: to.fullPath, - replace: true, - query: to.query, - hash: to.hash, - }) - return - } - } - catch { - // 如果路由初始化失败(比如 401 错误),重定向到登录页 - local.remove('accessToken') - const redirect = to.fullPath !== '/' ? to.fullPath : undefined - next({ path: '/login', query: redirect ? { redirect } : undefined }) - return - } - } - - next() + // 使用函数式处理路由 + await processRoute(to, from, next, context) }) + router.beforeResolve((to) => { // 设置菜单高亮 routeStore.setActiveMenu(to.meta.activePath ?? to.fullPath) // 添加tabs tabStore.addTab(to) - // 设置高亮标签; + // 设置高亮标签 tabStore.setCurrentTab(to.fullPath as string) }) router.afterEach((to) => { // 修改网页标题 - document.title = `${to.meta.title} - ${title}` + document.title = `${to.meta.title || 'Nova Admin'} - ${title}` // 结束 loadingBar appStore.showProgress && window.$loadingBar?.finish() })