import 'nprogress/nprogress.css'; import NProgress from 'nprogress'; import { MessagePlugin } from 'tdesign-vue-next'; import type { RouteLocationNormalized } from 'vue-router'; import { getSetupStatus } from '@/api/setup'; import router from '@/router'; import { getPermissionStore, useUserStore } from '@/store'; import { hasRoleAccess } from '@/store/modules/menu-access'; NProgress.configure({ showSpinner: false }); const isPublic = (to: RouteLocationNormalized) => to.path === '/login' || to.path === '/result/404' || to.path === '/setup'; let setupStatusRequest: ReturnType | null = null; function loadSetupStatus() { if (setupStatusRequest) return setupStatusRequest; // Share only an in-flight request. Keeping the resolved setup state would // send the user back to /setup after the installation marker is written. const request = getSetupStatus(); setupStatusRequest = request; request.then( () => { if (setupStatusRequest === request) setupStatusRequest = null; }, () => { if (setupStatusRequest === request) setupStatusRequest = null; }, ); return setupStatusRequest; } router.beforeEach(async (to) => { NProgress.start(); const userStore = useUserStore(); const permissionStore = getPermissionStore(); let setupRequired = false; let setupStatusAvailable = false; try { const setupStatus = await loadSetupStatus(); setupRequired = setupStatus.required; setupStatusAvailable = true; } catch (error) { setupStatusRequest = null; if (to.path === '/setup') { const statusCode = (error as { response?: { status?: number } }).response?.status; if (statusCode !== 404) return true; } } if (setupStatusAvailable && setupRequired && to.path !== '/setup') return '/setup'; if (to.path === '/setup') return setupStatusAvailable && setupRequired ? true : '/login'; if (isPublic(to)) { return true; } try { if (!userStore.authenticated) await userStore.restoreSession(); permissionStore.initRoutes(); } catch { userStore.clearSession(); permissionStore.restoreRoutes(); if (to.path !== '/login') { MessagePlugin.warning('登录会话已失效,请重新登录'); return { path: '/login', query: { redirect: to.fullPath } }; } return true; } if (to.path !== '/role-select' && to.path !== '/profile' && !userStore.currentRole) { return { path: '/role-select', query: { redirect: to.fullPath } }; } if (userStore.user.mustChangePassword && to.path !== '/profile') { return { path: '/profile', query: { forcePassword: '1', redirect: to.fullPath } }; } const roles = (to.meta.roleCodes || []) as string[]; const permission = to.meta.permission as string | string[] | undefined; if (!hasRoleAccess(roles, userStore.currentRole)) return '/result/403'; if ( permission && (Array.isArray(permission) ? !permission.some((item) => userStore.hasPermission(item)) : !userStore.hasPermission(permission)) ) { return '/result/403'; } return true; }); router.afterEach(() => NProgress.done()); let handlingSessionExpiry = false; window.addEventListener('kaidi:session-expired', async () => { if (handlingSessionExpiry) return; handlingSessionExpiry = true; const userStore = useUserStore(); userStore.clearSession(); getPermissionStore().restoreRoutes(); try { if (router.currentRoute.value.path !== '/login') { await router.replace({ path: '/login', query: { redirect: router.currentRoute.value.fullPath } }); } } finally { handlingSessionExpiry = false; } });