3v324v23 commited on
Commit
070e95d
·
1 Parent(s): 10cc58c

feat:中英文工程管理

Browse files
src/App.tsx CHANGED
@@ -1,4 +1,5 @@
1
  import { BrowserRouter as Router, Routes, Route, Navigate } from "react-router-dom";
 
2
  import Home from "@/pages/Home";
3
  import DashboardLayout from "@/pages/dashboard/Layout";
4
  import ChatPage from "@/pages/dashboard/Chat";
@@ -7,6 +8,7 @@ import StressTestPage from "@/pages/dashboard/StressTest";
7
  import BillingPage from "@/pages/dashboard/Billing";
8
 
9
  export default function App() {
 
10
  return (
11
  <Router>
12
  <Routes>
@@ -16,9 +18,9 @@ export default function App() {
16
  <Route path="chat" element={<ChatPage />} />
17
  <Route path="workflow" element={<WorkflowPage />} />
18
  <Route path="stresstest" element={<StressTestPage />} />
19
- <Route path="knowledge" element={<div className="text-zinc-500 p-8">知识库模块开发中...</div>} />
20
  <Route path="billing" element={<BillingPage />} />
21
- <Route path="settings" element={<div className="text-zinc-500 p-8">设置页面</div>} />
22
  </Route>
23
  <Route path="*" element={<Navigate to="/" replace />} />
24
  </Routes>
 
1
  import { BrowserRouter as Router, Routes, Route, Navigate } from "react-router-dom";
2
+ import { useTranslation } from 'react-i18next';
3
  import Home from "@/pages/Home";
4
  import DashboardLayout from "@/pages/dashboard/Layout";
5
  import ChatPage from "@/pages/dashboard/Chat";
 
8
  import BillingPage from "@/pages/dashboard/Billing";
9
 
10
  export default function App() {
11
+ const { t } = useTranslation();
12
  return (
13
  <Router>
14
  <Routes>
 
18
  <Route path="chat" element={<ChatPage />} />
19
  <Route path="workflow" element={<WorkflowPage />} />
20
  <Route path="stresstest" element={<StressTestPage />} />
21
+ <Route path="knowledge" element={<div className="text-zinc-500 p-8">{t('common.coming_soon')}</div>} />
22
  <Route path="billing" element={<BillingPage />} />
23
+ <Route path="settings" element={<div className="text-zinc-500 p-8">{t('common.settings_page')}</div>} />
24
  </Route>
25
  <Route path="*" element={<Navigate to="/" replace />} />
26
  </Routes>
src/locales/en/translation.json CHANGED
@@ -12,7 +12,15 @@
12
  "cancel": "Cancel",
13
  "submit": "Submit",
14
  "loading": "Loading...",
15
- "error": "Error occurred"
 
 
 
 
 
 
 
 
16
  },
17
  "dashboard": {
18
  "title": "Developer Console",
@@ -33,13 +41,87 @@
33
  "subtitle": "Drag and drop nodes to build complex AI logic",
34
  "add_node": "Add Node",
35
  "save_draft": "Save Draft",
36
- "run_test": "Run Test"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  },
38
  "billing": {
 
 
39
  "current_plan": "Current Plan",
40
  "upgrade": "Upgrade Plan",
41
  "free": "Free",
42
  "pro": "Pro",
43
- "enterprise": "Enterprise"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  }
45
  }
 
12
  "cancel": "Cancel",
13
  "submit": "Submit",
14
  "loading": "Loading...",
15
+ "error": "Error occurred",
16
+ "stresstest": "Stress Test",
17
+ "coming_soon": "Coming Soon...",
18
+ "settings_page": "Settings Page",
19
+ "or": "or",
20
+ "success": "Success",
21
+ "fail": "Fail",
22
+ "switch_en": "Switch to English",
23
+ "switch_zh": "Switch to Chinese"
24
  },
25
  "dashboard": {
26
  "title": "Developer Console",
 
41
  "subtitle": "Drag and drop nodes to build complex AI logic",
42
  "add_node": "Add Node",
43
  "save_draft": "Save Draft",
44
+ "run_test": "Run Test",
45
+ "run_error": "Error running workflow",
46
+ "run_success": "Workflow executed successfully!\nResult: {{result}}",
47
+ "run_failed": "Workflow execution failed: {{error}}",
48
+ "stresstest_confirm": "Starting stress test will simulate 10 concurrent tasks, continue?",
49
+ "stresstest_done": "Stress test complete!\nTotal time: {{duration}}\nAvg time: {{avgTime}}",
50
+ "stresstest_failed": "Failed to start stress test",
51
+ "processing_status": "Processing: {{active}} | Pending: {{pending}}"
52
+ },
53
+ "stresstest": {
54
+ "failed": "Stress Test Failed",
55
+ "description": "Simulate high concurrency to test AI workflow scheduling and processing capacity",
56
+ "active": "Active",
57
+ "pending": "Pending",
58
+ "config": "Test Config",
59
+ "testing": "Testing...",
60
+ "start": "Start Stress Test",
61
+ "task_list": "Real-time Task List",
62
+ "start_hint": "Configure then click the button on the left to start",
63
+ "task_number": "Task #",
64
+ "task_desc": "AI Workflow Processing Node",
65
+ "report": {
66
+ "title": "Test Summary Report",
67
+ "total_time": "Total Time",
68
+ "avg_time": "Avg Time"
69
+ }
70
  },
71
  "billing": {
72
+ "title": "Choose Your Plan",
73
+ "subtitle": "Select the best plan for your AI workflows. Cancel or change anytime.",
74
  "current_plan": "Current Plan",
75
  "upgrade": "Upgrade Plan",
76
  "free": "Free",
77
  "pro": "Pro",
78
+ "enterprise": "Enterprise",
79
+ "plans": {
80
+ "free": {
81
+ "features": ["500 AI Messages/mo", "Basic Workflows", "Standard Support"]
82
+ },
83
+ "pro": {
84
+ "features": ["Unlimited AI Messages", "Advanced Workflows", "Priority Support", "Knowledge Base (1GB)"]
85
+ },
86
+ "enterprise": {
87
+ "features": ["Unlimited AI Messages", "Custom Workflows", "24/7 Support", "Knowledge Base (10GB)", "Custom Domain"]
88
+ }
89
+ },
90
+ "payment_method": "Payment Method",
91
+ "alipay": "Alipay",
92
+ "wechat": "WeChat Pay",
93
+ "subscribe_now": "Subscribe Now",
94
+ "processing": "Processing...",
95
+ "sandbox_tip": "Note: Alipay is currently in sandbox mode. Do not close the page after payment, wait for redirect.",
96
+ "wechat_disabled_tip": "Note: WeChat Pay is currently pending application and unavailable. Please use Alipay for testing.",
97
+ "error_msg": "Payment service error, please try again later",
98
+ "per_month": "/mo",
99
+ "redirecting": "Redirecting to payment...",
100
+ "alipay_checkout": "Alipay Checkout",
101
+ "subscribing_for": "Subscribing for {{user}}",
102
+ "order_id": "Order ID",
103
+ "alipay_security": "Ant Group Security",
104
+ "syncing_payment": "Syncing payment status in real-time",
105
+ "scan_to_pay": "Scan with Alipay mobile app",
106
+ "login_to_pay": "Login to Alipay account",
107
+ "status": {
108
+ "cancelled_or_failed": "Payment cancelled or failed",
109
+ "incomplete": "Payment incomplete",
110
+ "timeout": "Payment timeout, please check order details later",
111
+ "closed": "Order closed"
112
+ },
113
+ "error": {
114
+ "popup_blocked": "Window blocked by browser, please allow popups and try again",
115
+ "request_failed": "Payment request failed",
116
+ "system_error": "Payment system error, please try again later"
117
+ },
118
+ "success": {
119
+ "title": "Payment Successful!",
120
+ "description": "Your subscription is active. You can now enjoy all premium features of the {{plan}} plan."
121
+ },
122
+ "failed": {
123
+ "title": "Payment Failed",
124
+ "description": "Sorry, we encountered an issue during payment, please try again later."
125
+ }
126
  }
127
  }
src/locales/zh/translation.json CHANGED
@@ -12,7 +12,15 @@
12
  "cancel": "取消",
13
  "submit": "提交",
14
  "loading": "加载中...",
15
- "error": "发生错误"
 
 
 
 
 
 
 
 
16
  },
17
  "dashboard": {
18
  "title": "开发者控制台",
@@ -33,7 +41,32 @@
33
  "subtitle": "拖拽节点构建复杂的 AI 业务逻辑",
34
  "add_node": "添加节点",
35
  "save_draft": "保存草稿",
36
- "run_test": "运行测试"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  },
38
  "billing": {
39
  "title": "选择您的订阅计划",
@@ -43,6 +76,17 @@
43
  "free": "免费版",
44
  "pro": "专业版",
45
  "enterprise": "企业版",
 
 
 
 
 
 
 
 
 
 
 
46
  "payment_method": "支付方式",
47
  "alipay": "支付宝",
48
  "wechat": "微信支付",
@@ -50,6 +94,33 @@
50
  "processing": "正在处理...",
51
  "sandbox_tip": "提示:支付宝暂时运行在沙箱测试模式。付款后请勿关闭页面,等待自动跳转。",
52
  "wechat_disabled_tip": "提示:微信支付目前正在申请中,暂时无法使用。请选择支付宝进行测试。",
53
- "error_msg": "支付服务异常,请稍后再试"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  }
55
  }
 
12
  "cancel": "取消",
13
  "submit": "提交",
14
  "loading": "加载中...",
15
+ "error": "发生错误",
16
+ "stresstest": "压力测试",
17
+ "coming_soon": "功能开发中...",
18
+ "settings_page": "设置页面",
19
+ "or": "或",
20
+ "success": "成功",
21
+ "fail": "失败",
22
+ "switch_en": "切换至英文",
23
+ "switch_zh": "切换至中文"
24
  },
25
  "dashboard": {
26
  "title": "开发者控制台",
 
41
  "subtitle": "拖拽节点构建复杂的 AI 业务逻辑",
42
  "add_node": "添加节点",
43
  "save_draft": "保存草稿",
44
+ "run_test": "运行测试",
45
+ "run_error": "工作流运行出错",
46
+ "run_success": "工作流执行成功!\n结果: {{result}}",
47
+ "run_failed": "工作流执行失败: {{error}}",
48
+ "stresstest_confirm": "启动压力测试将模拟 10 个并发任务,是否继续?",
49
+ "stresstest_done": "压力测试完成!\n总耗时: {{duration}}\n平均耗时: {{avgTime}}",
50
+ "stresstest_failed": "压力测试启动失败",
51
+ "processing_status": "正在处理: {{active}} | 待处理: {{pending}}"
52
+ },
53
+ "stresstest": {
54
+ "failed": "压力测试失败",
55
+ "description": "模拟高并发场景,测试 AI 工作流的队列调度与处理能力",
56
+ "active": "当前活动",
57
+ "pending": "待处理",
58
+ "config": "测试配置",
59
+ "testing": "正在测试...",
60
+ "start": "开始压力测试",
61
+ "task_list": "实时任务列表",
62
+ "start_hint": "配置完成后点击左侧按钮启动测试",
63
+ "task_number": "任务 #",
64
+ "task_desc": "AI 工作流处理节点",
65
+ "report": {
66
+ "title": "测试总结报告",
67
+ "total_time": "总耗时",
68
+ "avg_time": "平均耗时"
69
+ }
70
  },
71
  "billing": {
72
  "title": "选择您的订阅计划",
 
76
  "free": "免费版",
77
  "pro": "专业版",
78
  "enterprise": "企业版",
79
+ "plans": {
80
+ "free": {
81
+ "features": ["每月 500 条 AI 消息", "基础工作流", "标准技术支持"]
82
+ },
83
+ "pro": {
84
+ "features": ["无限 AI 消息", "高级工作流", "优先技术支持", "知识库 (1GB)"]
85
+ },
86
+ "enterprise": {
87
+ "features": ["无限 AI 消息", "自定义工作流", "24/7 专属支持", "知识库 (10GB)", "自定义域名"]
88
+ }
89
+ },
90
  "payment_method": "支付方式",
91
  "alipay": "支付宝",
92
  "wechat": "微信支付",
 
94
  "processing": "正在处理...",
95
  "sandbox_tip": "提示:支付宝暂时运行在沙箱测试模式。付款后请勿关闭页面,等待自动跳转。",
96
  "wechat_disabled_tip": "提示:微信支付目前正在申请中,暂时无法使用。请选择支付宝进行测试。",
97
+ "error_msg": "支付服务异常,请稍后再试",
98
+ "per_month": "/月",
99
+ "redirecting": "正在跳转到支付页面...",
100
+ "alipay_checkout": "支付宝收银台",
101
+ "subscribing_for": "正在为 {{user}} 订阅",
102
+ "order_id": "订单号",
103
+ "alipay_security": "蚂蚁集团安全保障",
104
+ "syncing_payment": "正在实时同步支付状态",
105
+ "scan_to_pay": "使用手机支付宝扫码付款",
106
+ "login_to_pay": "登录支付宝账号支付",
107
+ "status": {
108
+ "cancelled_or_failed": "支付已取消或支付失败",
109
+ "incomplete": "支付未完成",
110
+ "timeout": "支付超时,请稍后在订单详情中查看",
111
+ "closed": "订单已关闭"
112
+ },
113
+ "error": {
114
+ "popup_blocked": "窗口被浏览器拦截,请允许弹出窗口后重试",
115
+ "request_failed": "支付请求失败",
116
+ "system_error": "支付系统出错,请稍后再试"
117
+ },
118
+ "success": {
119
+ "title": "支付成功!"
120
+ },
121
+ "failed": {
122
+ "title": "支付未成功",
123
+ "description": "抱歉,支付过程中遇到了问题,请稍后重试。"
124
+ }
125
  }
126
  }
src/pages/dashboard/Billing.tsx CHANGED
@@ -52,7 +52,7 @@ export default function BillingPage() {
52
  setShowQrModal(false);
53
  } else if (e.newValue === 'false') {
54
  setPaymentStatus('error');
55
- setErrorMessage('支付已取消或支付失败');
56
  setShowQrModal(false);
57
  }
58
  localStorage.removeItem('alipay_success');
@@ -80,7 +80,7 @@ export default function BillingPage() {
80
  window.close();
81
  } else {
82
  setPaymentStatus('error');
83
- setErrorMessage(error || '支付未完成');
84
  }
85
  }
86
 
@@ -102,7 +102,7 @@ export default function BillingPage() {
102
  if (retryCount > maxRetries) {
103
  clearInterval(pollingRef.current!);
104
  setPaymentStatus('error');
105
- setErrorMessage('支付超时,请稍后在订单详情中查看');
106
  setShowQrModal(false);
107
  return;
108
  }
@@ -119,7 +119,7 @@ export default function BillingPage() {
119
  } else if (data.status === 'TRADE_CLOSED') {
120
  clearInterval(pollingRef.current!);
121
  setPaymentStatus('error');
122
- setErrorMessage('订单已关闭');
123
  setShowQrModal(false);
124
  }
125
  }
@@ -177,7 +177,7 @@ export default function BillingPage() {
177
  const paymentWindow = window.open('', 'alipay_payment', 'width=1000,height=800');
178
 
179
  if (!paymentWindow) {
180
- alert('窗口被浏览器拦截,请允许弹出窗口后重试');
181
  setLoading(false);
182
  setRedirecting(false);
183
  return;
@@ -190,12 +190,12 @@ export default function BillingPage() {
190
  window.open(data.url, '_blank');
191
  }
192
  } else if (!data.success) {
193
- alert(data.error || '支付请求失败');
194
  }
195
  } catch (error) {
196
  const err = error as Error;
197
  console.error('Payment Error:', err);
198
- alert(err.message || '支付系统出错,请稍后再试');
199
  } finally {
200
  setLoading(false);
201
  }
@@ -208,9 +208,9 @@ export default function BillingPage() {
208
  <div className="w-20 h-20 bg-green-100 text-green-600 rounded-full flex items-center justify-center mx-auto mb-8">
209
  <CheckCircle2 size={48} />
210
  </div>
211
- <h2 className="text-3xl font-bold text-zinc-900 mb-4">支付成功!</h2>
212
  <p className="text-zinc-600 mb-10 text-lg">
213
- 您的订阅已激活。现在您可以享受 {plans.find(p => p.id === searchParams.get('planId') || localStorage.getItem('alipay_plan_id') || 'pro')?.name} 套餐的所有高级功能了。
214
  </p>
215
  <button
216
  onClick={() => {
@@ -233,9 +233,9 @@ export default function BillingPage() {
233
  <div className="w-20 h-20 bg-red-100 text-red-600 rounded-full flex items-center justify-center mx-auto mb-8">
234
  <AlertCircle size={48} />
235
  </div>
236
- <h2 className="text-3xl font-bold text-zinc-900 mb-4">支付未成功</h2>
237
  <p className="text-zinc-600 mb-10 text-lg">
238
- {errorMessage || '抱歉,支付过程中遇到了问题,请稍后重试。'}
239
  </p>
240
  <div className="flex flex-col gap-4">
241
  <button
@@ -290,12 +290,12 @@ export default function BillingPage() {
290
  <h3 className="text-xl font-bold text-zinc-900 mb-2">{t(`billing.${plan.id}`)}</h3>
291
  <div className="flex items-baseline gap-1">
292
  <span className="text-4xl font-bold text-zinc-900">¥{plan.price}</span>
293
- <span className="text-zinc-500 text-sm">/月</span>
294
  </div>
295
  </div>
296
 
297
  <ul className="space-y-4 mb-8">
298
- {plan.features.map((feature, idx) => (
299
  <li key={idx} className="flex items-start gap-3 text-sm text-zinc-600">
300
  <Check size={16} className="text-green-500 mt-0.5 shrink-0" />
301
  <span>{feature}</span>
@@ -355,7 +355,7 @@ export default function BillingPage() {
355
  {loading || redirecting ? (
356
  <>
357
  <Loader2 size={20} className="animate-spin" />
358
- {redirecting ? '正在跳转到支付页面...' : t('billing.processing')}
359
  </>
360
  ) : (
361
  `${t('billing.subscribe_now')} (${t(`billing.${selectedPlan}`)} - ¥${plans.find(p => p.id === selectedPlan)?.price}/月)`
@@ -385,7 +385,7 @@ export default function BillingPage() {
385
  <div className="bg-[#f5f5f5] px-6 py-4 border-b border-zinc-200 flex items-center justify-between">
386
  <div className="flex items-center gap-2">
387
  <img src="https://img.alicdn.com/tfs/TB199S7Xrr1gK0jSZFDXXb9yVXa-200-200.png" alt="Alipay" className="w-6 h-6 object-contain" />
388
- <span className="text-sm font-bold text-zinc-800">支付宝收银台</span>
389
  </div>
390
  <button
391
  onClick={() => {
@@ -400,9 +400,9 @@ export default function BillingPage() {
400
 
401
  <div className="p-8">
402
  <div className="text-center mb-6">
403
- <div className="text-zinc-500 text-xs mb-1">正在为 {user?.id || 'Dev User'} 订阅</div>
404
  <div className="text-zinc-800 font-medium text-lg mb-2">
405
- AI Codex - {plans.find(p => p.id === selectedPlan)?.name} 套餐
406
  </div>
407
  <div className="flex items-baseline justify-center gap-1">
408
  <span className="text-3xl font-bold text-[#ff6600]">¥{plans.find(p => p.id === selectedPlan)?.price}</span>
@@ -434,15 +434,15 @@ export default function BillingPage() {
434
 
435
  <div className="flex items-center gap-2 bg-[#e6f3ff] text-[#00a3ff] px-4 py-2 rounded-lg text-xs font-medium mb-8">
436
  <div className="w-2 h-2 bg-[#00a3ff] rounded-full animate-pulse" />
437
- 使用手机支付宝扫码付款
438
  </div>
439
 
440
  <div className="w-full space-y-4">
441
  <div className="flex items-center justify-between text-[11px] text-zinc-400 border-t border-dashed border-zinc-200 pt-4">
442
- <span>订单号: {currentOrderId.slice(-12)}</span>
443
  <span className="flex items-center gap-1">
444
  <Loader2 size={10} className="animate-spin" />
445
- 正在实时同步支付状态
446
  </span>
447
  </div>
448
 
@@ -451,7 +451,7 @@ export default function BillingPage() {
451
  <span className="w-full border-t border-zinc-100" />
452
  </div>
453
  <div className="relative flex justify-center text-[10px] uppercase">
454
- <span className="bg-white px-3 text-zinc-300"></span>
455
  </div>
456
  </div>
457
 
@@ -460,7 +460,7 @@ export default function BillingPage() {
460
  className="w-full text-xs text-zinc-500 hover:text-blue-600 font-medium py-3 rounded-xl border border-zinc-100 hover:border-blue-100 hover:bg-blue-50 transition-all flex items-center justify-center gap-2"
461
  >
462
  <CreditCard size={14} />
463
- 登录支付宝账号支付
464
  </button>
465
 
466
  {/* 仅在开发模式下显示调试按钮 */}
@@ -488,7 +488,7 @@ export default function BillingPage() {
488
  <div className="bg-zinc-50 px-8 py-3 flex items-center justify-center gap-4 border-t border-zinc-100">
489
  <div className="flex items-center gap-1 grayscale opacity-50">
490
  <img src="https://img.alicdn.com/tfs/TB199S7Xrr1gK0jSZFDXXb9yVXa-200-200.png" alt="Safe" className="w-3 h-3" />
491
- <span className="text-[10px]">蚂蚁集团安全保障</span>
492
  </div>
493
  </div>
494
  </div>
 
52
  setShowQrModal(false);
53
  } else if (e.newValue === 'false') {
54
  setPaymentStatus('error');
55
+ setErrorMessage(t('billing.status.cancelled_or_failed'));
56
  setShowQrModal(false);
57
  }
58
  localStorage.removeItem('alipay_success');
 
80
  window.close();
81
  } else {
82
  setPaymentStatus('error');
83
+ setErrorMessage(error || t('billing.status.incomplete'));
84
  }
85
  }
86
 
 
102
  if (retryCount > maxRetries) {
103
  clearInterval(pollingRef.current!);
104
  setPaymentStatus('error');
105
+ setErrorMessage(t('billing.status.timeout'));
106
  setShowQrModal(false);
107
  return;
108
  }
 
119
  } else if (data.status === 'TRADE_CLOSED') {
120
  clearInterval(pollingRef.current!);
121
  setPaymentStatus('error');
122
+ setErrorMessage(t('billing.status.closed'));
123
  setShowQrModal(false);
124
  }
125
  }
 
177
  const paymentWindow = window.open('', 'alipay_payment', 'width=1000,height=800');
178
 
179
  if (!paymentWindow) {
180
+ alert(t('billing.error.popup_blocked'));
181
  setLoading(false);
182
  setRedirecting(false);
183
  return;
 
190
  window.open(data.url, '_blank');
191
  }
192
  } else if (!data.success) {
193
+ alert(data.error || t('billing.error.request_failed'));
194
  }
195
  } catch (error) {
196
  const err = error as Error;
197
  console.error('Payment Error:', err);
198
+ alert(err.message || t('billing.error.system_error'));
199
  } finally {
200
  setLoading(false);
201
  }
 
208
  <div className="w-20 h-20 bg-green-100 text-green-600 rounded-full flex items-center justify-center mx-auto mb-8">
209
  <CheckCircle2 size={48} />
210
  </div>
211
+ <h2 className="text-3xl font-bold text-zinc-900 mb-4">{t('billing.success.title')}</h2>
212
  <p className="text-zinc-600 mb-10 text-lg">
213
+ {t('billing.success.description', { plan: t(`billing.${plans.find(p => p.id === searchParams.get('planId') || localStorage.getItem('alipay_plan_id') || 'pro')?.id}`) })}
214
  </p>
215
  <button
216
  onClick={() => {
 
233
  <div className="w-20 h-20 bg-red-100 text-red-600 rounded-full flex items-center justify-center mx-auto mb-8">
234
  <AlertCircle size={48} />
235
  </div>
236
+ <h2 className="text-3xl font-bold text-zinc-900 mb-4">{t('billing.failed.title')}</h2>
237
  <p className="text-zinc-600 mb-10 text-lg">
238
+ {errorMessage || t('billing.failed.description')}
239
  </p>
240
  <div className="flex flex-col gap-4">
241
  <button
 
290
  <h3 className="text-xl font-bold text-zinc-900 mb-2">{t(`billing.${plan.id}`)}</h3>
291
  <div className="flex items-baseline gap-1">
292
  <span className="text-4xl font-bold text-zinc-900">¥{plan.price}</span>
293
+ <span className="text-zinc-500 text-sm">{t('billing.per_month')}</span>
294
  </div>
295
  </div>
296
 
297
  <ul className="space-y-4 mb-8">
298
+ {(t(`billing.plans.${plan.id}.features`, { returnObjects: true }) as string[]).map((feature, idx) => (
299
  <li key={idx} className="flex items-start gap-3 text-sm text-zinc-600">
300
  <Check size={16} className="text-green-500 mt-0.5 shrink-0" />
301
  <span>{feature}</span>
 
355
  {loading || redirecting ? (
356
  <>
357
  <Loader2 size={20} className="animate-spin" />
358
+ {redirecting ? t('billing.redirecting') : t('billing.processing')}
359
  </>
360
  ) : (
361
  `${t('billing.subscribe_now')} (${t(`billing.${selectedPlan}`)} - ¥${plans.find(p => p.id === selectedPlan)?.price}/月)`
 
385
  <div className="bg-[#f5f5f5] px-6 py-4 border-b border-zinc-200 flex items-center justify-between">
386
  <div className="flex items-center gap-2">
387
  <img src="https://img.alicdn.com/tfs/TB199S7Xrr1gK0jSZFDXXb9yVXa-200-200.png" alt="Alipay" className="w-6 h-6 object-contain" />
388
+ <span className="text-sm font-bold text-zinc-800">{t('billing.alipay_checkout')}</span>
389
  </div>
390
  <button
391
  onClick={() => {
 
400
 
401
  <div className="p-8">
402
  <div className="text-center mb-6">
403
+ <div className="text-zinc-500 text-xs mb-1">{t('billing.subscribing_for', { user: user?.id || 'Dev User' })}</div>
404
  <div className="text-zinc-800 font-medium text-lg mb-2">
405
+ AI Codex - {t(`billing.${plans.find(p => p.id === selectedPlan)?.id}`)}
406
  </div>
407
  <div className="flex items-baseline justify-center gap-1">
408
  <span className="text-3xl font-bold text-[#ff6600]">¥{plans.find(p => p.id === selectedPlan)?.price}</span>
 
434
 
435
  <div className="flex items-center gap-2 bg-[#e6f3ff] text-[#00a3ff] px-4 py-2 rounded-lg text-xs font-medium mb-8">
436
  <div className="w-2 h-2 bg-[#00a3ff] rounded-full animate-pulse" />
437
+ {t('billing.scan_to_pay')}
438
  </div>
439
 
440
  <div className="w-full space-y-4">
441
  <div className="flex items-center justify-between text-[11px] text-zinc-400 border-t border-dashed border-zinc-200 pt-4">
442
+ <span>{t('billing.order_id')}: {currentOrderId.slice(-12)}</span>
443
  <span className="flex items-center gap-1">
444
  <Loader2 size={10} className="animate-spin" />
445
+ {t('billing.syncing_payment')}
446
  </span>
447
  </div>
448
 
 
451
  <span className="w-full border-t border-zinc-100" />
452
  </div>
453
  <div className="relative flex justify-center text-[10px] uppercase">
454
+ <span className="bg-white px-3 text-zinc-300">{t('common.or')}</span>
455
  </div>
456
  </div>
457
 
 
460
  className="w-full text-xs text-zinc-500 hover:text-blue-600 font-medium py-3 rounded-xl border border-zinc-100 hover:border-blue-100 hover:bg-blue-50 transition-all flex items-center justify-center gap-2"
461
  >
462
  <CreditCard size={14} />
463
+ {t('billing.login_to_pay')}
464
  </button>
465
 
466
  {/* 仅在开发模式下显示调试按钮 */}
 
488
  <div className="bg-zinc-50 px-8 py-3 flex items-center justify-center gap-4 border-t border-zinc-100">
489
  <div className="flex items-center gap-1 grayscale opacity-50">
490
  <img src="https://img.alicdn.com/tfs/TB199S7Xrr1gK0jSZFDXXb9yVXa-200-200.png" alt="Safe" className="w-3 h-3" />
491
+ <span className="text-[10px]">{t('billing.alipay_security')}</span>
492
  </div>
493
  </div>
494
  </div>
src/pages/dashboard/Layout.tsx CHANGED
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
2
  import { Link, Outlet, useLocation } from 'react-router-dom';
3
  import {
4
  LayoutDashboard, MessageSquare, Database, GitBranch,
5
- CreditCard, Settings, Menu, X, Globe, Gauge
6
  } from 'lucide-react';
7
  import { useTranslation } from 'react-i18next';
8
  import { useStore } from '@/store/useStore';
@@ -18,7 +18,7 @@ export default function DashboardLayout() {
18
  { icon: MessageSquare, label: t('common.chat'), path: '/dashboard/chat' },
19
  { icon: Database, label: t('common.knowledge'), path: '/dashboard/knowledge' },
20
  { icon: GitBranch, label: t('common.workflow'), path: '/dashboard/workflow' },
21
- { icon: Gauge, label: '压力测试', path: '/dashboard/stresstest' },
22
  { icon: CreditCard, label: t('common.billing'), path: '/dashboard/billing' },
23
  { icon: Settings, label: t('common.settings'), path: '/dashboard/settings' },
24
  ];
@@ -66,21 +66,23 @@ export default function DashboardLayout() {
66
  ))}
67
  </nav>
68
 
69
- {/* 语言切换 & 用户信息 */}
70
- <div className="p-4 border-t border-zinc-200 space-y-4">
71
- <button
72
- onClick={() => setLanguage(language === 'zh' ? 'en' : 'zh')}
73
- className="flex items-center gap-3 px-3 py-2 w-full text-zinc-600 hover:bg-zinc-100 rounded-lg transition-colors text-sm"
74
- >
75
- <Globe size={18} />
76
- <span>{language === 'zh' ? 'English' : '中文'}</span>
77
- </button>
78
  <div className="flex items-center gap-3 px-3 py-2">
79
- <div className="w-8 h-8 bg-zinc-200 rounded-full flex items-center justify-center text-[10px]">USER</div>
80
  <div className="flex-1 min-w-0">
81
- <p className="text-xs font-medium text-zinc-900 truncate">Dev User</p>
82
  <p className="text-[10px] text-zinc-500 truncate">{t('billing.free')}</p>
83
  </div>
 
 
 
 
 
 
 
 
 
84
  </div>
85
  </div>
86
  </div>
 
2
  import { Link, Outlet, useLocation } from 'react-router-dom';
3
  import {
4
  LayoutDashboard, MessageSquare, Database, GitBranch,
5
+ CreditCard, Settings, Menu, X, Gauge
6
  } from 'lucide-react';
7
  import { useTranslation } from 'react-i18next';
8
  import { useStore } from '@/store/useStore';
 
18
  { icon: MessageSquare, label: t('common.chat'), path: '/dashboard/chat' },
19
  { icon: Database, label: t('common.knowledge'), path: '/dashboard/knowledge' },
20
  { icon: GitBranch, label: t('common.workflow'), path: '/dashboard/workflow' },
21
+ { icon: Gauge, label: t('common.stresstest'), path: '/dashboard/stresstest' },
22
  { icon: CreditCard, label: t('common.billing'), path: '/dashboard/billing' },
23
  { icon: Settings, label: t('common.settings'), path: '/dashboard/settings' },
24
  ];
 
66
  ))}
67
  </nav>
68
 
69
+ {/* 用户信息 & 语言切换 */}
70
+ <div className="p-4 border-t border-zinc-100">
 
 
 
 
 
 
 
71
  <div className="flex items-center gap-3 px-3 py-2">
72
+ <div className="w-8 h-8 bg-blue-50 text-blue-600 rounded-full flex items-center justify-center text-[10px] font-bold ring-2 ring-white">USER</div>
73
  <div className="flex-1 min-w-0">
74
+ <p className="text-xs font-bold text-zinc-900 truncate">Dev User</p>
75
  <p className="text-[10px] text-zinc-500 truncate">{t('billing.free')}</p>
76
  </div>
77
+ <button
78
+ onClick={() => setLanguage(language === 'zh' ? 'en' : 'zh')}
79
+ className="flex items-center justify-center w-8 h-8 rounded-lg hover:bg-zinc-100 transition-all"
80
+ title={language === 'zh' ? t('common.switch_en') : t('common.switch_zh')}
81
+ >
82
+ <span className="text-[13px] text-zinc-600">
83
+ {language === 'zh' ? '中' : 'EN'}
84
+ </span>
85
+ </button>
86
  </div>
87
  </div>
88
  </div>
src/pages/dashboard/StressTest.tsx CHANGED
@@ -75,7 +75,7 @@ export default function StressTestPage() {
75
  throw new Error(data.error);
76
  }
77
  } catch (err: any) {
78
- alert('压力测试失败: ' + err.message);
79
  } finally {
80
  setIsTesting(false);
81
  }
@@ -87,23 +87,23 @@ export default function StressTestPage() {
87
  <div>
88
  <h1 className="text-2xl font-bold text-zinc-900 flex items-center gap-2">
89
  <Gauge className="text-blue-600" />
90
- 系统压力测试
91
  </h1>
92
- <p className="text-sm text-zinc-500 mt-1">模拟高并发场景,测试 AI 工作流的队列调度与处理能力</p>
93
  </div>
94
 
95
  <div className="flex items-center gap-3 bg-white p-2 rounded-xl border border-zinc-200 shadow-sm">
96
  <div className="flex items-center gap-2 px-3 py-1 border-r border-zinc-100">
97
  <Activity size={16} className="text-green-500" />
98
  <div className="text-[10px] leading-tight">
99
- <p className="text-zinc-400 uppercase font-bold">当前活动</p>
100
  <p className="text-zinc-900 font-mono">{queueStatus.active}</p>
101
  </div>
102
  </div>
103
  <div className="flex items-center gap-2 px-3 py-1">
104
  <BarChart3 size={16} className="text-blue-500" />
105
  <div className="text-[10px] leading-tight">
106
- <p className="text-zinc-400 uppercase font-bold">待处理</p>
107
  <p className="text-zinc-900 font-mono">{queueStatus.pending}</p>
108
  </div>
109
  </div>
@@ -114,7 +114,7 @@ export default function StressTestPage() {
114
  {/* 左侧控制面板 */}
115
  <div className="lg:col-span-1 space-y-6">
116
  <div className="bg-white p-6 rounded-2xl border border-zinc-200 shadow-sm space-y-6">
117
- <h2 className="text-sm font-semibold text-zinc-900 border-b border-zinc-50 pb-4">测试配置</h2>
118
 
119
  <div className="space-y-4">
120
  <div className="space-y-2">
@@ -157,31 +157,31 @@ export default function StressTestPage() {
157
  `}
158
  >
159
  {isTesting ? <Loader2 className="animate-spin" size={18} /> : <Play size={18} />}
160
- {isTesting ? '正在测试...' : '开始压力测试'}
161
  </button>
162
  </div>
163
 
164
  {summary && (
165
  <div className="bg-blue-600 text-white p-6 rounded-2xl shadow-xl space-y-4 animate-in fade-in slide-in-from-bottom-4 duration-500">
166
- <h2 className="text-xs font-bold uppercase tracking-wider opacity-70">测试总结报告</h2>
167
  <div className="grid grid-cols-2 gap-4">
168
  <div className="space-y-1">
169
- <p className="text-[10px] opacity-70">总耗时</p>
170
  <p className="text-lg font-mono font-bold">{summary.totalTime}</p>
171
  </div>
172
  <div className="space-y-1">
173
- <p className="text-[10px] opacity-70">平均耗时</p>
174
  <p className="text-lg font-mono font-bold">{summary.avgTime}</p>
175
  </div>
176
  </div>
177
  <div className="pt-4 border-t border-white/10 flex justify-between items-center">
178
  <div className="flex items-center gap-2">
179
  <CheckCircle2 size={14} className="text-green-300" />
180
- <span className="text-xs font-medium">成功: {summary.successCount}</span>
181
  </div>
182
  <div className="flex items-center gap-2">
183
  <AlertCircle size={14} className="text-red-300" />
184
- <span className="text-xs font-medium">失败: {summary.failCount}</span>
185
  </div>
186
  </div>
187
  </div>
@@ -192,7 +192,7 @@ export default function StressTestPage() {
192
  <div className="lg:col-span-2">
193
  <div className="bg-white rounded-2xl border border-zinc-200 shadow-sm overflow-hidden flex flex-col h-[600px]">
194
  <div className="p-4 border-b border-zinc-100 bg-zinc-50/50 flex items-center justify-between">
195
- <h2 className="text-sm font-semibold text-zinc-900">实时任务列表</h2>
196
  <span className="text-[10px] font-mono text-zinc-400">TOTAL: {results.length}</span>
197
  </div>
198
 
@@ -200,7 +200,7 @@ export default function StressTestPage() {
200
  {results.length === 0 ? (
201
  <div className="h-full flex flex-col items-center justify-center text-zinc-400 space-y-2">
202
  <Clock size={32} strokeWidth={1.5} />
203
- <p className="text-xs">配置完成后点击左侧按钮启动测试</p>
204
  </div>
205
  ) : (
206
  results.map((res) => (
@@ -220,8 +220,8 @@ export default function StressTestPage() {
220
  <Clock size={16} />}
221
  </div>
222
  <div>
223
- <p className="text-xs font-medium text-zinc-900">任务 #{res.id.split('-')[1]}</p>
224
- <p className="text-[10px] text-zinc-400">AI 工作流处理节点</p>
225
  </div>
226
  </div>
227
 
 
75
  throw new Error(data.error);
76
  }
77
  } catch (err: any) {
78
+ alert(t('stresstest.failed') + ': ' + err.message);
79
  } finally {
80
  setIsTesting(false);
81
  }
 
87
  <div>
88
  <h1 className="text-2xl font-bold text-zinc-900 flex items-center gap-2">
89
  <Gauge className="text-blue-600" />
90
+ {t('common.stresstest')}
91
  </h1>
92
+ <p className="text-sm text-zinc-500 mt-1">{t('stresstest.description')}</p>
93
  </div>
94
 
95
  <div className="flex items-center gap-3 bg-white p-2 rounded-xl border border-zinc-200 shadow-sm">
96
  <div className="flex items-center gap-2 px-3 py-1 border-r border-zinc-100">
97
  <Activity size={16} className="text-green-500" />
98
  <div className="text-[10px] leading-tight">
99
+ <p className="text-zinc-400 uppercase font-bold">{t('stresstest.active')}</p>
100
  <p className="text-zinc-900 font-mono">{queueStatus.active}</p>
101
  </div>
102
  </div>
103
  <div className="flex items-center gap-2 px-3 py-1">
104
  <BarChart3 size={16} className="text-blue-500" />
105
  <div className="text-[10px] leading-tight">
106
+ <p className="text-zinc-400 uppercase font-bold">{t('stresstest.pending')}</p>
107
  <p className="text-zinc-900 font-mono">{queueStatus.pending}</p>
108
  </div>
109
  </div>
 
114
  {/* 左侧控制面板 */}
115
  <div className="lg:col-span-1 space-y-6">
116
  <div className="bg-white p-6 rounded-2xl border border-zinc-200 shadow-sm space-y-6">
117
+ <h2 className="text-sm font-semibold text-zinc-900 border-b border-zinc-50 pb-4">{t('stresstest.config')}</h2>
118
 
119
  <div className="space-y-4">
120
  <div className="space-y-2">
 
157
  `}
158
  >
159
  {isTesting ? <Loader2 className="animate-spin" size={18} /> : <Play size={18} />}
160
+ {isTesting ? t('stresstest.testing') : t('stresstest.start')}
161
  </button>
162
  </div>
163
 
164
  {summary && (
165
  <div className="bg-blue-600 text-white p-6 rounded-2xl shadow-xl space-y-4 animate-in fade-in slide-in-from-bottom-4 duration-500">
166
+ <h2 className="text-xs font-bold uppercase tracking-wider opacity-70">{t('stresstest.report.title')}</h2>
167
  <div className="grid grid-cols-2 gap-4">
168
  <div className="space-y-1">
169
+ <p className="text-[10px] opacity-70">{t('stresstest.report.total_time')}</p>
170
  <p className="text-lg font-mono font-bold">{summary.totalTime}</p>
171
  </div>
172
  <div className="space-y-1">
173
+ <p className="text-[10px] opacity-70">{t('stresstest.report.avg_time')}</p>
174
  <p className="text-lg font-mono font-bold">{summary.avgTime}</p>
175
  </div>
176
  </div>
177
  <div className="pt-4 border-t border-white/10 flex justify-between items-center">
178
  <div className="flex items-center gap-2">
179
  <CheckCircle2 size={14} className="text-green-300" />
180
+ <span className="text-xs font-medium">{t('common.success')}: {summary.successCount}</span>
181
  </div>
182
  <div className="flex items-center gap-2">
183
  <AlertCircle size={14} className="text-red-300" />
184
+ <span className="text-xs font-medium">{t('common.fail')}: {summary.failCount}</span>
185
  </div>
186
  </div>
187
  </div>
 
192
  <div className="lg:col-span-2">
193
  <div className="bg-white rounded-2xl border border-zinc-200 shadow-sm overflow-hidden flex flex-col h-[600px]">
194
  <div className="p-4 border-b border-zinc-100 bg-zinc-50/50 flex items-center justify-between">
195
+ <h2 className="text-sm font-semibold text-zinc-900">{t('stresstest.task_list')}</h2>
196
  <span className="text-[10px] font-mono text-zinc-400">TOTAL: {results.length}</span>
197
  </div>
198
 
 
200
  {results.length === 0 ? (
201
  <div className="h-full flex flex-col items-center justify-center text-zinc-400 space-y-2">
202
  <Clock size={32} strokeWidth={1.5} />
203
+ <p className="text-xs">{t('stresstest.start_hint')}</p>
204
  </div>
205
  ) : (
206
  results.map((res) => (
 
220
  <Clock size={16} />}
221
  </div>
222
  <div>
223
+ <p className="text-xs font-medium text-zinc-900">{t('stresstest.task_number')}{res.id.split('-')[1]}</p>
224
+ <p className="text-[10px] text-zinc-400">{t('stresstest.task_desc')}</p>
225
  </div>
226
  </div>
227
 
src/pages/dashboard/Workflow.tsx CHANGED
@@ -78,18 +78,18 @@ export default function WorkflowPage() {
78
  });
79
  const data = await response.json();
80
  if (data.success) {
81
- alert(`工作流执行成功!\n结果: ${data.result}`);
82
  } else {
83
- alert(`工作流执行失败: ${data.error}`);
84
  }
85
  } catch (err) {
86
  console.error('Workflow run error:', err);
87
- alert('工作流运行出错');
88
  }
89
  }, [nodes, edges]);
90
 
91
  const onStressTest = useCallback(async () => {
92
- if (!confirm('启动压力测试将模拟 10 个并发任务,是否继续?')) return;
93
  try {
94
  const response = await fetch('/api/debug/stress-test', {
95
  method: 'POST',
@@ -97,11 +97,11 @@ export default function WorkflowPage() {
97
  body: JSON.stringify({ count: 10, concurrency: 3 }),
98
  });
99
  const data = await response.json();
100
- alert(`压力测试完成!\n总耗时: ${data.duration}\n平均耗时: ${data.avgTime}`);
101
  } catch (err) {
102
- alert('压力测试启动失败');
103
  }
104
- }, []);
105
 
106
  return (
107
  <div className="h-full flex flex-col bg-white rounded-xl shadow-sm border border-zinc-200 overflow-hidden">
@@ -114,7 +114,7 @@ export default function WorkflowPage() {
114
  {queueStatus.active > 0 && (
115
  <div className="flex items-center gap-1.5 px-2 py-1 bg-zinc-100 rounded text-[10px] text-zinc-600 animate-pulse">
116
  <Activity size={10} className="text-blue-500" />
117
- 正在处理: {queueStatus.active} | 待处理: {queueStatus.pending}
118
  </div>
119
  )}
120
  <button
@@ -143,7 +143,7 @@ export default function WorkflowPage() {
143
  className="flex items-center gap-1.5 px-3 py-1.5 bg-zinc-900 text-white rounded-lg text-[10px] font-medium hover:bg-zinc-800 transition-colors"
144
  >
145
  <Activity size={12} />
146
- 压力测试
147
  </button>
148
  </div>
149
  </div>
 
78
  });
79
  const data = await response.json();
80
  if (data.success) {
81
+ alert(t('workflow.run_success', { result: data.result }));
82
  } else {
83
+ alert(t('workflow.run_failed', { error: data.error }));
84
  }
85
  } catch (err) {
86
  console.error('Workflow run error:', err);
87
+ alert(t('workflow.run_error'));
88
  }
89
  }, [nodes, edges]);
90
 
91
  const onStressTest = useCallback(async () => {
92
+ if (!confirm(t('workflow.stresstest_confirm'))) return;
93
  try {
94
  const response = await fetch('/api/debug/stress-test', {
95
  method: 'POST',
 
97
  body: JSON.stringify({ count: 10, concurrency: 3 }),
98
  });
99
  const data = await response.json();
100
+ alert(t('workflow.stresstest_done', { duration: data.duration, avgTime: data.avgTime }));
101
  } catch (err) {
102
+ alert(t('workflow.stresstest_failed'));
103
  }
104
+ }, [t]);
105
 
106
  return (
107
  <div className="h-full flex flex-col bg-white rounded-xl shadow-sm border border-zinc-200 overflow-hidden">
 
114
  {queueStatus.active > 0 && (
115
  <div className="flex items-center gap-1.5 px-2 py-1 bg-zinc-100 rounded text-[10px] text-zinc-600 animate-pulse">
116
  <Activity size={10} className="text-blue-500" />
117
+ {t('workflow.processing_status', { active: queueStatus.active, pending: queueStatus.pending })}
118
  </div>
119
  )}
120
  <button
 
143
  className="flex items-center gap-1.5 px-3 py-1.5 bg-zinc-900 text-white rounded-lg text-[10px] font-medium hover:bg-zinc-800 transition-colors"
144
  >
145
  <Activity size={12} />
146
+ {t('common.stresstest')}
147
  </button>
148
  </div>
149
  </div>