- 状态点自制徽标改用 Badge,回收站剩余天数紧急态改用红色 Tag - 危险按钮改用 danger 属性并补充语言,text+danger 不套实心红样式 - 后台搜索框改 antd Input 前缀形态,删除手写容器与聚焦环样式 - 加载态统一 Spin/Skeleton(Table 用 loading),空态统一 Empty - Segmented 选项计数改用 Badge count,弱化为 muted 灰 - 首页分页说明改 Pagination showTotal,笔记错误态改 Empty 承接布局 - 代码块复制按钮改 antd Button 并增加「已复制」反馈,修复悬浮不显示 - 博客/后台图标按钮与溢出菜单触发器统一为 antd Button type=text
81 lines
3.2 KiB
TypeScript
81 lines
3.2 KiB
TypeScript
import { lazy, Suspense, useEffect, useState } from 'react'
|
||
import type { ReactNode } from 'react'
|
||
import { Navigate, Route, Routes } from 'react-router'
|
||
import { App as AntdApp, ConfigProvider, Spin } from 'antd'
|
||
import zhCN from 'antd/locale/zh_CN'
|
||
import { useAuth } from './lib/auth'
|
||
import { useTheme } from './lib/theme'
|
||
import useShadcnTheme from './theme/shadcnTheme'
|
||
import { BlogLayout, AdminLayout } from './components/Layout'
|
||
import Home from './pages/Home'
|
||
import TagPage from './pages/TagPage'
|
||
import Login from './pages/Login'
|
||
import AdminList from './pages/AdminList'
|
||
import AdminTrash from './pages/AdminTrash'
|
||
import AdminSettings from './pages/AdminSettings'
|
||
import NotFound from './pages/NotFound'
|
||
|
||
// 重依赖路由懒加载:Note 带 markdown 渲染管线,AdminEdit 额外带 CodeMirror,
|
||
// 拆出主包以避免单 chunk 过大(vite 500kB 告警)
|
||
const Note = lazy(() => import('./pages/Note'))
|
||
const AdminEdit = lazy(() => import('./pages/AdminEdit'))
|
||
|
||
// antd ConfigProvider:跟随应用三态主题切换 light/dark 算法(system 监听系统变化)
|
||
function AntdProvider({ children }: { children: ReactNode }) {
|
||
const { mode } = useTheme()
|
||
const [systemDark, setSystemDark] = useState(
|
||
() => window.matchMedia('(prefers-color-scheme: dark)').matches,
|
||
)
|
||
useEffect(() => {
|
||
const mq = window.matchMedia('(prefers-color-scheme: dark)')
|
||
const onChange = (e: MediaQueryListEvent) => setSystemDark(e.matches)
|
||
mq.addEventListener('change', onChange)
|
||
return () => mq.removeEventListener('change', onChange)
|
||
}, [])
|
||
const dark = mode === 'dark' || (mode === 'system' && systemDark)
|
||
const configProps = useShadcnTheme(dark)
|
||
return (
|
||
<ConfigProvider locale={zhCN} {...configProps}>
|
||
<AntdApp>{children}</AntdApp>
|
||
</ConfigProvider>
|
||
)
|
||
}
|
||
|
||
// 登录守卫:401 统一跳转 /admin/login(§8.2)
|
||
function RequireAuth({ children }: { children: ReactNode }) {
|
||
const { authenticated, loading } = useAuth()
|
||
if (loading) {
|
||
return <div className="pn-loading"><Spin size="large" /></div>
|
||
}
|
||
if (!authenticated) {
|
||
return <Navigate to="/admin/login" replace />
|
||
}
|
||
return <>{children}</>
|
||
}
|
||
|
||
export default function App() {
|
||
return (
|
||
<AntdProvider>
|
||
<Suspense fallback={<div className="pn-loading"><Spin size="large" /></div>}>
|
||
<Routes>
|
||
<Route element={<BlogLayout />}>
|
||
<Route path="/" element={<Home />} />
|
||
<Route path="/notes/:slug" element={<Note />} />
|
||
<Route path="/tags" element={<TagPage />} />
|
||
<Route path="/tags/:tag" element={<TagPage />} />
|
||
</Route>
|
||
<Route path="/admin/login" element={<Login />} />
|
||
<Route element={<RequireAuth><AdminLayout /></RequireAuth>}>
|
||
<Route path="/admin" element={<AdminList />} />
|
||
<Route path="/admin/trash" element={<AdminTrash />} />
|
||
<Route path="/admin/settings" element={<AdminSettings />} />
|
||
<Route path="/admin/notes/new" element={<AdminEdit />} />
|
||
<Route path="/admin/notes/:id/edit" element={<AdminEdit />} />
|
||
</Route>
|
||
<Route path="*" element={<NotFound />} />
|
||
</Routes>
|
||
</Suspense>
|
||
</AntdProvider>
|
||
)
|
||
}
|