Files
sundynix-micro-admin/src/App.tsx
T
2026-04-30 22:53:46 +08:00

127 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
import { useAuthStore } from '@/store/auth'
import { useAppStore } from '@/store/app'
import AdminLayout from '@/layouts/AdminLayout'
import LoginPage from '@/pages/LoginPage'
import ErrorBoundary from '@/components/ErrorBoundary'
import { Suspense, useMemo, lazy, useEffect } from 'react'
import { Loader2, Shield } from 'lucide-react'
import { Button } from '@/components/ui/button'
import type { SystemMenu } from '@/api/system'
const pages = import.meta.glob('./pages/**/*.tsx')
const dynamicComponentMap: Record<string, React.LazyExoticComponent<any>> = {}
for (const path in pages) {
let routePath = path.replace(/^\.\/pages/, '').replace(/\.tsx$/, '').replace(/\/index$/, '').toLowerCase()
if (routePath === '/loginpage') continue
dynamicComponentMap[routePath] = lazy(pages[path] as any)
}
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const isAuthenticated = useAuthStore(s => s.isAuthenticated)
if (!isAuthenticated) return <Navigate to="/login" replace />
return <>{children}</>
}
function PublicRoute({ children }: { children: React.ReactNode }) {
const isAuthenticated = useAuthStore(s => s.isAuthenticated)
if (isAuthenticated) return <Navigate to="/" replace />
return <>{children}</>
}
function NoPermission() {
const logout = useAuthStore(s => s.logout)
return (
<div className="flex flex-col items-center justify-center min-h-[60vh] text-center w-full">
<Shield className="h-16 w-16 text-muted-foreground/30 mb-4" />
<h2 className="text-2xl font-bold mb-2 text-foreground">访</h2>
<p className="text-muted-foreground mb-6 max-w-md"></p>
<Button onClick={logout} variant="default" className="w-32">退</Button>
</div>
)
}
function AppRoutes() {
const menus = useAuthStore(s => s.menus)
const isAuthenticated = useAuthStore(s => s.isAuthenticated)
const refreshMenus = useAuthStore(s => s.refreshMenus)
const hasFetchedMenus = useAuthStore(s => s.hasFetchedMenus)
useEffect(() => {
if (isAuthenticated && menus.length === 0) refreshMenus()
}, [isAuthenticated, menus.length, refreshMenus])
const dynamicRoutes = useMemo(() => {
const routes: { path: string; Component: React.ComponentType }[] = []
const traverse = (items: SystemMenu[]) => {
items.forEach(item => {
if (item.children?.length) traverse(item.children)
const routeKey = item.path || item.code
if (routeKey && dynamicComponentMap[routeKey]) {
routes.push({ path: routeKey, Component: dynamicComponentMap[routeKey] })
}
})
}
if (menus) traverse(menus)
return routes
}, [menus])
const Loading = <div className="flex justify-center p-8"><Loader2 className="h-8 w-8 animate-spin text-muted-foreground" /></div>
return (
<Routes>
<Route path="/login" element={<PublicRoute><LoginPage /></PublicRoute>} />
<Route path="/" element={<ProtectedRoute><AdminLayout /></ProtectedRoute>}>
<Route index element={
hasFetchedMenus ? (
dynamicRoutes.length > 0 ? <Navigate to={dynamicRoutes[0].path} replace /> : <Navigate to="/403" replace />
) : Loading
} />
{dynamicRoutes.map(({ path, Component }) => (
<Route key={path} path={path.startsWith('/') ? path.substring(1) : path}
element={<ErrorBoundary><Suspense fallback={Loading}><Component /></Suspense></ErrorBoundary>} />
))}
{hasFetchedMenus && dynamicRoutes.length === 0 && (
<Route path="*" element={<NoPermission />} />
)}
{hasFetchedMenus && dynamicRoutes.length > 0 && (
<Route path="*" element={
<div className="flex flex-col items-center justify-center min-h-[60vh] text-center w-full">
<h2 className="text-2xl font-bold mb-2 text-foreground"></h2>
<p className="text-muted-foreground"></p>
</div>
} />
)}
</Route>
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
)
}
export default function App() {
const themeHue = useAppStore(s => s.themeHue)
useEffect(() => {
document.documentElement.style.setProperty('--theme-hue', themeHue)
if (themeHue === '45') {
document.documentElement.style.setProperty('--theme-l', '0.65')
document.documentElement.style.setProperty('--theme-c', '0.18')
document.documentElement.style.setProperty('--theme-l-dark', '0.70')
document.documentElement.style.setProperty('--theme-c-dark', '0.16')
} else {
document.documentElement.style.removeProperty('--theme-l')
document.documentElement.style.removeProperty('--theme-c')
document.documentElement.style.removeProperty('--theme-l-dark')
document.documentElement.style.removeProperty('--theme-c-dark')
}
}, [themeHue])
return (
<BrowserRouter>
<AppRoutes />
</BrowserRouter>
)
}