feat(web): 整体替换 UI 框架为 Ant Design 6 并接入自定义主题

以 antd ConfigProvider + antd-style 承载 zinc 风格自定义主题(明暗双套 token),
button/input/select/notification 用 createStyles 定制;三态主题经
darkAlgorithm 无刷新切换;use-toast 改基于 antd notification;
移除 shadcn/ui 组件目录与 radix 依赖;样式桥接层保证排版与多端布局不变。
This commit is contained in:
2026-09-09 10:35:24 +08:00
parent 5a78de6aa6
commit 87d9bab5dc
23 changed files with 2787 additions and 1608 deletions
+48 -21
View File
@@ -1,7 +1,11 @@
import { lazy, Suspense } from 'react'
import { Navigate, Route, Routes } from 'react-router'
import { lazy, Suspense, useEffect, useState } from 'react'
import type { ReactNode } from 'react'
import { Navigate, Route, Routes } from 'react-router'
import { App as AntdApp, ConfigProvider } 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'
@@ -16,6 +20,27 @@ import NotFound from './pages/NotFound'
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()
@@ -30,24 +55,26 @@ function RequireAuth({ children }: { children: ReactNode }) {
export default function App() {
return (
<Suspense fallback={<div className="pn-loading">加载中…</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>
<Suspense fallback={<div className="pn-loading">加载中…</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>
)
}