Initial commit

This commit is contained in:
2026-03-14 18:24:33 +08:00
commit 890c33f510
67 changed files with 13080 additions and 0 deletions

214
src/router/index.tsx Normal file
View File

@@ -0,0 +1,214 @@
import { createBrowserRouter, Navigate, useLocation } from 'react-router-dom'
import { Suspense, lazy } from 'react'
import { useAuthStore, ROLES } from '@/stores/authStore'
// 路由守卫组件
export function AuthGuard({ children }: { children: React.ReactNode }) {
const { isAuthenticated, isLoading } = useAuthStore()
const location = useLocation()
if (isLoading) {
return (
<div className="flex h-screen items-center justify-center">
<div className="h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" />
</div>
)
}
if (!isAuthenticated) {
return <Navigate to="/login" state={{ from: location }} replace />
}
return <>{children}</>
}
export function RoleGuard({
children,
roles
}: {
children: React.ReactNode
roles: string[]
}) {
const { hasAnyRole, isAuthenticated, isLoading } = useAuthStore()
const location = useLocation()
if (isLoading) {
return (
<div className="flex h-screen items-center justify-center">
<div className="h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" />
</div>
)
}
if (!isAuthenticated) {
return <Navigate to="/login" state={{ from: location }} replace />
}
if (!hasAnyRole(roles)) {
return <Navigate to="/403" replace />
}
return <>{children}</>
}
export function AdminGuard({ children }: { children: React.ReactNode }) {
return (
<RoleGuard roles={[ROLES.SUPER_ADMIN, ROLES.ADMIN]}>
{children}
</RoleGuard>
)
}
export function ModeratorGuard({ children }: { children: React.ReactNode }) {
return (
<RoleGuard roles={[ROLES.SUPER_ADMIN, ROLES.ADMIN, ROLES.MODERATOR]}>
{children}
</RoleGuard>
)
}
// 公开路由守卫已登录用户重定向到dashboard
export function PublicGuard({ children }: { children: React.ReactNode }) {
const { isAuthenticated, isLoading } = useAuthStore()
if (isLoading) {
return (
<div className="flex h-screen items-center justify-center">
<div className="h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" />
</div>
)
}
if (isAuthenticated) {
return <Navigate to="/dashboard" replace />
}
return <>{children}</>
}
// 加载指示器组件
function PageLoader() {
return (
<div className="flex h-full items-center justify-center p-8">
<div className="h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" />
</div>
)
}
// 懒加载页面组件
const Login = lazy(() => import('@/pages/Login'))
const Dashboard = lazy(() => import('@/pages/Dashboard'))
const Users = lazy(() => import('@/pages/Users'))
const Roles = lazy(() => import('@/pages/Roles'))
const Posts = lazy(() => import('@/pages/Posts'))
const Comments = lazy(() => import('@/pages/Comments'))
const Groups = lazy(() => import('@/pages/Groups'))
const Forbidden = lazy(() => import('@/pages/Forbidden'))
const NotFound = lazy(() => import('@/pages/NotFound'))
// 布局组件
import MainLayout from '@/components/Layout/MainLayout'
// 创建路由
export const router = createBrowserRouter([
{
path: '/login',
element: (
<PublicGuard>
<Suspense fallback={<PageLoader />}>
<Login />
</Suspense>
</PublicGuard>
),
},
{
path: '/',
element: (
<AuthGuard>
<MainLayout />
</AuthGuard>
),
children: [
{
index: true,
element: <Navigate to="/dashboard" replace />,
},
{
path: 'dashboard',
element: (
<Suspense fallback={<PageLoader />}>
<Dashboard />
</Suspense>
),
},
{
path: 'users',
element: (
<AdminGuard>
<Suspense fallback={<PageLoader />}>
<Users />
</Suspense>
</AdminGuard>
),
},
{
path: 'roles',
element: (
<AdminGuard>
<Suspense fallback={<PageLoader />}>
<Roles />
</Suspense>
</AdminGuard>
),
},
{
path: 'posts',
element: (
<ModeratorGuard>
<Suspense fallback={<PageLoader />}>
<Posts />
</Suspense>
</ModeratorGuard>
),
},
{
path: 'comments',
element: (
<ModeratorGuard>
<Suspense fallback={<PageLoader />}>
<Comments />
</Suspense>
</ModeratorGuard>
),
},
{
path: 'groups',
element: (
<ModeratorGuard>
<Suspense fallback={<PageLoader />}>
<Groups />
</Suspense>
</ModeratorGuard>
),
},
],
},
{
path: '/403',
element: (
<Suspense fallback={<PageLoader />}>
<Forbidden />
</Suspense>
),
},
{
path: '*',
element: (
<Suspense fallback={<PageLoader />}>
<NotFound />
</Suspense>
),
},
])
export default router