Browse Source

✨ feat(ui): 优化错误页面和保护路由UI设计

- ErrorPage: 使用Card和Alert组件重构,添加错误详情和技术信息展示
- NotFoundPage: 改进404页面设计,增加返回上一页功能和视觉引导
- ProtectedRoute: 优化加载状态,使用Skeleton组件提供更好的加载体验
- MemberPage: 修复容器居中问题,添加mx-auto确保居中显示

💄 style(ui): 统一使用shadcn组件和slate主题色

- 替换自定义样式为shadcn组件系统
- 统一使用slate色系替换原有gray色系
- 优化按钮和卡片的视觉层次和交互效果
- 添加图标增强视觉引导和用户体验
yourname 4 tháng trước cách đây
mục cha
commit
bbd4f2c733

+ 52 - 36
src/client/home-shadcn/components/ErrorPage.tsx

@@ -1,5 +1,9 @@
 import React from 'react';
 import { useRouteError, useNavigate } from 'react-router';
+import { AlertCircle, RotateCcw, Home } from 'lucide-react';
+import { Button } from '@/client/components/ui/button';
+import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/client/components/ui/card';
+import { Alert, AlertDescription, AlertTitle } from '@/client/components/ui/alert';
 
 export const ErrorPage = () => {
   const navigate = useNavigate();
@@ -7,43 +11,55 @@ export const ErrorPage = () => {
   const errorMessage = error?.statusText || error?.message || '未知错误';
   
   return (
-    <div className="flex flex-col items-center justify-center min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 p-4">
-      <div className="w-full max-w-md bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden transition-all duration-300 hover:shadow-xl">
-        <div className="bg-red-50 dark:bg-red-900/30 px-6 py-4 border-b border-red-100 dark:border-red-800">
-          <h1 className="text-2xl font-bold text-red-600 dark:text-red-400">发生错误</h1>
-        </div>
-        <div className="p-6">
-          <div className="flex items-start mb-4">
-            <div className="flex-shrink-0 bg-red-100 dark:bg-red-900/50 p-3 rounded-full">
-              <svg className="w-8 h-8 text-red-500 dark:text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
-                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
-              </svg>
+    <div className="flex flex-col items-center justify-center min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800 p-4">
+      <Card className="w-full max-w-md border-0 shadow-xl">
+        <CardHeader className="bg-destructive/10">
+          <CardTitle className="flex items-center space-x-2 text-destructive">
+            <AlertCircle className="h-6 w-6" />
+            <span>发生错误</span>
+          </CardTitle>
+          <CardDescription>
+            抱歉,页面加载时遇到了问题
+          </CardDescription>
+        </CardHeader>
+        
+        <CardContent className="space-y-4">
+          <Alert variant="destructive">
+            <AlertCircle className="h-4 w-4" />
+            <AlertTitle>错误详情</AlertTitle>
+            <AlertDescription>
+              {error?.message || '未知错误'}
+            </AlertDescription>
+          </Alert>
+          
+          {error?.stack && (
+            <div className="space-y-2">
+              <h4 className="text-sm font-medium text-muted-foreground">技术信息</h4>
+              <pre className="text-xs text-muted-foreground bg-muted/50 p-3 rounded-lg overflow-x-auto max-h-40">
+                {error.stack}
+              </pre>
             </div>
-            <div className="ml-4">
-              <h3 className="text-lg font-medium text-gray-900 dark:text-white">{error?.message || '未知错误'}</h3>
-              {error?.stack && (
-                <pre className="mt-2 text-xs text-gray-600 dark:text-gray-300 bg-gray-50 dark:bg-gray-700 p-3 rounded overflow-x-auto max-h-40">
-                  {error.stack}
-                </pre>
-              )}
-            </div>
-          </div>
-          <div className="flex gap-4">
-            <button
-              onClick={() => navigate(0)}
-              className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition-colors duration-200"
-            >
-              重新加载
-            </button>
-            <button
-              onClick={() => navigate('/')}
-              className="inline-flex items-center px-4 py-2 border border-gray-300 dark:border-gray-600 shadow-sm text-sm font-medium rounded-md text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition-colors duration-200"
-            >
-              返回首页
-            </button>
-          </div>
-        </div>
-      </div>
+          )}
+        </CardContent>
+        
+        <CardFooter className="flex gap-2">
+          <Button
+            onClick={() => navigate(0)}
+            className="flex-1"
+            variant="outline"
+          >
+            <RotateCcw className="h-4 w-4 mr-2" />
+            重新加载
+          </Button>
+          <Button
+            onClick={() => navigate('/')}
+            className="flex-1"
+          >
+            <Home className="h-4 w-4 mr-2" />
+            返回首页
+          </Button>
+        </CardFooter>
+      </Card>
     </div>
   );
 };

+ 35 - 21
src/client/home-shadcn/components/NotFoundPage.tsx

@@ -1,35 +1,49 @@
 import React from 'react';
 import { useNavigate } from 'react-router';
+import { ArrowLeft, Home } from 'lucide-react';
+import { Button } from '@/client/components/ui/button';
+import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/client/components/ui/card';
 
 export const NotFoundPage = () => {
   const navigate = useNavigate();
   
   return (
-    <div className="flex flex-col items-center justify-center min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 p-4">
-      <div className="w-full max-w-md bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden transition-all duration-300 hover:shadow-xl">
-        <div className="bg-blue-50 dark:bg-blue-900/30 px-6 py-4 border-b border-blue-100 dark:border-blue-800">
-          <h1 className="text-2xl font-bold text-blue-600 dark:text-blue-400">404 - 页面未找到</h1>
-        </div>
-        <div className="p-6">
-          <div className="flex items-start mb-6">
-            <div className="flex-shrink-0 bg-blue-100 dark:bg-blue-900/50 p-3 rounded-full">
-              <svg className="w-10 h-10 text-blue-500 dark:text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
-                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
-              </svg>
-            </div>
-            <div className="ml-4">
-              <p className="text-lg text-gray-700 dark:text-gray-300">您访问的页面不存在或已被移除</p>
-              <p className="mt-2 text-gray-500 dark:text-gray-400">请检查URL是否正确或返回首页</p>
-            </div>
+    <div className="flex flex-col items-center justify-center min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800 p-4">
+      <Card className="w-full max-w-md border-0 shadow-xl">
+        <CardHeader className="text-center">
+          <div className="mx-auto flex h-20 w-20 items-center justify-center rounded-full bg-primary/10 mb-4">
+            <span className="text-4xl font-bold text-primary">404</span>
           </div>
-          <button
+          <CardTitle className="text-2xl">页面未找到</CardTitle>
+          <CardDescription>
+            抱歉,您访问的页面不存在或已被移除
+          </CardDescription>
+        </CardHeader>
+        
+        <CardContent className="text-center">
+          <p className="text-muted-foreground">
+            请检查URL是否正确,或尝试以下操作:
+          </p>
+        </CardContent>
+        
+        <CardFooter className="flex gap-2">
+          <Button
+            onClick={() => navigate(-1)}
+            variant="outline"
+            className="flex-1"
+          >
+            <ArrowLeft className="h-4 w-4 mr-2" />
+            返回上一页
+          </Button>
+          <Button
             onClick={() => navigate('/')}
-            className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors duration-200 w-full"
+            className="flex-1"
           >
+            <Home className="h-4 w-4 mr-2" />
             返回首页
-          </button>
-        </div>
-      </div>
+          </Button>
+        </CardFooter>
+      </Card>
     </div>
   );
 };

+ 16 - 9
src/client/home-shadcn/components/ProtectedRoute.tsx

@@ -1,12 +1,8 @@
 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';
+import { Card, CardContent } from '@/client/components/ui/card';
 
 export const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
   const { isAuthenticated, isLoading } = useAuth();
@@ -22,8 +18,19 @@ 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="flex justify-center items-center min-h-screen bg-gradient-to-br from-slate-50 to-slate-100">
+        <Card className="border-0 shadow-lg">
+          <CardContent className="flex flex-col items-center space-y-4 py-12 px-8">
+            <div className="relative">
+              <Skeleton className="h-12 w-12 rounded-full" />
+              <div className="absolute inset-0 rounded-full bg-primary/20 animate-pulse" />
+            </div>
+            <div className="space-y-2 text-center">
+              <Skeleton className="h-4 w-32" />
+              <Skeleton className="h-3 w-24" />
+            </div>
+          </CardContent>
+        </Card>
       </div>
     );
   }

+ 1 - 1
src/client/home-shadcn/pages/MemberPage.tsx

@@ -44,7 +44,7 @@ const MemberPage: React.FC = () => {
 
   return (
     <div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100">
-      <div className="container py-8">
+      <div className="container mx-auto py-8">
         <div className="mx-auto max-w-4xl space-y-8">
           {/* 用户资料卡片 */}
           <Card className="border-0 shadow-lg">