diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index f3b9e6b..f883221 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -10,6 +10,7 @@ import { Tokens } from './pages/Tokens' import { Pricing } from './pages/Pricing' import { ApiTest } from './pages/ApiTest' import { Database } from './pages/Database' +import { NotFound } from './pages/NotFound' const queryClient = new QueryClient({ defaultOptions: { @@ -80,6 +81,7 @@ function App() { } /> + } /> diff --git a/frontend/src/components/layout/AppLayout.tsx b/frontend/src/components/layout/AppLayout.tsx index 1e0dd16..146918f 100644 --- a/frontend/src/components/layout/AppLayout.tsx +++ b/frontend/src/components/layout/AppLayout.tsx @@ -14,7 +14,7 @@ import { Button } from '@/components/ui/button' import { useAuthStore } from '@/store/auth' import { useToast } from '@/components/ui/use-toast' import { Alert, AlertDescription } from '@/components/ui/alert' -import { Menu, Sparkles } from 'lucide-react' +import { Menu, Zap } from 'lucide-react' interface AppLayoutProps { children: ReactNode @@ -56,23 +56,24 @@ export function AppLayout({ children }: AppLayoutProps) { />
-
+
-
-
- +
+
+
- Awsl One API + Awsl One API
-
+
diff --git a/frontend/src/components/layout/Sidebar.tsx b/frontend/src/components/layout/Sidebar.tsx index 7294d46..98ff960 100644 --- a/frontend/src/components/layout/Sidebar.tsx +++ b/frontend/src/components/layout/Sidebar.tsx @@ -28,40 +28,12 @@ interface NavItem { } const navItems: NavItem[] = [ - { - title: '仪表盘', - href: '/', - icon: LayoutDashboard, - }, - { - title: 'API 测试', - href: '/api-test', - icon: TestTube2, - }, - { - title: '频道管理', - href: '/channels', - icon: LinkIcon, - adminOnly: true, - }, - { - title: '令牌管理', - href: '/tokens', - icon: Key, - adminOnly: true, - }, - { - title: '定价管理', - href: '/pricing', - icon: DollarSign, - adminOnly: true, - }, - { - title: '数据库', - href: '/database', - icon: Database, - adminOnly: true, - }, + { title: '仪表盘', href: '/', icon: LayoutDashboard }, + { title: 'API 测试', href: '/api-test', icon: TestTube2 }, + { title: '频道管理', href: '/channels', icon: LinkIcon, adminOnly: true }, + { title: '令牌管理', href: '/tokens', icon: Key, adminOnly: true }, + { title: '定价管理', href: '/pricing', icon: DollarSign, adminOnly: true }, + { title: '数据库', href: '/database', icon: Database, adminOnly: true }, ] interface SidebarProps { @@ -125,16 +97,16 @@ export function Sidebar({ onClick={() => onNavigate?.()} title={collapsed ? item.title : undefined} className={cn( - 'group relative flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-all duration-200', + 'group relative flex items-center gap-3 px-3 py-2 rounded-lg text-[13px] font-medium transition-all duration-150', collapsed && 'justify-center px-0 w-10 h-10 mx-auto', isActive - ? 'bg-primary text-primary-foreground shadow-md' - : 'text-muted-foreground hover:text-foreground hover:bg-muted' + ? 'bg-primary text-primary-foreground shadow-sm shadow-primary/20' + : 'text-muted-foreground hover:text-foreground hover:bg-muted/80' )} > {!collapsed && {item.title}} @@ -144,37 +116,35 @@ export function Sidebar({ return ( ) diff --git a/frontend/src/components/ui/dialog.tsx b/frontend/src/components/ui/dialog.tsx index 7326a43..cb5a2c3 100644 --- a/frontend/src/components/ui/dialog.tsx +++ b/frontend/src/components/ui/dialog.tsx @@ -18,7 +18,7 @@ const DialogOverlay = React.forwardRef< * { + animation: animate-in 0.4s cubic-bezier(0.16, 1, 0.3, 1) both; + } + .stagger-children > *:nth-child(1) { animation-delay: 0ms; } + .stagger-children > *:nth-child(2) { animation-delay: 50ms; } + .stagger-children > *:nth-child(3) { animation-delay: 100ms; } + .stagger-children > *:nth-child(4) { animation-delay: 150ms; } + .stagger-children > *:nth-child(5) { animation-delay: 200ms; } + .stagger-children > *:nth-child(6) { animation-delay: 250ms; } + + /* Hover lift */ .hover-lift { - transition: transform 0.2s ease, box-shadow 0.2s ease; + transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.2s ease; } .hover-lift:hover { transform: translateY(-2px); box-shadow: - 0 4px 12px hsl(220 9% 46% / 0.1), - 0 8px 24px hsl(220 9% 46% / 0.1); + 0 0 0 1px hsl(220 9% 46% / 0.04), + 0 4px 12px hsl(220 9% 46% / 0.08), + 0 12px 24px -4px hsl(220 9% 46% / 0.08); } .dark .hover-lift:hover { box-shadow: + 0 0 0 1px hsl(0 0% 100% / 0.03), 0 4px 12px hsl(0 0% 0% / 0.3), - 0 8px 24px hsl(0 0% 0% / 0.3); + 0 12px 24px -4px hsl(0 0% 0% / 0.3); + } + + /* Gradient mesh background for hero */ + .gradient-mesh { + background: + radial-gradient(ellipse at 20% 50%, hsl(234 89% 60% / 0.08) 0%, transparent 50%), + radial-gradient(ellipse at 80% 20%, hsl(280 80% 60% / 0.06) 0%, transparent 50%), + radial-gradient(ellipse at 40% 80%, hsl(142 71% 45% / 0.05) 0%, transparent 50%); + } + + .dark .gradient-mesh { + background: + radial-gradient(ellipse at 20% 50%, hsl(234 89% 60% / 0.12) 0%, transparent 50%), + radial-gradient(ellipse at 80% 20%, hsl(280 80% 60% / 0.08) 0%, transparent 50%), + radial-gradient(ellipse at 40% 80%, hsl(142 71% 45% / 0.06) 0%, transparent 50%); + } + + /* Shimmer effect */ + @keyframes shimmer { + 0% { background-position: -200% 0; } + 100% { background-position: 200% 0; } + } + + .shimmer { + background: linear-gradient( + 90deg, + transparent 0%, + hsl(234 89% 60% / 0.06) 50%, + transparent 100% + ); + background-size: 200% 100%; + animation: shimmer 3s ease-in-out infinite; + } + + /* Pulse glow */ + @keyframes pulse-glow { + 0%, 100% { opacity: 0.4; } + 50% { opacity: 0.8; } + } + + .pulse-glow { + animation: pulse-glow 3s ease-in-out infinite; + } + + /* Grid pattern for backgrounds */ + .grid-pattern { + background-image: linear-gradient(hsl(220 9% 46% / 0.03) 1px, transparent 1px), + linear-gradient(90deg, hsl(220 9% 46% / 0.03) 1px, transparent 1px); + background-size: 32px 32px; + } + + .dark .grid-pattern { + background-image: linear-gradient(hsl(0 0% 100% / 0.02) 1px, transparent 1px), + linear-gradient(90deg, hsl(0 0% 100% / 0.02) 1px, transparent 1px); + background-size: 32px 32px; } } diff --git a/frontend/src/pages/ApiTest.tsx b/frontend/src/pages/ApiTest.tsx index 1381cf4..401dbd3 100644 --- a/frontend/src/pages/ApiTest.tsx +++ b/frontend/src/pages/ApiTest.tsx @@ -1,5 +1,5 @@ import { useState } from 'react' -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { Card, CardContent } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' @@ -8,8 +8,9 @@ import { Select } from '@/components/ui/select' import { Badge } from '@/components/ui/badge' import { useToast } from '@/components/ui/use-toast' import { apiClient } from '@/api/client' -import { Send } from 'lucide-react' +import { Send, Clock, CheckCircle, XCircle, Copy } from 'lucide-react' import { PageContainer } from '@/components/ui/page-container' +import { cn, copyToClipboard } from '@/lib/utils' const requestTemplates: Record = { '/v1/chat/completions': { @@ -63,19 +64,30 @@ export function ApiTest() { setRequestBody(JSON.stringify(requestTemplates[newEndpoint], null, 2)) } + const handleCopyResponse = async () => { + if (response) { + try { + await copyToClipboard(JSON.stringify(response, null, 2)) + addToast('已复制到剪贴板', 'success') + } catch { + addToast('复制失败', 'error') + } + } + } + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (!apiToken) { - addToast('请输入API令牌', 'error') + addToast('请输入 API 令牌', 'error') return } let body: any try { body = JSON.parse(requestBody) - } catch (error) { - addToast('请求体JSON格式错误', 'error') + } catch { + addToast('请求体 JSON 格式错误', 'error') return } @@ -91,32 +103,34 @@ export function ApiTest() { setResponseTime(endTime - startTime) setResponse(result) setStatusCode(200) - addToast('请求成功', 'success') } catch (error: any) { const endTime = Date.now() setResponseTime(endTime - startTime) setStatusCode(error.status || 500) setResponse({ error: error.message }) - addToast('请求失败', 'error') } finally { setIsLoading(false) } } return ( - - - - API 测试工具 - 测试您的 API 连接和配置 - - -
-
+ +
+ {/* Request Panel */} + + +
+
+

请求

+
+ +
- + setApiToken(e.target.value)} placeholder="sk-..." - required />
-
-
- -