Browse Source

✨ feat(admin): 重构管理后台UI组件库为shadcn/ui

- 替换Ant Design为shadcn/ui组件库,提升UI一致性
- 实现ErrorPage错误页面重构,使用shadcn Alert和Button组件
- 优化NotFoundPage页面,移除Ant Design依赖
- 改进ProtectedRoute加载状态,使用shadcn Skeleton组件
- 添加主题切换功能和通知系统
- 配置React Query默认选项,优化请求行为

💄 style(admin): 优化管理后台视觉样式

- 统一错误提示样式,使用AlertCircle图标增强视觉反馈
- 改进加载状态UI,使用骨架屏提升用户体验
- 优化按钮样式和交互效果
- 调整错误堆栈展示样式,增强可读性
- 添加主题存储功能,记住用户主题偏好
yourname 4 months ago
parent
commit
56d3a30648

+ 14 - 14
src/client/admin-shadcn/components/ErrorPage.tsx

@@ -1,6 +1,8 @@
 import React from 'react';
 import { useRouteError, useNavigate } from 'react-router';
-import { Alert, Button } from 'antd';
+import { Alert, AlertDescription, AlertTitle } from '@/client/components/ui/alert';
+import { Button } from '@/client/components/ui/button';
+import { AlertCircle } from 'lucide-react';
 
 export const ErrorPage = () => {
   const navigate = useNavigate();
@@ -8,30 +10,28 @@ export const ErrorPage = () => {
   const errorMessage = error?.statusText || error?.message || '未知错误';
   
   return (
-    <div className="flex flex-col items-center justify-center flex-grow p-4"
-    >
+    <div className="flex flex-col items-center justify-center flex-grow p-4">
       <div className="max-w-3xl w-full">
         <h1 className="text-2xl font-bold mb-4">发生错误</h1>
-        <Alert 
-          type="error"
-          message={error?.message || '未知错误'}
-          description={
-            error?.stack ? (
-              <pre className="text-xs overflow-auto p-2 bg-gray-100 dark:bg-gray-800 rounded">
+        <Alert variant="destructive" className="mb-4">
+          <AlertCircle className="h-4 w-4" />
+          <AlertTitle>{error?.message || '未知错误'}</AlertTitle>
+          <AlertDescription>
+            {error?.stack ? (
+              <pre className="text-xs overflow-auto p-2 bg-muted rounded mt-2">
                 {error.stack}
               </pre>
-            ) : null
-          }
-          className="mb-4"
-        />
+            ) : null}
+          </AlertDescription>
+        </Alert>
         <div className="flex gap-4">
           <Button 
-            type="primary" 
             onClick={() => navigate(0)}
           >
             重新加载
           </Button>
           <Button 
+            variant="outline"
             onClick={() => navigate('/admin')}
           >
             返回首页

+ 2 - 3
src/client/admin-shadcn/components/NotFoundPage.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import { useNavigate } from 'react-router';
-import { Button } from 'antd';
+import { Button } from '@/client/components/ui/button';
 
 export const NotFoundPage = () => {
   const navigate = useNavigate();
@@ -9,12 +9,11 @@ export const NotFoundPage = () => {
     <div className="flex flex-col items-center justify-center flex-grow p-4">
       <div className="max-w-3xl w-full">
         <h1 className="text-2xl font-bold mb-4">404 - 页面未找到</h1>
-        <p className="mb-6 text-gray-600 dark:text-gray-300">
+        <p className="mb-6 text-muted-foreground">
           您访问的页面不存在或已被移除
         </p>
         <div className="flex gap-4">
           <Button 
-            type="primary" 
             onClick={() => navigate('/admin')}
           >
             返回首页

+ 9 - 8
src/client/admin-shadcn/components/ProtectedRoute.tsx

@@ -1,12 +1,7 @@
 import React, { useEffect } from 'react';
-import { 
-  useNavigate,
-} from 'react-router';
+import { useNavigate } from 'react-router';
 import { useAuth } from '../hooks/AuthProvider';
-
-
-
-
+import { Skeleton } from '@/client/components/ui/skeleton';
 
 export const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
   const { isAuthenticated, isLoading } = useAuth();
@@ -23,7 +18,13 @@ export const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
   if (isLoading) {
     return (
       <div className="flex justify-center items-center h-screen">
-        <div className="loader ease-linear rounded-full border-4 border-t-4 border-gray-200 h-12 w-12"></div>
+        <div className="space-y-2">
+          <Skeleton className="h-12 w-12 rounded-full" />
+          <div className="space-y-2">
+            <Skeleton className="h-4 w-[250px]" />
+            <Skeleton className="h-4 w-[200px]" />
+          </div>
+        </div>
       </div>
     );
   }

+ 21 - 25
src/client/admin-shadcn/index.tsx

@@ -1,12 +1,12 @@
 import { createRoot } from 'react-dom/client'
 import { RouterProvider } from 'react-router';
 import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-import { App as AntdApp , ConfigProvider} from 'antd'
+import { ThemeProvider } from '@/client/components/theme-provider';
+import { Toaster } from '@/client/components/ui/sonner';
 import dayjs from 'dayjs';
 import weekday from 'dayjs/plugin/weekday';
 import localeData from 'dayjs/plugin/localeData';
 import 'dayjs/locale/zh-cn';
-import zhCN from 'antd/locale/zh_CN';
 
 import { AuthProvider } from './hooks/AuthProvider';
 import { router } from './routes';
@@ -19,34 +19,30 @@ dayjs.extend(localeData);
 dayjs.locale('zh-cn');
 
 // 创建QueryClient实例
-const queryClient = new QueryClient();
+const queryClient = new QueryClient({
+  defaultOptions: {
+    queries: {
+      retry: 1,
+      refetchOnWindowFocus: false,
+    },
+  },
+});
 
 // 应用入口组件
 const App = () => {
   return (
     <QueryClientProvider client={queryClient}>
-      <ConfigProvider locale={zhCN} theme={{
-        token: {
-          colorPrimary: '#1890ff',
-          borderRadius: 4,
-          colorBgContainer: '#f5f5f5',
-        },
-        components: {
-          Button: {
-            borderRadius: 4,
-          },
-          Card: {
-            borderRadius: 6,
-            boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)',
-          }
-        }
-      }}>
-        <AntdApp>
-          <AuthProvider>
-            <RouterProvider router={router} />
-          </AuthProvider>
-        </AntdApp>
-      </ConfigProvider>
+      <ThemeProvider defaultTheme="light" storageKey="admin-theme">
+        <AuthProvider>
+          <RouterProvider router={router} />
+          <Toaster 
+            position="top-right"
+            expand={false}
+            richColors
+            closeButton
+          />
+        </AuthProvider>
+      </ThemeProvider>
     </QueryClientProvider>
   )
 };