Ver Fonte

优化前端

lxylxy123321 há 6 dias atrás
pai
commit
b8e2af8def

+ 10 - 0
frontend/package-lock.json

@@ -9,6 +9,7 @@
       "version": "0.1.0",
       "dependencies": {
         "axios": "^1.7.7",
+        "lucide-react": "^1.16.0",
         "react": "^18.3.1",
         "react-dom": "^18.3.1",
         "react-dropzone": "^14.3.5",
@@ -1964,6 +1965,15 @@
         "yallist": "^3.0.2"
       }
     },
+    "node_modules/lucide-react": {
+      "version": "1.16.0",
+      "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-1.16.0.tgz",
+      "integrity": "sha512-dYwyPzb4MEKpGUmNYk3WKWPnMrHs3FKM+q94kAnJrcDIqqn1hq2xY8scaS2ovsOCM5D51ey2gaRG3PBb1vgoYQ==",
+      "license": "ISC",
+      "peerDependencies": {
+        "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+      }
+    },
     "node_modules/math-intrinsics": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",

+ 5 - 4
frontend/package.json

@@ -10,14 +10,15 @@
     "lint": "eslint ."
   },
   "dependencies": {
+    "axios": "^1.7.7",
+    "lucide-react": "^1.16.0",
     "react": "^18.3.1",
     "react-dom": "^18.3.1",
+    "react-dropzone": "^14.3.5",
     "react-router-dom": "^6.28.0",
-    "zustand": "^4.5.5",
-    "axios": "^1.7.7",
     "recharts": "^2.13.3",
-    "react-dropzone": "^14.3.5",
-    "sonner": "^1.7.0"
+    "sonner": "^1.7.0",
+    "zustand": "^4.5.5"
   },
   "devDependencies": {
     "@types/react": "^18.3.12",

+ 1 - 1
frontend/src/App.tsx

@@ -14,7 +14,7 @@ const Login = lazy(() => import('./pages/AuthLogin').then(m => ({ default: m.Log
 const AuthCallback = lazy(() => import('./pages/AuthCallback').then(m => ({ default: m.AuthCallback })))
 
 function PageFallback() {
-  return <div style={{ padding: 24, color: '#999' }}>加载中...</div>
+  return <div style={{ padding: 24, color: '#94a3b8' }}>加载中...</div>
 }
 
 function AuthRoute({ children }: { children: React.ReactNode }) {

+ 37 - 34
frontend/src/components/layout/Layout.tsx

@@ -1,14 +1,15 @@
 import { Link, useLocation } from 'react-router-dom'
 import { useAuth } from '../../contexts/AuthContext'
+import { BarChart3, Cpu, Database, Train, TrendingUp, CloudUpload, MessageSquare } from 'lucide-react'
 
 const NAV_ITEMS = [
-  { path: '/', label: '仪表盘', icon: '📊' },
-  { path: '/models', label: '模型', icon: '🧠' },
-  { path: '/datasets', label: '数据集', icon: '📁' },
-  { path: '/training', label: '训练', icon: '⚡' },
-  { path: '/evaluation', label: '评估', icon: '📈' },
-  { path: '/deployment', label: '部署', icon: '🚀' },
-  { path: '/inference', label: '推理', icon: '💬' },
+  { path: '/', label: '仪表盘', icon: BarChart3 },
+  { path: '/models', label: '模型', icon: Cpu },
+  { path: '/datasets', label: '数据集', icon: Database },
+  { path: '/training', label: '训练', icon: Train },
+  { path: '/evaluation', label: '评估', icon: TrendingUp },
+  { path: '/deployment', label: '部署', icon: CloudUpload },
+  { path: '/inference', label: '推理', icon: MessageSquare },
 ]
 
 export function Layout({ children }: { children: React.ReactNode }) {
@@ -17,21 +18,22 @@ export function Layout({ children }: { children: React.ReactNode }) {
   const isActive = (path: string) => location.pathname === path
 
   return (
-    <div style={{ display: 'flex', minHeight: '100vh', fontFamily: 'system-ui, -apple-system, sans-serif', background: '#f8f9fa' }}>
+    <div style={{ display: 'flex', minHeight: '100vh', fontFamily: 'system-ui, -apple-system, sans-serif', background: '#f0fdfa' }}>
       <nav style={{
         width: 220,
-        background: 'linear-gradient(180deg, #1a1a2e 0%, #16213e 100%)',
-        color: '#fff',
+        background: 'linear-gradient(180deg, #ffffff 0%, #f0fdfa 100%)',
+        color: '#134e4a',
         display: 'flex',
         flexDirection: 'column',
-        boxShadow: '2px 0 8px rgba(0,0,0,0.1)',
+        boxShadow: '2px 0 8px rgba(0,0,0,0.04)',
+        borderRight: '1px solid rgba(0,0,0,0.06)',
       }}>
         {/* Logo / Brand */}
-        <div style={{ padding: '24px 20px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>
-          <h2 style={{ fontSize: 18, fontWeight: 800, margin: 0, letterSpacing: '-0.5px' }}>
+        <div style={{ padding: '24px 20px', borderBottom: '1px solid rgba(0,0,0,0.06)' }}>
+          <h2 style={{ fontSize: 18, fontWeight: 800, margin: 0, letterSpacing: '-0.5px', color: '#134e4a' }}>
             PEFT Platform
           </h2>
-          <p style={{ fontSize: 11, color: 'rgba(255,255,255,0.4)', margin: '4px 0 0' }}>
+          <p style={{ fontSize: 11, color: '#94a3b8', margin: '4px 0 0' }}>
             Fine-Tuning Studio
           </p>
         </div>
@@ -50,8 +52,8 @@ export function Layout({ children }: { children: React.ReactNode }) {
                   gap: 10,
                   padding: '10px 14px',
                   textDecoration: 'none',
-                  color: active ? '#fff' : 'rgba(255,255,255,0.55)',
-                  background: active ? 'rgba(233,69,96,0.15)' : 'transparent',
+                  color: active ? '#14b8a6' : '#64748b',
+                  background: active ? 'rgba(20,184,166,0.12)' : 'transparent',
                   borderRadius: 8,
                   fontSize: 14,
                   fontWeight: active ? 600 : 400,
@@ -60,18 +62,18 @@ export function Layout({ children }: { children: React.ReactNode }) {
                 }}
                 onMouseEnter={e => {
                   if (!active) {
-                    e.currentTarget.style.background = 'rgba(255,255,255,0.06)'
-                    e.currentTarget.style.color = 'rgba(255,255,255,0.85)'
+                    e.currentTarget.style.background = 'rgba(20,184,166,0.06)'
+                    e.currentTarget.style.color = '#14b8a6'
                   }
                 }}
                 onMouseLeave={e => {
                   if (!active) {
                     e.currentTarget.style.background = 'transparent'
-                    e.currentTarget.style.color = 'rgba(255,255,255,0.55)'
+                    e.currentTarget.style.color = '#64748b'
                   }
                 }}
               >
-                <span style={{ fontSize: 16, width: 22, textAlign: 'center' }}>{item.icon}</span>
+                <item.icon size={18} strokeWidth={1.8} color={active ? '#14b8a6' : '#64748b'} />
                 {item.label}
               </Link>
             )
@@ -79,13 +81,13 @@ export function Layout({ children }: { children: React.ReactNode }) {
         </div>
 
         {/* User section */}
-        <div style={{ padding: '16px 12px', borderTop: '1px solid rgba(255,255,255,0.08)' }}>
+        <div style={{ padding: '16px 12px', borderTop: '1px solid rgba(0,0,0,0.06)' }}>
           <div style={{
             display: 'flex',
             alignItems: 'center',
             gap: 10,
             padding: '8px 10px',
-            background: 'rgba(255,255,255,0.04)',
+            background: 'rgba(20,184,166,0.06)',
             borderRadius: 8,
             marginBottom: 10,
           }}>
@@ -93,21 +95,22 @@ export function Layout({ children }: { children: React.ReactNode }) {
               width: 32,
               height: 32,
               borderRadius: '50%',
-              background: 'linear-gradient(135deg, #e94560, #c23656)',
+              background: 'linear-gradient(135deg, #14b8a6, #0d9488)',
               display: 'flex',
               alignItems: 'center',
               justifyContent: 'center',
               fontSize: 14,
               fontWeight: 700,
+              color: '#fff',
               flexShrink: 0,
             }}>
               {(user?.real_name || user?.username || 'U').charAt(0).toUpperCase()}
             </div>
             <div style={{ overflow: 'hidden', flex: 1 }}>
-              <div style={{ fontSize: 13, fontWeight: 500, color: '#fff', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
+              <div style={{ fontSize: 13, fontWeight: 500, color: '#134e4a', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                 {user?.real_name || user?.username || '用户'}
               </div>
-              <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.4)' }}>在线</div>
+              <div style={{ fontSize: 11, color: '#94a3b8' }}>在线</div>
             </div>
           </div>
           <button
@@ -115,23 +118,23 @@ export function Layout({ children }: { children: React.ReactNode }) {
             style={{
               width: '100%',
               padding: '8px 12px',
-              background: 'rgba(255,255,255,0.04)',
-              border: '1px solid rgba(255,255,255,0.1)',
+              background: 'rgba(255,255,255,0.6)',
+              border: '1px solid rgba(0,0,0,0.08)',
               borderRadius: 8,
-              color: 'rgba(255,255,255,0.6)',
+              color: '#64748b',
               cursor: 'pointer',
               fontSize: 13,
               transition: 'all 0.15s ease',
             }}
             onMouseEnter={e => {
-              e.currentTarget.style.background = 'rgba(233,69,96,0.15)'
-              e.currentTarget.style.borderColor = 'rgba(233,69,96,0.3)'
-              e.currentTarget.style.color = '#e94560'
+              e.currentTarget.style.background = 'rgba(244,63,94,0.1)'
+              e.currentTarget.style.borderColor = 'rgba(244,63,94,0.3)'
+              e.currentTarget.style.color = '#f43f5e'
             }}
             onMouseLeave={e => {
-              e.currentTarget.style.background = 'rgba(255,255,255,0.04)'
-              e.currentTarget.style.borderColor = 'rgba(255,255,255,0.1)'
-              e.currentTarget.style.color = 'rgba(255,255,255,0.6)'
+              e.currentTarget.style.background = 'rgba(255,255,255,0.6)'
+              e.currentTarget.style.borderColor = 'rgba(0,0,0,0.08)'
+              e.currentTarget.style.color = '#64748b'
             }}
           >
             退出登录

+ 4 - 0
frontend/src/main.tsx

@@ -8,6 +8,10 @@ import App from './App'
 createRoot(document.getElementById('root')!).render(
   <StrictMode>
     <BrowserRouter>
+      <style>{`
+        body { margin: 0; background: #f0fdfa; }
+        @keyframes lucide-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
+      `}</style>
       <AuthProvider>
         <App />
         <Toaster position="top-right" />

+ 4 - 4
frontend/src/pages/AuthCallback.tsx

@@ -39,16 +39,16 @@ export function AuthCallback() {
 
   if (error) {
     return (
-      <div style={{ padding: 40, textAlign: 'center', background: '#0f0f23', minHeight: '100vh', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', color: '#fff' }}>
+      <div style={{ padding: 40, textAlign: 'center', background: '#f0fdfa', minHeight: '100vh', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', color: '#134e4a' }}>
         <h2>登录失败</h2>
-        <p style={{ color: '#e94560' }}>{error}</p>
-        <p style={{ color: '#999', fontSize: 14 }}>3秒后跳转到登录页...</p>
+        <p style={{ color: '#f43f5e' }}>{error}</p>
+        <p style={{ color: '#94a3b8', fontSize: 14 }}>3秒后跳转到登录页...</p>
       </div>
     )
   }
 
   return (
-    <div style={{ padding: 40, textAlign: 'center', background: '#0f0f23', minHeight: '100vh', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', color: '#fff' }}>
+    <div style={{ padding: 40, textAlign: 'center', background: '#f0fdfa', minHeight: '100vh', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', color: '#134e4a' }}>
       <h2>正在登录,请稍候...</h2>
     </div>
   )

+ 23 - 22
frontend/src/pages/AuthLogin.tsx

@@ -1,11 +1,12 @@
 import { useEffect } from 'react'
 import { useNavigate } from 'react-router-dom'
 import { useAuth } from '../contexts/AuthContext'
+import { Sparkles, Train, Cpu, BarChart3 } from 'lucide-react'
 
 const FEATURES = [
-  { icon: '⚡', text: 'LoRA / QLoRA / IA3 等多种微调方法' },
-  { icon: '🧠', text: '支持 LLaMA、Qwen 等主流模型' },
-  { icon: '📊', text: '可视化训练监控与评估' },
+  { icon: Train, text: 'LoRA / QLoRA / IA3 等多种微调方法' },
+  { icon: Cpu, text: '支持 LLaMA、Qwen 等主流模型' },
+  { icon: BarChart3, text: '可视化训练监控与评估' },
 ]
 
 export function Login() {
@@ -24,46 +25,46 @@ export function Login() {
     <div style={{
       display: 'flex', justifyContent: 'center', alignItems: 'center',
       minHeight: '100vh',
-      background: 'linear-gradient(135deg, #0f0f23 0%, #1a1a2e 50%, #16213e 100%)',
+      background: 'linear-gradient(135deg, #f0fdfa 0%, #ccfbf1 50%, #e0f2fe 100%)',
       position: 'relative',
       overflow: 'hidden',
     }}>
       {/* 装饰性背景圆 */}
       <div style={{
         position: 'absolute', width: 500, height: 500, borderRadius: '50%',
-        background: 'radial-gradient(circle, rgba(233,69,96,0.08) 0%, transparent 70%)',
+        background: 'radial-gradient(circle, rgba(20,184,166,0.08) 0%, transparent 70%)',
         top: -150, right: -100, pointerEvents: 'none',
       }} />
       <div style={{
         position: 'absolute', width: 400, height: 400, borderRadius: '50%',
-        background: 'radial-gradient(circle, rgba(33,150,243,0.06) 0%, transparent 70%)',
+        background: 'radial-gradient(circle, rgba(14,165,233,0.06) 0%, transparent 70%)',
         bottom: -100, left: -80, pointerEvents: 'none',
       }} />
 
       <div style={{
         position: 'relative', zIndex: 1,
         width: 440,
-        background: 'rgba(26,26,46,0.95)',
+        background: 'rgba(255,255,255,0.95)',
         backdropFilter: 'blur(10px)',
         borderRadius: 16,
         padding: '40px 36px',
-        border: '1px solid rgba(255,255,255,0.08)',
-        boxShadow: '0 8px 40px rgba(0,0,0,0.5)',
+        border: '1px solid rgba(0,0,0,0.06)',
+        boxShadow: '0 8px 40px rgba(20,184,166,0.1)',
       }}>
         {/* Header */}
         <div style={{ textAlign: 'center', marginBottom: 32 }}>
           <div style={{
             width: 56, height: 56, borderRadius: 16, margin: '0 auto 16px',
-            background: 'linear-gradient(135deg, #e94560, #c23656)',
+            background: 'linear-gradient(135deg, #14b8a6, #0d9488)',
             display: 'flex', alignItems: 'center', justifyContent: 'center',
-            fontSize: 24, boxShadow: '0 4px 16px rgba(233,69,96,0.3)',
+            boxShadow: '0 4px 16px rgba(20,184,166,0.25)',
           }}>
-            🎯
+            <Sparkles size={24} color="#fff" />
           </div>
-          <h1 style={{ fontSize: 22, fontWeight: 800, margin: '0 0 6px', color: '#fff', letterSpacing: '-0.5px' }}>
+          <h1 style={{ fontSize: 22, fontWeight: 800, margin: '0 0 6px', color: '#134e4a', letterSpacing: '-0.5px' }}>
             PEFT Fine-Tuning Platform
           </h1>
-          <p style={{ color: 'rgba(255,255,255,0.4)', fontSize: 13, margin: 0 }}>
+          <p style={{ color: '#94a3b8', fontSize: 13, margin: 0 }}>
             高效、易用的参数高效微调平台
           </p>
         </div>
@@ -74,10 +75,10 @@ export function Login() {
             <div key={i} style={{
               display: 'flex', alignItems: 'center', gap: 10,
               padding: '8px 12px', marginBottom: i < FEATURES.length - 1 ? 4 : 0,
-              background: 'rgba(255,255,255,0.03)', borderRadius: 8,
+              background: 'rgba(20,184,166,0.04)', borderRadius: 8,
             }}>
-              <span style={{ fontSize: 16 }}>{f.icon}</span>
-              <span style={{ fontSize: 13, color: 'rgba(255,255,255,0.6)' }}>{f.text}</span>
+              <f.icon size={16} color="#14b8a6" strokeWidth={1.8} />
+              <span style={{ fontSize: 13, color: '#64748b' }}>{f.text}</span>
             </div>
           ))}
         </div>
@@ -88,26 +89,26 @@ export function Login() {
           style={{
             width: '100%', padding: '14px 24px', borderRadius: 10,
             border: 'none',
-            background: 'linear-gradient(135deg, #e94560, #c23656)',
+            background: 'linear-gradient(135deg, #14b8a6, #0d9488)',
             color: '#fff', fontSize: 15, fontWeight: 600,
             cursor: 'pointer',
-            boxShadow: '0 4px 16px rgba(233,69,96,0.3)',
+            boxShadow: '0 4px 16px rgba(20,184,166,0.25)',
             transition: 'all 0.2s ease',
           }}
           onMouseEnter={e => {
             e.currentTarget.style.transform = 'translateY(-1px)'
-            e.currentTarget.style.boxShadow = '0 6px 20px rgba(233,69,96,0.4)'
+            e.currentTarget.style.boxShadow = '0 6px 20px rgba(20,184,166,0.3)'
           }}
           onMouseLeave={e => {
             e.currentTarget.style.transform = 'translateY(0)'
-            e.currentTarget.style.boxShadow = '0 4px 16px rgba(233,69,96,0.3)'
+            e.currentTarget.style.boxShadow = '0 4px 16px rgba(20,184,166,0.25)'
           }}
         >
           使用统一认证平台登录
         </button>
 
         {/* Footer */}
-        <p style={{ textAlign: 'center', fontSize: 12, color: 'rgba(255,255,255,0.25)', margin: '24px 0 0' }}>
+        <p style={{ textAlign: 'center', fontSize: 12, color: '#cbd5e1', margin: '24px 0 0' }}>
           登录后即可使用全部微调功能
         </p>
       </div>

+ 25 - 24
frontend/src/pages/Dashboard.tsx

@@ -1,14 +1,15 @@
 import { useState, useEffect, memo } from 'react'
 import api from '../api/client'
+import { Cpu, Database, Train, Download, Upload, CloudUpload } from 'lucide-react'
 
 const STAT_CARDS = [
-  { title: '模型', icon: '🧠', desc: '已缓存', color: '#e94560', bg: '#fff5f7' },
-  { title: '数据集', icon: '📁', desc: '已上传', color: '#2196f3', bg: '#f0f7ff' },
-  { title: '训练任务', icon: '⚡', desc: '总数', color: '#4caf50', bg: '#f0fff4' },
+  { title: '模型', icon: Cpu, desc: '已缓存', color: '#14b8a6', bg: '#ccfbf1' },
+  { title: '数据集', icon: Database, desc: '已上传', color: '#0ea5e9', bg: '#e0f2fe' },
+  { title: '训练任务', icon: Train, desc: '总数', color: '#10b981', bg: '#d1fae5' },
 ]
 
-const StatCard = memo(function StatCard({ title, value, desc, icon, color, bg }: {
-  title: string; value: number; desc: string; icon: string; color: string; bg: string
+const StatCard = memo(function StatCard({ title, value, desc, icon: Icon, color, bg }: {
+  title: string; value: number; desc: string; icon: typeof Cpu; color: string; bg: string
 }) {
   return (
     <div style={{
@@ -20,7 +21,7 @@ const StatCard = memo(function StatCard({ title, value, desc, icon, color, bg }:
     }}
     onMouseEnter={e => {
       e.currentTarget.style.transform = 'translateY(-2px)'
-      e.currentTarget.style.boxShadow = '0 4px 12px rgba(0,0,0,0.1)'
+      e.currentTarget.style.boxShadow = '0 4px 12px rgba(20,184,166,0.12)'
     }}
     onMouseLeave={e => {
       e.currentTarget.style.transform = 'translateY(0)'
@@ -28,18 +29,18 @@ const StatCard = memo(function StatCard({ title, value, desc, icon, color, bg }:
     }}
     >
       <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 }}>
-        <div style={{ fontSize: 13, color: '#666', fontWeight: 500 }}>{title}</div>
+        <div style={{ fontSize: 13, color: '#64748b', fontWeight: 500 }}>{title}</div>
         <div style={{
           width: 36, height: 36, borderRadius: 10, background: bg,
-          display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 18,
+          display: 'flex', alignItems: 'center', justifyContent: 'center',
         }}>
-          {icon}
+          <Icon size={18} color={color} strokeWidth={1.8} />
         </div>
       </div>
       <div style={{ fontSize: 36, fontWeight: 800, margin: '4px 0', color, letterSpacing: '-1px' }}>
         {value}
       </div>
-      <div style={{ fontSize: 12, color: '#999' }}>{desc}</div>
+      <div style={{ fontSize: 12, color: '#94a3b8' }}>{desc}</div>
     </div>
   )
 })
@@ -67,10 +68,10 @@ export function Dashboard() {
     <div>
       {/* Header */}
       <div style={{ marginBottom: 28 }}>
-        <h1 style={{ margin: 0, fontSize: 24, fontWeight: 800, color: '#1a1a2e', letterSpacing: '-0.5px' }}>
+        <h1 style={{ margin: 0, fontSize: 24, fontWeight: 800, color: '#134e4a', letterSpacing: '-0.5px' }}>
           仪表盘
         </h1>
-        <p style={{ color: '#888', fontSize: 14, margin: '6px 0 0' }}>
+        <p style={{ color: '#64748b', fontSize: 14, margin: '6px 0 0' }}>
           PEFT Fine-Tuning Platform v0.1.0 — 欢迎回来
         </p>
       </div>
@@ -83,10 +84,10 @@ export function Dashboard() {
               background: '#fff', borderRadius: 12, padding: '20px 24px',
               boxShadow: '0 1px 3px rgba(0,0,0,0.06)', border: '1px solid rgba(0,0,0,0.04)',
             }}>
-              <div style={{ height: 36, marginBottom: 12, background: '#f0f0f0', borderRadius: 10, width: 36, marginLeft: 'auto' }} />
-              <div style={{ height: 14, width: 60, background: '#f0f0f0', borderRadius: 4, marginBottom: 12 }} />
-              <div style={{ height: 36, width: 80, background: '#f0f0f0', borderRadius: 6, marginBottom: 8 }} />
-              <div style={{ height: 12, width: 40, background: '#f0f0f0', borderRadius: 4 }} />
+              <div style={{ height: 36, marginBottom: 12, background: '#f1f5f9', borderRadius: 10, width: 36, marginLeft: 'auto' }} />
+              <div style={{ height: 14, width: 60, background: '#f1f5f9', borderRadius: 4, marginBottom: 12 }} />
+              <div style={{ height: 36, width: 80, background: '#f1f5f9', borderRadius: 6, marginBottom: 8 }} />
+              <div style={{ height: 12, width: 40, background: '#f1f5f9', borderRadius: 4 }} />
             </div>
           ))
         ) : (
@@ -98,12 +99,12 @@ export function Dashboard() {
 
       {/* Quick actions */}
       <div style={{ marginTop: 32 }}>
-        <h2 style={{ margin: '0 0 16px', fontSize: 16, fontWeight: 700, color: '#1a1a2e' }}>快速开始</h2>
+        <h2 style={{ margin: '0 0 16px', fontSize: 16, fontWeight: 700, color: '#134e4a' }}>快速开始</h2>
         <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 16 }}>
           {[
-            { label: '下载模型', desc: '从 HuggingFace 或 ModelScope 下载', icon: '📥', link: '/models', color: '#e94560' },
-            { label: '上传数据集', desc: '支持 JSONL / CSV / Parquet', icon: '📤', link: '/datasets', color: '#2196f3' },
-            { label: '创建训练', desc: '配置超参数并启动微调', icon: '🚀', link: '/training', color: '#4caf50' },
+            { label: '下载模型', desc: '从 HuggingFace 或 ModelScope 下载', icon: Download, link: '/models', color: '#14b8a6' },
+            { label: '上传数据集', desc: '支持 JSONL / CSV / Parquet', icon: Upload, link: '/datasets', color: '#0ea5e9' },
+            { label: '创建训练', desc: '配置超参数并启动微调', icon: CloudUpload, link: '/training', color: '#10b981' },
           ].map(item => (
             <a
               key={item.label}
@@ -128,13 +129,13 @@ export function Dashboard() {
             >
               <div style={{
                 width: 40, height: 40, borderRadius: 10, background: item.color + '10',
-                display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 20,
+                display: 'flex', alignItems: 'center', justifyContent: 'center',
               }}>
-                {item.icon}
+                <item.icon size={20} color={item.color} strokeWidth={1.8} />
               </div>
               <div>
-                <div style={{ fontSize: 14, fontWeight: 600, color: '#1a1a2e' }}>{item.label}</div>
-                <div style={{ fontSize: 12, color: '#999', marginTop: 2 }}>{item.desc}</div>
+                <div style={{ fontSize: 14, fontWeight: 600, color: '#134e4a' }}>{item.label}</div>
+                <div style={{ fontSize: 12, color: '#94a3b8', marginTop: 2 }}>{item.desc}</div>
               </div>
             </a>
           ))}

+ 49 - 42
frontend/src/pages/Datasets.tsx

@@ -1,5 +1,6 @@
 import { useState, useRef, memo } from 'react'
 import api, { DatasetInfo } from '../api/client'
+import { Database, Upload, Loader2 } from 'lucide-react'
 
 const DatasetRow = memo(function DatasetRow({ d, onPreview, onDelete }: {
   d: DatasetInfo
@@ -8,39 +9,39 @@ const DatasetRow = memo(function DatasetRow({ d, onPreview, onDelete }: {
 }) {
   return (
     <tr style={{
-      borderBottom: '1px solid #f0f0f0',
+      borderBottom: '1px solid #f1f5f9',
       transition: 'background 0.15s ease',
     }}
-    onMouseEnter={e => { e.currentTarget.style.background = '#fafbfc' }}
+    onMouseEnter={e => { e.currentTarget.style.background = '#f0fdfa' }}
     onMouseLeave={e => { e.currentTarget.style.background = 'transparent' }}
     >
       <td style={{ padding: '12px 12px', fontWeight: 500, fontSize: 13 }}>{d.name}</td>
-      <td style={{ padding: '12px 12px', fontSize: 13, color: '#666' }}>
+      <td style={{ padding: '12px 12px', fontSize: 13, color: '#64748b' }}>
         <span style={{
           display: 'inline-block', padding: '2px 8px', borderRadius: 4, fontSize: 12,
-          background: '#f5f5f5', color: '#666',
+          background: '#f1f5f9', color: '#64748b',
         }}>
           {d.format}
         </span>
       </td>
       <td style={{ padding: '12px 12px', fontSize: 13 }}>{d.record_count}</td>
-      <td style={{ padding: '12px 12px', fontSize: 13, color: '#888' }}>{d.created_at}</td>
+      <td style={{ padding: '12px 12px', fontSize: 13, color: '#94a3b8' }}>{d.created_at}</td>
       <td style={{ padding: '12px 12px' }}>
         <button onClick={() => onPreview(d.id)} style={{
-          marginRight: 8, padding: '4px 12px', color: '#2196f3',
-          border: '1px solid #2196f3', borderRadius: 6, background: 'transparent',
+          marginRight: 8, padding: '4px 12px', color: '#0ea5e9',
+          border: '1px solid #0ea5e9', borderRadius: 6, background: 'transparent',
           cursor: 'pointer', fontSize: 12, fontWeight: 500, transition: 'all 0.15s ease',
         }}
-        onMouseEnter={e => { e.currentTarget.style.background = '#2196f3'; e.currentTarget.style.color = '#fff' }}
-        onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = '#2196f3' }}
+        onMouseEnter={e => { e.currentTarget.style.background = '#0ea5e9'; e.currentTarget.style.color = '#fff' }}
+        onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = '#0ea5e9' }}
         >预览</button>
         <button onClick={() => onDelete(d.id)} style={{
-          padding: '4px 12px', color: '#e94560', border: '1px solid #e94560',
+          padding: '4px 12px', color: '#f43f5e', border: '1px solid #f43f5e',
           borderRadius: 6, background: 'transparent', cursor: 'pointer',
           fontSize: 12, fontWeight: 500, transition: 'all 0.15s ease',
         }}
-        onMouseEnter={e => { e.currentTarget.style.background = '#e94560'; e.currentTarget.style.color = '#fff' }}
-        onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = '#e94560' }}
+        onMouseEnter={e => { e.currentTarget.style.background = '#f43f5e'; e.currentTarget.style.color = '#fff' }}
+        onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = '#f43f5e' }}
         >删除</button>
       </td>
     </tr>
@@ -115,25 +116,31 @@ export function Datasets() {
   return (
     <div>
       <h1 style={{ margin: 0, fontSize: 22, fontWeight: 700 }}>数据集管理</h1>
-      <p style={{ color: '#888', fontSize: 13, margin: '4px 0 16px' }}>上传和管理训练数据集</p>
+      <p style={{ color: '#64748b', fontSize: 13, margin: '4px 0 16px' }}>上传和管理训练数据集</p>
 
       {/* Upload area */}
       <div
         onClick={() => inputRef.current?.click()}
         style={{
-          marginTop: 16, border: '2px dashed #d0d0d0', borderRadius: 12,
-          padding: 40, textAlign: 'center', color: '#888', cursor: 'pointer',
+          marginTop: 16, border: '2px dashed #cbd5e1', borderRadius: 12,
+          padding: 40, textAlign: 'center', color: '#94a3b8', cursor: 'pointer',
           opacity: uploading ? 0.6 : 1, background: '#fff',
           transition: 'all 0.2s ease',
         }}
-        onMouseEnter={e => { e.currentTarget.style.borderColor = '#e94560'; e.currentTarget.style.background = '#fff5f7' }}
-        onMouseLeave={e => { e.currentTarget.style.borderColor = '#d0d0d0'; e.currentTarget.style.background = '#fff' }}
+        onMouseEnter={e => { e.currentTarget.style.borderColor = '#14b8a6'; e.currentTarget.style.background = '#f0fdfa' }}
+        onMouseLeave={e => { e.currentTarget.style.borderColor = '#cbd5e1'; e.currentTarget.style.background = '#fff' }}
       >
-        <div style={{ fontSize: 32, marginBottom: 8 }}>{uploading ? '⏳' : '📤'}</div>
+        <div style={{ marginBottom: 8 }}>
+          {uploading ? (
+            <Loader2 size={32} color="#14b8a6" strokeWidth={1.5} style={{ animation: 'lucide-spin 1s linear infinite' }} />
+          ) : (
+            <Upload size={32} color="#14b8a6" strokeWidth={1.5} />
+          )}
+        </div>
         <div style={{ fontSize: 14, fontWeight: 500 }}>
           {uploading ? '上传中...' : '拖拽文件到此处或点击上传'}
         </div>
-        <div style={{ fontSize: 12, color: '#aaa', marginTop: 4 }}>
+        <div style={{ fontSize: 12, color: '#cbd5e1', marginTop: 4 }}>
           支持 JSONL / CSV / Parquet / JSON 格式
         </div>
         <input
@@ -159,13 +166,13 @@ export function Datasets() {
             onChange={e => setDlDatasetId(e.target.value)}
             style={{
               padding: '10px 14px', flex: 1, maxWidth: 400, borderRadius: 8,
-              border: '1px solid #d0d0d0', fontSize: 14, outline: 'none',
+              border: '1px solid #cbd5e1', fontSize: 14, outline: 'none',
               transition: 'border-color 0.2s',
             }}
-            onFocus={e => { e.currentTarget.style.borderColor = '#e94560' }}
-            onBlur={e => { e.currentTarget.style.borderColor = '#d0d0d0' }}
+            onFocus={e => { e.currentTarget.style.borderColor = '#14b8a6' }}
+            onBlur={e => { e.currentTarget.style.borderColor = '#cbd5e1' }}
           />
-          <label style={{ fontSize: 13, color: '#666', whiteSpace: 'nowrap', display: 'flex', alignItems: 'center', gap: 4, cursor: 'pointer' }}>
+          <label style={{ fontSize: 13, color: '#64748b', whiteSpace: 'nowrap', display: 'flex', alignItems: 'center', gap: 4, cursor: 'pointer' }}>
             <input type="checkbox" checked={dlUseModelscope} onChange={e => setDlUseModelscope(e.target.checked)} />
             {' '}ModelScope
           </label>
@@ -174,7 +181,7 @@ export function Datasets() {
             disabled={downloading}
             style={{
               padding: '10px 20px', borderRadius: 8, border: 'none',
-              background: '#e94560', color: '#fff', cursor: 'pointer',
+              background: '#14b8a6', color: '#fff', cursor: 'pointer',
               opacity: downloading ? 0.6 : 1, fontSize: 14, fontWeight: 600,
             }}
           >
@@ -183,9 +190,9 @@ export function Datasets() {
         </div>
         {dlStatus && <p style={{
           marginTop: 10, padding: '8px 12px', borderRadius: 6, fontSize: 13,
-          background: dlStatus.includes('失败') ? '#fff2f0' : '#f5f5f5',
-          color: dlStatus.includes('失败') ? '#cf1322' : '#666',
-          border: `1px solid ${dlStatus.includes('失败') ? '#ffccc7' : 'transparent'}`,
+          background: dlStatus.includes('失败') ? '#fff1f2' : '#f1f5f9',
+          color: dlStatus.includes('失败') ? '#e11d48' : '#64748b',
+          border: `1px solid ${dlStatus.includes('失败') ? '#fecdd3' : 'transparent'}`,
         }}>{dlStatus}</p>}
       </div>
 
@@ -194,24 +201,24 @@ export function Datasets() {
         <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
           <h2 style={{ margin: 0, fontSize: 15, fontWeight: 600 }}>已上传数据集</h2>
           <button onClick={fetchDatasets} style={{
-            padding: '6px 14px', borderRadius: 6, border: '1px solid #d0d0d0',
+            padding: '6px 14px', borderRadius: 6, border: '1px solid #cbd5e1',
             background: '#fff', cursor: 'pointer', fontSize: 13, fontWeight: 500,
           }}
-          onMouseEnter={e => { e.currentTarget.style.background = '#f5f5f5' }}
+          onMouseEnter={e => { e.currentTarget.style.background = '#f0fdfa' }}
           onMouseLeave={e => { e.currentTarget.style.background = '#fff' }}
           >
             刷新
           </button>
         </div>
 
-        {loading && <p style={{ color: '#999', fontSize: 13 }}>加载中...</p>}
+        {loading && <p style={{ color: '#94a3b8', fontSize: 13 }}>加载中...</p>}
 
         {!loading && datasets.length === 0 && (
           <div style={{
-            padding: 40, textAlign: 'center', color: '#999', fontSize: 14,
+            padding: 40, textAlign: 'center', color: '#94a3b8', fontSize: 14,
             background: '#fff', borderRadius: 10, boxShadow: '0 1px 3px rgba(0,0,0,0.06)',
           }}>
-            <div style={{ fontSize: 32, marginBottom: 8 }}>📁</div>
+            <div style={{ marginBottom: 8 }}><Database size={32} color="#94a3b8" strokeWidth={1.5} /></div>
             暂无数据集,请上传文件或从平台下载
           </div>
         )}
@@ -223,12 +230,12 @@ export function Datasets() {
           }}>
             <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 14 }}>
               <thead>
-                <tr style={{ background: '#fafbfc', borderBottom: '2px solid #f0f0f0', textAlign: 'left' }}>
-                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#666', fontWeight: 600 }}>名称</th>
-                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#666', fontWeight: 600 }}>格式</th>
-                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#666', fontWeight: 600 }}>记录数</th>
-                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#666', fontWeight: 600 }}>上传时间</th>
-                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#666', fontWeight: 600 }}>操作</th>
+                <tr style={{ background: '#f0fdfa', borderBottom: '2px solid #f1f5f9', textAlign: 'left' }}>
+                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#64748b', fontWeight: 600 }}>名称</th>
+                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#64748b', fontWeight: 600 }}>格式</th>
+                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#64748b', fontWeight: 600 }}>记录数</th>
+                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#64748b', fontWeight: 600 }}>上传时间</th>
+                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#64748b', fontWeight: 600 }}>操作</th>
                 </tr>
               </thead>
               <tbody>
@@ -251,16 +258,16 @@ export function Datasets() {
           <div style={{ overflowX: 'auto' }}>
             <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
               <thead>
-                <tr style={{ background: '#fafbfc', borderBottom: '2px solid #f0f0f0', textAlign: 'left' }}>
+                <tr style={{ background: '#f0fdfa', borderBottom: '2px solid #f1f5f9', textAlign: 'left' }}>
                   {previewData.columns.map(col => (
-                    <th key={col} style={{ padding: '8px 12px', fontSize: 12, color: '#666', fontWeight: 600, whiteSpace: 'nowrap' }}>{col}</th>
+                    <th key={col} style={{ padding: '8px 12px', fontSize: 12, color: '#64748b', fontWeight: 600, whiteSpace: 'nowrap' }}>{col}</th>
                   ))}
                 </tr>
               </thead>
               <tbody>
                 {previewData.rows.slice(0, 10).map((row, i) => (
-                  <tr key={i} style={{ borderBottom: '1px solid #f0f0f0', transition: 'background 0.15s ease' }}
-                    onMouseEnter={e => { e.currentTarget.style.background = '#fafbfc' }}
+                  <tr key={i} style={{ borderBottom: '1px solid #f1f5f9', transition: 'background 0.15s ease' }}
+                    onMouseEnter={e => { e.currentTarget.style.background = '#f0fdfa' }}
                     onMouseLeave={e => { e.currentTarget.style.background = 'transparent' }}
                   >
                     {previewData.columns.map(col => {

+ 12 - 12
frontend/src/pages/Deployment.tsx

@@ -59,8 +59,8 @@ export function Deployment() {
                 fontSize: 14, outline: 'none', background: '#fff',
                 transition: 'border-color 0.2s',
               }}
-              onFocus={e => { e.currentTarget.style.borderColor = '#e94560' }}
-              onBlur={e => { e.currentTarget.style.borderColor = '#d0d0d0' }}
+              onFocus={e => { e.currentTarget.style.borderColor = '#14b8a6' }}
+              onBlur={e => { e.currentTarget.style.borderColor = '#cbd5e1' }}
             >
               <option value="" disabled>选择已完成的训练任务</option>
               {jobs.map(j => (
@@ -79,8 +79,8 @@ export function Deployment() {
                 fontSize: 14, outline: 'none', background: '#fff',
                 transition: 'border-color 0.2s',
               }}
-              onFocus={e => { e.currentTarget.style.borderColor = '#e94560' }}
-              onBlur={e => { e.currentTarget.style.borderColor = '#d0d0d0' }}
+              onFocus={e => { e.currentTarget.style.borderColor = '#14b8a6' }}
+              onBlur={e => { e.currentTarget.style.borderColor = '#cbd5e1' }}
             >
               {EXPORT_FORMATS.map(f => (
                 <option key={f.value} value={f.value}>{f.label}</option>
@@ -99,7 +99,7 @@ export function Deployment() {
         </div>
 
         {error && (
-          <div style={{ marginTop: 16, padding: 12, background: '#fff2f0', borderRadius: 8, fontSize: 13, color: '#cf1322', border: '1px solid #ffccc7' }}>
+          <div style={{ marginTop: 16, padding: 12, background: '#fff1f2', borderRadius: 8, fontSize: 13, color: '#e11d48', border: '1px solid #fecdd3' }}>
             {error}
           </div>
         )}
@@ -109,7 +109,7 @@ export function Deployment() {
           disabled={running || !jobId}
           style={{
             marginTop: 20, padding: '10px 32px', borderRadius: 8, border: 'none',
-            background: '#e94560', color: '#fff', cursor: 'pointer',
+            background: '#14b8a6', color: '#fff', cursor: 'pointer',
             opacity: (running || !jobId) ? 0.5 : 1, fontSize: 14, fontWeight: 600,
             transition: 'all 0.2s ease',
           }}
@@ -129,9 +129,9 @@ export function Deployment() {
               <div style={{ fontSize: 12, color: '#888', marginBottom: 4 }}>任务 ID</div>
               <div style={{ fontSize: 14, fontFamily: 'monospace' }}>{result.job_id}</div>
             </div>
-            <div style={{ padding: '14px 16px', background: result.error ? '#fff2f0' : '#f0fff4', borderRadius: 8, border: `1px solid ${result.error ? '#ffccc7' : '#d1f0d8'}` }}>
-              <div style={{ fontSize: 12, color: '#888', marginBottom: 4 }}>状态</div>
-              <div style={{ color: result.error ? '#cf1322' : '#22863a', fontWeight: 600, fontSize: 14 }}>{result.status}</div>
+            <div style={{ padding: '14px 16px', background: result.error ? '#fff1f2' : '#ecfdf5', borderRadius: 8, border: `1px solid ${result.error ? '#fecdd3' : '#d1fae5'}` }}>
+              <div style={{ fontSize: 12, color: '#94a3b8', marginBottom: 4 }}>状态</div>
+              <div style={{ color: result.error ? '#e11d48' : '#059669', fontWeight: 600, fontSize: 14 }}>{result.status}</div>
             </div>
             {result.output_path && (
               <div style={{ padding: '14px 16px', background: '#fafbfc', borderRadius: 8, border: '1px solid #f0f0f0' }}>
@@ -140,9 +140,9 @@ export function Deployment() {
               </div>
             )}
             {result.error && (
-              <div style={{ padding: '14px 16px', background: '#fff2f0', borderRadius: 8, border: '1px solid #ffccc7' }}>
-                <div style={{ fontSize: 12, color: '#888', marginBottom: 4 }}>错误</div>
-                <div style={{ color: '#cf1322', fontSize: 13 }}>{result.error}</div>
+              <div style={{ padding: '14px 16px', background: '#fff1f2', borderRadius: 8, border: '1px solid #fecdd3' }}>
+                <div style={{ fontSize: 12, color: '#94a3b8', marginBottom: 4 }}>错误</div>
+                <div style={{ color: '#e11d48', fontSize: 13 }}>{result.error}</div>
               </div>
             )}
           </div>

+ 10 - 10
frontend/src/pages/Evaluation.tsx

@@ -58,8 +58,8 @@ export function Evaluation() {
                 fontSize: 14, outline: 'none', background: '#fff',
                 transition: 'border-color 0.2s',
               }}
-              onFocus={e => { e.currentTarget.style.borderColor = '#e94560' }}
-              onBlur={e => { e.currentTarget.style.borderColor = '#d0d0d0' }}
+              onFocus={e => { e.currentTarget.style.borderColor = '#14b8a6' }}
+              onBlur={e => { e.currentTarget.style.borderColor = '#cbd5e1' }}
             >
               <option value="" disabled>选择已完成的训练任务</option>
               {jobs.map(j => (
@@ -78,8 +78,8 @@ export function Evaluation() {
                 fontSize: 14, outline: 'none', background: '#fff',
                 transition: 'border-color 0.2s',
               }}
-              onFocus={e => { e.currentTarget.style.borderColor = '#e94560' }}
-              onBlur={e => { e.currentTarget.style.borderColor = '#d0d0d0' }}
+              onFocus={e => { e.currentTarget.style.borderColor = '#14b8a6' }}
+              onBlur={e => { e.currentTarget.style.borderColor = '#cbd5e1' }}
             >
               {METRICS_PRESETS.map(m => (
                 <option key={m.value} value={m.value}>{m.label}</option>
@@ -89,7 +89,7 @@ export function Evaluation() {
         </div>
 
         {error && (
-          <div style={{ marginTop: 16, padding: 12, background: '#fff2f0', borderRadius: 8, fontSize: 13, color: '#cf1322', border: '1px solid #ffccc7' }}>
+          <div style={{ marginTop: 16, padding: 12, background: '#fff1f2', borderRadius: 8, fontSize: 13, color: '#e11d48', border: '1px solid #fecdd3' }}>
             {error}
           </div>
         )}
@@ -99,7 +99,7 @@ export function Evaluation() {
           disabled={running || !jobId}
           style={{
             marginTop: 20, padding: '10px 32px', borderRadius: 8, border: 'none',
-            background: '#e94560', color: '#fff', cursor: 'pointer',
+            background: '#14b8a6', color: '#fff', cursor: 'pointer',
             opacity: (running || !jobId) ? 0.5 : 1, fontSize: 14, fontWeight: 600,
             transition: 'all 0.2s ease',
           }}
@@ -122,11 +122,11 @@ export function Evaluation() {
               <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 12 }}>
                 {Object.entries(result.metrics).map(([k, v]) => (
                   <div key={k} style={{
-                    padding: '14px 16px', background: '#fafbfc', borderRadius: 8,
-                    border: '1px solid #f0f0f0',
+                    padding: '14px 16px', background: '#f0fdfa', borderRadius: 8,
+                    border: '1px solid #ccfbf1',
                   }}>
-                    <div style={{ fontSize: 12, color: '#888', marginBottom: 4 }}>{k}</div>
-                    <div style={{ fontSize: 20, fontWeight: 700, fontFamily: 'monospace', color: '#1a1a2e' }}>{String(v)}</div>
+                    <div style={{ fontSize: 12, color: '#94a3b8', marginBottom: 4 }}>{k}</div>
+                    <div style={{ fontSize: 20, fontWeight: 700, fontFamily: 'monospace', color: '#134e4a' }}>{String(v)}</div>
                   </div>
                 ))}
               </div>

+ 19 - 18
frontend/src/pages/Inference.tsx

@@ -1,5 +1,6 @@
 import { useState, useEffect } from 'react'
 import api, { AdapterInfo } from '../api/client'
+import { MessageSquare } from 'lucide-react'
 
 // --- 预设值 ---
 const MAX_TOKEN_PRESETS = [
@@ -125,10 +126,10 @@ export function Inference() {
         <h2 style={{ margin: '0 0 12px', fontSize: 15, fontWeight: 600 }}>选择 Adapter</h2>
         {adapters.length === 0 ? (
           <div style={{
-            padding: 24, textAlign: 'center', color: '#999', fontSize: 13,
-            background: '#fafbfc', borderRadius: 8, border: '1px solid #f0f0f0',
+            padding: 24, textAlign: 'center', color: '#94a3b8', fontSize: 13,
+            background: '#f0fdfa', borderRadius: 8, border: '1px solid #ccfbf1',
           }}>
-            <div style={{ fontSize: 24, marginBottom: 6 }}>💬</div>
+            <div style={{ marginBottom: 6 }}><MessageSquare size={24} color="#94a3b8" strokeWidth={1.5} /></div>
             暂无可用的 adapter,请先完成训练任务
           </div>
         ) : (
@@ -140,8 +141,8 @@ export function Inference() {
               width: '100%', maxWidth: 500, fontSize: 14, outline: 'none',
               transition: 'border-color 0.2s',
             }}
-            onFocus={e => { e.currentTarget.style.borderColor = '#e94560' }}
-            onBlur={e => { e.currentTarget.style.borderColor = '#d0d0d0' }}
+            onFocus={e => { e.currentTarget.style.borderColor = '#14b8a6' }}
+            onBlur={e => { e.currentTarget.style.borderColor = '#cbd5e1' }}
           >
             {adapters.map(a => (
               <option key={a.id} value={a.id}>{a.id} — {a.base_model} ({a.peft_type})</option>
@@ -209,7 +210,7 @@ export function Inference() {
           disabled={generating || !adapterId || !prompt.trim()}
           style={{
             marginTop: 20, padding: '12px 36px', borderRadius: 8, border: 'none',
-            background: '#2196f3', color: '#fff', cursor: 'pointer',
+            background: '#0ea5e9', color: '#fff', cursor: 'pointer',
             opacity: (generating || !adapterId || !prompt.trim()) ? 0.5 : 1,
             fontSize: 14, fontWeight: 600, transition: 'all 0.2s ease',
           }}
@@ -221,8 +222,8 @@ export function Inference() {
       {/* Error */}
       {error && (
         <div style={{
-          marginTop: 16, padding: 14, background: '#fff2f0', borderRadius: 8,
-          color: '#cf1322', fontSize: 13, border: '1px solid #ffccc7',
+          marginTop: 16, padding: 14, background: '#fff1f2', borderRadius: 8,
+          color: '#e11d48', fontSize: 13, border: '1px solid #fecdd3',
         }}>
           {error}
         </div>
@@ -237,7 +238,7 @@ export function Inference() {
           <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 }}>
             <h2 style={{ margin: 0, fontSize: 15, fontWeight: 600 }}>生成结果</h2>
             <span style={{
-              fontSize: 12, color: '#666', background: '#f0f7ff', padding: '4px 12px',
+              fontSize: 12, color: '#64748b', background: '#f0f9ff', padding: '4px 12px',
               borderRadius: 12, fontWeight: 500,
             }}>
               {result.tokens_generated} tokens
@@ -247,7 +248,7 @@ export function Inference() {
           {/* Prompt */}
           <div style={{ marginBottom: 20 }}>
             <div style={{ fontSize: 12, color: '#888', marginBottom: 8, fontWeight: 600 }}>Prompt</div>
-            <div style={{ padding: 14, background: '#f0f7ff', borderRadius: 8, fontSize: 14, lineHeight: 1.6, border: '1px solid #d6e8fa' }}>
+            <div style={{ padding: 14, background: '#f0f9ff', borderRadius: 8, fontSize: 14, lineHeight: 1.6, border: '1px solid #bae6fd' }}>
               {prompt}
             </div>
           </div>
@@ -261,9 +262,9 @@ export function Inference() {
                   onClick={() => setViewMode('full')}
                   style={{
                     padding: '4px 12px', borderRadius: 6, fontSize: 12, cursor: 'pointer',
-                    border: `1px solid ${viewMode === 'full' ? '#e94560' : '#d0d0d0'}`,
-                    background: viewMode === 'full' ? '#e94560' : '#fff',
-                    color: viewMode === 'full' ? '#fff' : '#666',
+                    border: `1px solid ${viewMode === 'full' ? '#14b8a6' : '#cbd5e1'}`,
+                    background: viewMode === 'full' ? '#14b8a6' : '#fff',
+                    color: viewMode === 'full' ? '#fff' : '#64748b',
                     fontWeight: 500, transition: 'all 0.15s ease',
                   }}
                 >
@@ -273,9 +274,9 @@ export function Inference() {
                   onClick={() => setViewMode('new')}
                   style={{
                     padding: '4px 12px', borderRadius: 6, fontSize: 12, cursor: 'pointer',
-                    border: `1px solid ${viewMode === 'new' ? '#e94560' : '#d0d0d0'}`,
-                    background: viewMode === 'new' ? '#e94560' : '#fff',
-                    color: viewMode === 'new' ? '#fff' : '#666',
+                    border: `1px solid ${viewMode === 'new' ? '#14b8a6' : '#cbd5e1'}`,
+                    background: viewMode === 'new' ? '#14b8a6' : '#fff',
+                    color: viewMode === 'new' ? '#fff' : '#64748b',
                     fontWeight: 500, transition: 'all 0.15s ease',
                   }}
                 >
@@ -284,9 +285,9 @@ export function Inference() {
               </div>
             </div>
             <pre style={{
-              whiteSpace: 'pre-wrap', wordBreak: 'break-word', background: '#fafbfc',
+              whiteSpace: 'pre-wrap', wordBreak: 'break-word', background: '#f0fdfa',
               padding: 16, borderRadius: 8, fontSize: 14, lineHeight: 1.6,
-              maxHeight: 400, overflow: 'auto', margin: 0, border: '1px solid #f0f0f0',
+              maxHeight: 400, overflow: 'auto', margin: 0, border: '1px solid #ccfbf1',
             }}>
               {viewMode === 'full' ? result.generated_text : result.generated_text.slice(prompt.length).trim()}
             </pre>

+ 60 - 55
frontend/src/pages/Models.tsx

@@ -1,5 +1,6 @@
 import { useState, memo } from 'react'
 import api, { ModelInfo } from '../api/client'
+import { Cpu, CheckCircle, XCircle } from 'lucide-react'
 
 const ModelRow = memo(function ModelRow({ m, onTest, onDelete }: {
   m: ModelInfo
@@ -8,44 +9,44 @@ const ModelRow = memo(function ModelRow({ m, onTest, onDelete }: {
 }) {
   return (
     <tr style={{
-      borderBottom: '1px solid #f0f0f0',
+      borderBottom: '1px solid #f1f5f9',
       transition: 'background 0.15s ease',
     }}
-    onMouseEnter={e => { e.currentTarget.style.background = '#fafbfc' }}
+    onMouseEnter={e => { e.currentTarget.style.background = '#f0fdfa' }}
     onMouseLeave={e => { e.currentTarget.style.background = 'transparent' }}
     >
-      <td style={{ padding: '12px 12px', fontFamily: 'monospace', fontSize: 12, color: '#666' }}>{m.id}</td>
+      <td style={{ padding: '12px 12px', fontFamily: 'monospace', fontSize: 12, color: '#64748b' }}>{m.id}</td>
       <td style={{ padding: '12px 12px', fontWeight: 500, fontSize: 13 }}>{m.name}</td>
-      <td style={{ padding: '12px 12px', fontSize: 13, color: '#666' }}>{m.model_type}</td>
+      <td style={{ padding: '12px 12px', fontSize: 13, color: '#64748b' }}>{m.model_type}</td>
       <td style={{ padding: '12px 12px' }}>
         <span style={{
           display: 'inline-block', padding: '3px 10px', borderRadius: 12, fontSize: 12, fontWeight: 500,
-          background: m.is_downloaded ? '#f0fff4' : '#fff5f7',
-          color: m.is_downloaded ? '#22863a' : '#e94560',
-          border: `1px solid ${m.is_downloaded ? '#d1f0d8' : '#ffdce0'}`,
+          background: m.is_downloaded ? '#ecfdf5' : '#fff1f2',
+          color: m.is_downloaded ? '#059669' : '#f43f5e',
+          border: `1px solid ${m.is_downloaded ? '#d1fae5' : '#fecdd3'}`,
         }}>
           {m.is_downloaded ? '已缓存' : '未下载'}
         </span>
       </td>
-      <td style={{ padding: '12px 12px', fontSize: 13, color: '#666' }}>{m.supported_peft_methods.join(', ') || '-'}</td>
+      <td style={{ padding: '12px 12px', fontSize: 13, color: '#64748b' }}>{m.supported_peft_methods.join(', ') || '-'}</td>
       <td style={{ padding: '12px 12px' }}>
         {m.is_downloaded && (
           <button onClick={() => onTest(m.id)} style={{
-            marginRight: 8, padding: '4px 12px', color: '#2196f3',
-            border: '1px solid #2196f3', borderRadius: 6, background: 'transparent',
+            marginRight: 8, padding: '4px 12px', color: '#0ea5e9',
+            border: '1px solid #0ea5e9', borderRadius: 6, background: 'transparent',
             cursor: 'pointer', fontSize: 12, fontWeight: 500, transition: 'all 0.15s ease',
           }}
-          onMouseEnter={e => { e.currentTarget.style.background = '#2196f3'; e.currentTarget.style.color = '#fff' }}
-          onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = '#2196f3' }}
+          onMouseEnter={e => { e.currentTarget.style.background = '#0ea5e9'; e.currentTarget.style.color = '#fff' }}
+          onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = '#0ea5e9' }}
           >测试</button>
         )}
         <button onClick={() => onDelete(m.id, m.name)} style={{
-          padding: '4px 12px', color: '#e94560', border: '1px solid #e94560',
+          padding: '4px 12px', color: '#f43f5e', border: '1px solid #f43f5e',
           borderRadius: 6, background: 'transparent', cursor: 'pointer',
           fontSize: 12, fontWeight: 500, transition: 'all 0.15s ease',
         }}
-        onMouseEnter={e => { e.currentTarget.style.background = '#e94560'; e.currentTarget.style.color = '#fff' }}
-        onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = '#e94560' }}
+        onMouseEnter={e => { e.currentTarget.style.background = '#f43f5e'; e.currentTarget.style.color = '#fff' }}
+        onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = '#f43f5e' }}
         >删除</button>
       </td>
     </tr>
@@ -58,7 +59,8 @@ export function Models() {
   const [downloading, setDownloading] = useState(false)
   const [models, setModels] = useState<ModelInfo[]>([])
   const [loading, setLoading] = useState(false)
-  const [statusMsg, setStatusMsg] = useState('')
+  const [statusType, setStatusType] = useState<'success' | 'error' | ''>('')
+  const [statusContent, setStatusContent] = useState('')
 
   // Test state
   const [testModelId, setTestModelId] = useState('')
@@ -78,10 +80,11 @@ export function Models() {
   const handleDownload = () => {
     if (!modelId.trim()) return
     setDownloading(true)
-    setStatusMsg('正在下载...')
+    setStatusType('')
+    setStatusContent('正在下载...')
     api.models.download(modelId, useModelscope)
-      .then(res => setStatusMsg(`✅ ${res.model_id}: ${res.status}`))
-      .catch(err => setStatusMsg(`❌ 下载失败: ${err.message}`))
+      .then(res => { setStatusType('success'); setStatusContent(`${res.model_id}: ${res.status}`) })
+      .catch(err => { setStatusType('error'); setStatusContent(`下载失败: ${err.message}`) })
       .finally(() => setDownloading(false))
   }
 
@@ -92,7 +95,8 @@ export function Models() {
       fetchModels()
     } catch (err) {
       const msg = err instanceof Error ? err.message : '删除失败'
-      setStatusMsg(`❌ ${msg}`)
+      setStatusType('error')
+      setStatusContent(msg)
     }
   }
 
@@ -101,7 +105,6 @@ export function Models() {
     setTestPrompt('')
     setTestResult('')
     setTestError('')
-    // Show test panel
     setTestPrompt('你好,请简单介绍一下自己。')
   }
 
@@ -130,7 +133,7 @@ export function Models() {
   return (
     <div>
       <h1 style={{ margin: 0, fontSize: 22, fontWeight: 700 }}>模型注册</h1>
-      <p style={{ color: '#888', fontSize: 13, margin: '4px 0 16px' }}>下载和管理预训练模型</p>
+      <p style={{ color: '#64748b', fontSize: 13, margin: '4px 0 16px' }}>下载和管理预训练模型</p>
 
       {/* Download form */}
       <div style={{
@@ -146,13 +149,13 @@ export function Models() {
             onChange={e => setModelId(e.target.value)}
             style={{
               padding: '10px 14px', flex: 1, maxWidth: 400, borderRadius: 8,
-              border: '1px solid #d0d0d0', fontSize: 14, outline: 'none',
+              border: '1px solid #cbd5e1', fontSize: 14, outline: 'none',
               transition: 'border-color 0.2s',
             }}
-            onFocus={e => { e.currentTarget.style.borderColor = '#e94560' }}
-            onBlur={e => { e.currentTarget.style.borderColor = '#d0d0d0' }}
+            onFocus={e => { e.currentTarget.style.borderColor = '#14b8a6' }}
+            onBlur={e => { e.currentTarget.style.borderColor = '#cbd5e1' }}
           />
-          <label style={{ fontSize: 13, color: '#666', whiteSpace: 'nowrap', display: 'flex', alignItems: 'center', gap: 4, cursor: 'pointer' }}>
+          <label style={{ fontSize: 13, color: '#64748b', whiteSpace: 'nowrap', display: 'flex', alignItems: 'center', gap: 4, cursor: 'pointer' }}>
             <input type="checkbox" checked={useModelscope} onChange={e => setUseModelscope(e.target.checked)} />
             {' '}ModelScope
           </label>
@@ -161,7 +164,7 @@ export function Models() {
             disabled={downloading}
             style={{
               padding: '10px 20px', borderRadius: 8, border: 'none',
-              background: '#e94560', color: '#fff', cursor: 'pointer',
+              background: '#14b8a6', color: '#fff', cursor: 'pointer',
               opacity: downloading ? 0.6 : 1, fontSize: 14, fontWeight: 600,
               transition: 'all 0.2s ease',
             }}
@@ -169,14 +172,16 @@ export function Models() {
             {downloading ? '下载中...' : '下载模型'}
           </button>
         </div>
-        {statusMsg && (
+        {statusType && (
           <p style={{
             marginTop: 10, padding: '8px 12px', borderRadius: 6, fontSize: 13, margin: '10px 0 0',
-            background: statusMsg.includes('❌') ? '#fff2f0' : '#f0fff4',
-            color: statusMsg.includes('❌') ? '#cf1322' : '#22863a',
-            border: `1px solid ${statusMsg.includes('❌') ? '#ffccc7' : '#d1f0d8'}`,
+            display: 'flex', alignItems: 'center', gap: 6,
+            background: statusType === 'error' ? '#fff1f2' : '#ecfdf5',
+            color: statusType === 'error' ? '#e11d48' : '#059669',
+            border: `1px solid ${statusType === 'error' ? '#fecdd3' : '#d1fae5'}`,
           }}>
-            {statusMsg}
+            {statusType === 'error' ? <XCircle size={14} /> : <CheckCircle size={14} />}
+            {statusContent}
           </p>
         )}
       </div>
@@ -186,25 +191,25 @@ export function Models() {
         <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
           <h2 style={{ margin: 0, fontSize: 15, fontWeight: 600 }}>已缓存模型</h2>
           <button onClick={fetchModels} style={{
-            padding: '6px 14px', borderRadius: 6, border: '1px solid #d0d0d0',
+            padding: '6px 14px', borderRadius: 6, border: '1px solid #cbd5e1',
             background: '#fff', cursor: 'pointer', fontSize: 13, fontWeight: 500,
             transition: 'all 0.15s ease',
           }}
-          onMouseEnter={e => { e.currentTarget.style.background = '#f5f5f5' }}
+          onMouseEnter={e => { e.currentTarget.style.background = '#f0fdfa' }}
           onMouseLeave={e => { e.currentTarget.style.background = '#fff' }}
           >
             刷新
           </button>
         </div>
 
-        {loading && <p style={{ color: '#999', fontSize: 13 }}>加载中...</p>}
+        {loading && <p style={{ color: '#94a3b8', fontSize: 13 }}>加载中...</p>}
 
         {!loading && models.length === 0 && (
           <div style={{
-            padding: 40, textAlign: 'center', color: '#999', fontSize: 14,
+            padding: 40, textAlign: 'center', color: '#94a3b8', fontSize: 14,
             background: '#fff', borderRadius: 10, boxShadow: '0 1px 3px rgba(0,0,0,0.06)',
           }}>
-            <div style={{ fontSize: 32, marginBottom: 8 }}>🧠</div>
+            <div style={{ marginBottom: 8 }}><Cpu size={32} color="#94a3b8" strokeWidth={1.5} /></div>
             暂无已缓存模型,请在上方下载模型
           </div>
         )}
@@ -216,13 +221,13 @@ export function Models() {
           }}>
             <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 14 }}>
               <thead>
-                <tr style={{ background: '#fafbfc', borderBottom: '2px solid #f0f0f0', textAlign: 'left' }}>
-                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#666', fontWeight: 600 }}>ID</th>
-                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#666', fontWeight: 600 }}>名称</th>
-                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#666', fontWeight: 600 }}>类型</th>
-                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#666', fontWeight: 600 }}>状态</th>
-                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#666', fontWeight: 600 }}>PEFT 支持</th>
-                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#666', fontWeight: 600 }}>操作</th>
+                <tr style={{ background: '#f0fdfa', borderBottom: '2px solid #f1f5f9', textAlign: 'left' }}>
+                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#64748b', fontWeight: 600 }}>ID</th>
+                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#64748b', fontWeight: 600 }}>名称</th>
+                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#64748b', fontWeight: 600 }}>类型</th>
+                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#64748b', fontWeight: 600 }}>状态</th>
+                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#64748b', fontWeight: 600 }}>PEFT 支持</th>
+                  <th style={{ padding: '10px 12px', fontSize: 12, color: '#64748b', fontWeight: 600 }}>操作</th>
                 </tr>
               </thead>
               <tbody>
@@ -242,9 +247,9 @@ export function Models() {
           boxShadow: '0 2px 8px rgba(0,0,0,0.08)', border: '1px solid rgba(0,0,0,0.04)',
         }}>
           <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 }}>
-            <h2 style={{ margin: 0, fontSize: 16, fontWeight: 600 }}>模型测试 — <code style={{ background: '#f5f5f5', padding: '2px 8px', borderRadius: 4, fontSize: 13 }}>{testModelId}</code></h2>
+            <h2 style={{ margin: 0, fontSize: 16, fontWeight: 600 }}>模型测试 — <code style={{ background: '#f1f5f9', padding: '2px 8px', borderRadius: 4, fontSize: 13 }}>{testModelId}</code></h2>
             <button onClick={() => { setTestModelId(''); setTestResult(''); setTestError(''); setTestPrompt('') }} style={{
-              padding: '6px 14px', borderRadius: 6, border: '1px solid #d0d0d0',
+              padding: '6px 14px', borderRadius: 6, border: '1px solid #cbd5e1',
               background: '#fff', cursor: 'pointer', fontSize: 13,
             }}>关闭</button>
           </div>
@@ -258,18 +263,18 @@ export function Models() {
               placeholder="输入提示词,按 Enter 发送..."
               style={{
                 flex: 1, padding: '10px 14px', borderRadius: 8,
-                border: '1px solid #d0d0d0', fontSize: 14, outline: 'none',
+                border: '1px solid #cbd5e1', fontSize: 14, outline: 'none',
                 transition: 'border-color 0.2s',
               }}
-              onFocus={e => { e.currentTarget.style.borderColor = '#2196f3' }}
-              onBlur={e => { e.currentTarget.style.borderColor = '#d0d0d0' }}
+              onFocus={e => { e.currentTarget.style.borderColor = '#0ea5e9' }}
+              onBlur={e => { e.currentTarget.style.borderColor = '#cbd5e1' }}
             />
             <button
               onClick={handleTestSubmit}
               disabled={testing}
               style={{
                 padding: '10px 24px', borderRadius: 8, border: 'none',
-                background: '#2196f3', color: '#fff', cursor: 'pointer',
+                background: '#0ea5e9', color: '#fff', cursor: 'pointer',
                 opacity: testing ? 0.6 : 1, whiteSpace: 'nowrap', fontSize: 14, fontWeight: 600,
                 transition: 'all 0.2s ease',
               }}
@@ -280,7 +285,7 @@ export function Models() {
 
           {/* Error */}
           {testError && (
-            <div style={{ marginTop: 16, padding: 12, background: '#fff2f0', borderRadius: 8, color: '#cf1322', fontSize: 13, border: '1px solid #ffccc7' }}>
+            <div style={{ marginTop: 16, padding: 12, background: '#fff1f2', borderRadius: 8, color: '#e11d48', fontSize: 13, border: '1px solid #fecdd3' }}>
               {testError}
             </div>
           )}
@@ -289,15 +294,15 @@ export function Models() {
           {testResult && (
             <div style={{ marginTop: 16 }}>
               <div style={{ display: 'flex', gap: 8, marginBottom: 8 }}>
-                <span style={{ fontSize: 12, color: '#666', fontWeight: 600 }}>Prompt</span>
+                <span style={{ fontSize: 12, color: '#64748b', fontWeight: 600 }}>Prompt</span>
               </div>
-              <div style={{ padding: 14, background: '#f0f7ff', borderRadius: 8, fontSize: 14, lineHeight: 1.6, marginBottom: 16, border: '1px solid #d6e8fa' }}>
+              <div style={{ padding: 14, background: '#f0f9ff', borderRadius: 8, fontSize: 14, lineHeight: 1.6, marginBottom: 16, border: '1px solid #bae6fd' }}>
                 {testPrompt}
               </div>
               <div style={{ display: 'flex', gap: 8, marginBottom: 8 }}>
-                <span style={{ fontSize: 12, color: '#666', fontWeight: 600 }}>Response</span>
+                <span style={{ fontSize: 12, color: '#64748b', fontWeight: 600 }}>Response</span>
               </div>
-              <div style={{ padding: 14, background: '#f0fff4', borderRadius: 8, fontSize: 14, lineHeight: 1.6, whiteSpace: 'pre-wrap', wordBreak: 'break-word', border: '1px solid #d1f0d8' }}>
+              <div style={{ padding: 14, background: '#ecfdf5', borderRadius: 8, fontSize: 14, lineHeight: 1.6, whiteSpace: 'pre-wrap', wordBreak: 'break-word', border: '1px solid #d1fae5' }}>
                 {testResult}
               </div>
             </div>

+ 25 - 24
frontend/src/pages/Training.tsx

@@ -1,6 +1,7 @@
 import { useState, useEffect, useRef, useCallback, memo } from 'react'
 import api, { TrainingJob, ModelInfo, DatasetInfo } from '../api/client'
 import { wsManager } from '../api/websocket'
+import { Train } from 'lucide-react'
 
 const MODEL_TYPES = [
   { value: 'text', label: '文本 (LLaMA/Qwen)' },
@@ -205,7 +206,7 @@ function SearchableSelect({ options, value, onChange, placeholder, loading }: Se
                 onClick={() => handleSelect(opt.value)}
                 style={{
                   padding: '8px 12px', cursor: 'pointer',
-                  background: opt.value === value ? '#e94560' : 'transparent',
+                  background: opt.value === value ? '#14b8a6' : 'transparent',
                   color: opt.value === value ? '#fff' : '#333',
                   fontSize: 13,
                 }}
@@ -230,13 +231,13 @@ function SearchableSelect({ options, value, onChange, placeholder, loading }: Se
 // --- 任务状态颜色 ---
 const statusColor = (status: string) => {
   switch (status) {
-    case 'completed': return '#4caf50'
-    case 'failed': return '#e94560'
-    case 'training': return '#2196f3'
-    case 'pending': case 'queued': return '#ff9800'
-    case 'preprocessing': return '#9c27b0'
-    case 'cancelled': return '#999'
-    default: return '#666'
+    case 'completed': return '#10b981'
+    case 'failed': return '#f43f5e'
+    case 'training': return '#0ea5e9'
+    case 'pending': case 'queued': return '#f59e0b'
+    case 'preprocessing': return '#a855f7'
+    case 'cancelled': return '#94a3b8'
+    default: return '#64748b'
   }
 }
 
@@ -260,7 +261,7 @@ const JobRow = memo(function JobRow({ j, onCancel }: { j: TrainingJob; onCancel:
       borderBottom: '1px solid #f0f0f0',
       transition: 'background 0.15s ease',
     }}
-    onMouseEnter={e => { e.currentTarget.style.background = '#fafbfc' }}
+    onMouseEnter={e => { e.currentTarget.style.background = '#f0fdfa' }}
     onMouseLeave={e => { e.currentTarget.style.background = 'transparent' }}
     >
       <td style={{ padding: '12px 12px', fontFamily: 'monospace', fontSize: 12, color: '#666' }}>{j.id.slice(0, 8)}...</td>
@@ -293,12 +294,12 @@ const JobRow = memo(function JobRow({ j, onCancel }: { j: TrainingJob; onCancel:
       <td style={{ padding: '12px 12px' }}>
         {(j.status === 'training' || j.status === 'pending' || j.status === 'queued' || j.status === 'preprocessing') && (
           <button onClick={() => onCancel(j.id)} style={{
-            padding: '4px 12px', color: '#e94560', border: '1px solid #e94560',
+            padding: '4px 12px', color: '#f43f5e', border: '1px solid #f43f5e',
             borderRadius: 6, background: 'transparent', cursor: 'pointer',
             fontSize: 12, fontWeight: 500, transition: 'all 0.15s ease',
           }}
-          onMouseEnter={e => { e.currentTarget.style.background = '#e94560'; e.currentTarget.style.color = '#fff' }}
-          onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = '#e94560' }}
+          onMouseEnter={e => { e.currentTarget.style.background = '#f43f5e'; e.currentTarget.style.color = '#fff' }}
+          onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = '#f43f5e' }}
           >取消</button>
         )}
       </td>
@@ -439,8 +440,8 @@ export function Training() {
 
         {/* 核心配置 */}
         <div style={{
-          fontSize: 13, fontWeight: 600, color: '#e94560', marginBottom: 12,
-          paddingBottom: 6, borderBottom: '2px solid #fff5f7',
+          fontSize: 13, fontWeight: 600, color: '#14b8a6', marginBottom: 12,
+          paddingBottom: 6, borderBottom: '2px solid #ccfbf1',
         }}>核心配置</div>
         <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 16, marginBottom: 20 }}>
           <div>
@@ -471,8 +472,8 @@ export function Training() {
 
         {/* 训练超参 */}
         <div style={{
-          fontSize: 13, fontWeight: 600, color: '#e94560', marginBottom: 12,
-          paddingBottom: 6, borderBottom: '2px solid #fff5f7',
+          fontSize: 13, fontWeight: 600, color: '#14b8a6', marginBottom: 12,
+          paddingBottom: 6, borderBottom: '2px solid #ccfbf1',
         }}>训练超参数</div>
         <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 16, marginBottom: 20 }}>
           <div>
@@ -503,8 +504,8 @@ export function Training() {
 
         {/* 高级选项 */}
         <div style={{
-          fontSize: 13, fontWeight: 600, color: '#e94560', marginBottom: 12,
-          paddingBottom: 6, borderBottom: '2px solid #fff5f7',
+          fontSize: 13, fontWeight: 600, color: '#14b8a6', marginBottom: 12,
+          paddingBottom: 6, borderBottom: '2px solid #ccfbf1',
         }}>高级选项</div>
         <div style={{ display: 'flex', gap: 16, alignItems: 'center', marginBottom: 20 }}>
           <label style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 13, cursor: 'pointer' }}>
@@ -516,8 +517,8 @@ export function Training() {
         {/* 错误提示 */}
         {createError && (
           <div style={{
-            marginBottom: 16, padding: 12, background: '#fff2f0', borderRadius: 8,
-            fontSize: 13, color: '#cf1322', border: '1px solid #ffccc7',
+            marginBottom: 16, padding: 12, background: '#fff1f2', borderRadius: 8,
+            fontSize: 13, color: '#e11d48', border: '1px solid #fecdd3',
           }}>
             {createError}
           </div>
@@ -528,7 +529,7 @@ export function Training() {
           disabled={submitting || !modelId || !datasetId}
           style={{
             padding: '12px 36px', borderRadius: 8, border: 'none',
-            background: '#e94560', color: '#fff', cursor: 'pointer',
+            background: '#14b8a6', color: '#fff', cursor: 'pointer',
             opacity: (submitting || !modelId || !datasetId) ? 0.5 : 1,
             fontSize: 14, fontWeight: 600, transition: 'all 0.2s ease',
           }}
@@ -556,10 +557,10 @@ export function Training() {
 
         {!loading && jobs.length === 0 && (
           <div style={{
-            padding: 40, textAlign: 'center', color: '#999', fontSize: 14,
+            padding: 40, textAlign: 'center', color: '#94a3b8', fontSize: 14,
             background: '#fff', borderRadius: 10, boxShadow: '0 1px 3px rgba(0,0,0,0.06)',
           }}>
-            <div style={{ fontSize: 32, marginBottom: 8 }}>⚡</div>
+            <div style={{ marginBottom: 8 }}><Train size={32} color="#94a3b8" strokeWidth={1.5} /></div>
             暂无训练任务,请先创建训练任务
           </div>
         )}
@@ -571,7 +572,7 @@ export function Training() {
           }}>
             <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
               <thead>
-                <tr style={{ background: '#fafbfc', borderBottom: '2px solid #f0f0f0', textAlign: 'left' }}>
+                <tr style={{ background: '#f0fdfa', borderBottom: '2px solid #f1f5f9', textAlign: 'left' }}>
                   <th style={{ padding: '10px 12px', fontSize: 12, color: '#666', fontWeight: 600 }}>任务 ID</th>
                   <th style={{ padding: '10px 12px', fontSize: 12, color: '#666', fontWeight: 600 }}>模型</th>
                   <th style={{ padding: '10px 12px', fontSize: 12, color: '#666', fontWeight: 600 }}>PEFT</th>

+ 1 - 1
frontend/tsconfig.tsbuildinfo

@@ -1 +1 @@
-{"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/client.ts","./src/api/websocket.ts","./src/components/layout/layout.tsx","./src/pages/dashboard.tsx","./src/pages/datasets.tsx","./src/pages/deployment.tsx","./src/pages/evaluation.tsx","./src/pages/inference.tsx","./src/pages/models.tsx","./src/pages/training.tsx","./src/stores/trainingstore.ts"],"version":"5.6.3"}
+{"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/client.ts","./src/api/websocket.ts","./src/components/layout/layout.tsx","./src/contexts/authcontext.tsx","./src/pages/authcallback.tsx","./src/pages/authlogin.tsx","./src/pages/dashboard.tsx","./src/pages/datasets.tsx","./src/pages/deployment.tsx","./src/pages/evaluation.tsx","./src/pages/inference.tsx","./src/pages/models.tsx","./src/pages/training.tsx","./src/stores/trainingstore.ts"],"version":"5.6.3"}