为 WebUI 首页支持 i18n
This commit is contained in:
@@ -473,5 +473,96 @@
|
||||
"sidebarNav": "Main navigation",
|
||||
"closeMenu": "Close menu",
|
||||
"navigatedTo": "Navigated to {{page}}"
|
||||
},
|
||||
"home": {
|
||||
"title": "Live Monitor",
|
||||
"subtitle": "MaiBot runtime status and statistics overview",
|
||||
"loading": "Loading statistics...",
|
||||
"loadingHint": "Fetching MaiBot runtime data",
|
||||
"timeRange": {
|
||||
"24h": "24 Hours",
|
||||
"7d": "7 Days",
|
||||
"30d": "30 Days"
|
||||
},
|
||||
"autoRefresh": "Auto Refresh",
|
||||
"botStatus": {
|
||||
"title": "MaiBot Status",
|
||||
"running": "Running",
|
||||
"stopped": "Stopped",
|
||||
"uptime": "Up {{time}}"
|
||||
},
|
||||
"quickActions": {
|
||||
"title": "Quick Actions",
|
||||
"restart": "Restart MaiBot",
|
||||
"restarting": "Restarting...",
|
||||
"expressionReview": "Expression Review",
|
||||
"viewLogs": "View Logs",
|
||||
"pluginManage": "Plugins",
|
||||
"systemSettings": "Settings"
|
||||
},
|
||||
"survey": {
|
||||
"title": "Feedback Survey",
|
||||
"description": "Help us improve the experience",
|
||||
"webui": "WebUI Feedback",
|
||||
"maibot": "MaiBot Feedback"
|
||||
},
|
||||
"stats": {
|
||||
"totalRequests": "Total Requests",
|
||||
"totalCost": "Total Cost",
|
||||
"tokenUsage": "Token Usage",
|
||||
"avgResponse": "Avg Response",
|
||||
"avgResponseDesc": "Avg API latency",
|
||||
"onlineTime": "Online Time",
|
||||
"messageProcessing": "Messages",
|
||||
"costEfficiency": "Cost Efficiency",
|
||||
"recentPeriod": "Last {{range}}",
|
||||
"perHour": "{{value}}/hr",
|
||||
"noData": "No data",
|
||||
"replied": "{{num}} replies",
|
||||
"per100Messages": "Per 100 messages",
|
||||
"seconds": "sec",
|
||||
"hours": "hours",
|
||||
"days": "days"
|
||||
},
|
||||
"charts": {
|
||||
"tabs": {
|
||||
"trends": "Trends",
|
||||
"models": "Models",
|
||||
"activity": "Activity",
|
||||
"daily": "Daily"
|
||||
},
|
||||
"requestTrend": "Request Trend",
|
||||
"requestTrendDesc": "Request volume over the last {{hours}} hours",
|
||||
"costTrend": "Cost Trend",
|
||||
"costTrendDesc": "API call cost over time",
|
||||
"tokenUsage": "Token Usage",
|
||||
"tokenUsageDesc": "Token consumption over time",
|
||||
"modelDistribution": "Model Request Distribution",
|
||||
"modelDistributionDesc": "Usage share per model ({{count}} models)",
|
||||
"modelDetails": "Model Details",
|
||||
"modelDetailsDesc": "Requests, cost and performance",
|
||||
"recentActivity": "Recent Activity",
|
||||
"recentActivityDesc": "Latest API call records",
|
||||
"dailyStats": "Daily Statistics",
|
||||
"dailyStatsDesc": "Data summary for the last 7 days",
|
||||
"requests": "Requests",
|
||||
"cost": "Cost (¥)",
|
||||
"requestCount": "Requests",
|
||||
"costLabel": "Cost",
|
||||
"avgTime": "Avg Time",
|
||||
"timeCost": "Duration",
|
||||
"status": "Status"
|
||||
},
|
||||
"time": {
|
||||
"hoursMinutes": "{{hours}}h {{minutes}}m"
|
||||
},
|
||||
"hitokotoFallback": "Life is like a box of chocolates, you never know what you're gonna get.",
|
||||
"hitokotoFallbackFrom": "Forrest Gump",
|
||||
"unknownSource": "Unknown",
|
||||
"ariaLabel": {
|
||||
"requestTrend": "Hourly request trend chart showing request count changes over recent hours",
|
||||
"costTrend": "API cost trend chart showing API call cost changes over recent hours",
|
||||
"tokenUsage": "Token usage trend chart showing token consumption over recent hours"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,5 +473,96 @@
|
||||
"sidebarNav": "メインナビゲーション",
|
||||
"closeMenu": "メニューを閉じる",
|
||||
"navigatedTo": "{{page}} に移動しました"
|
||||
},
|
||||
"home": {
|
||||
"title": "リアルタイムモニター",
|
||||
"subtitle": "MaiBotの稼働状況と統計データの一覧",
|
||||
"loading": "統計データを読み込み中...",
|
||||
"loadingHint": "MaiBotの稼働データを取得しています",
|
||||
"timeRange": {
|
||||
"24h": "24時間",
|
||||
"7d": "7日間",
|
||||
"30d": "30日間"
|
||||
},
|
||||
"autoRefresh": "自動更新",
|
||||
"botStatus": {
|
||||
"title": "MaiBot ステータス",
|
||||
"running": "稼働中",
|
||||
"stopped": "停止中",
|
||||
"uptime": "稼働 {{time}}"
|
||||
},
|
||||
"quickActions": {
|
||||
"title": "クイック操作",
|
||||
"restart": "MaiBotを再起動",
|
||||
"restarting": "再起動中...",
|
||||
"expressionReview": "表現レビュー",
|
||||
"viewLogs": "ログを見る",
|
||||
"pluginManage": "プラグイン管理",
|
||||
"systemSettings": "システム設定"
|
||||
},
|
||||
"survey": {
|
||||
"title": "フィードバック",
|
||||
"description": "製品体験の改善にご協力ください",
|
||||
"webui": "WebUI フィードバック",
|
||||
"maibot": "MaiBot フィードバック"
|
||||
},
|
||||
"stats": {
|
||||
"totalRequests": "総リクエスト数",
|
||||
"totalCost": "総コスト",
|
||||
"tokenUsage": "トークン消費",
|
||||
"avgResponse": "平均応答",
|
||||
"avgResponseDesc": "API平均レイテンシ",
|
||||
"onlineTime": "オンライン時間",
|
||||
"messageProcessing": "メッセージ処理",
|
||||
"costEfficiency": "コスト効率",
|
||||
"recentPeriod": "直近{{range}}",
|
||||
"perHour": "{{value}}/時",
|
||||
"noData": "データなし",
|
||||
"replied": "返信 {{num}} 件",
|
||||
"per100Messages": "100メッセージあたり",
|
||||
"seconds": "秒",
|
||||
"hours": "時間",
|
||||
"days": "日"
|
||||
},
|
||||
"charts": {
|
||||
"tabs": {
|
||||
"trends": "トレンド",
|
||||
"models": "モデル",
|
||||
"activity": "アクティビティ",
|
||||
"daily": "日次統計"
|
||||
},
|
||||
"requestTrend": "リクエストトレンド",
|
||||
"requestTrendDesc": "直近{{hours}}時間のリクエスト量の変化",
|
||||
"costTrend": "コストトレンド",
|
||||
"costTrendDesc": "API呼び出しコストの変化",
|
||||
"tokenUsage": "トークン消費",
|
||||
"tokenUsageDesc": "トークン使用量の変化",
|
||||
"modelDistribution": "モデルリクエスト分布",
|
||||
"modelDistributionDesc": "各モデルの使用割合({{count}}モデル)",
|
||||
"modelDetails": "モデル詳細統計",
|
||||
"modelDetailsDesc": "リクエスト数、コスト、パフォーマンス",
|
||||
"recentActivity": "最近のアクティビティ",
|
||||
"recentActivityDesc": "最新のAPI呼び出し記録",
|
||||
"dailyStats": "日次統計",
|
||||
"dailyStatsDesc": "直近7日間のデータサマリー",
|
||||
"requests": "リクエスト数",
|
||||
"cost": "コスト(¥)",
|
||||
"requestCount": "リクエスト数",
|
||||
"costLabel": "コスト",
|
||||
"avgTime": "平均所要時間",
|
||||
"timeCost": "所要時間",
|
||||
"status": "ステータス"
|
||||
},
|
||||
"time": {
|
||||
"hoursMinutes": "{{hours}}時間{{minutes}}分"
|
||||
},
|
||||
"hitokotoFallback": "人生はチョコレートの箱のようなもの、次に何が出てくるか分からない。",
|
||||
"hitokotoFallbackFrom": "フォレスト・ガンプ",
|
||||
"unknownSource": "不明",
|
||||
"ariaLabel": {
|
||||
"requestTrend": "時間ごとのリクエスト数トレンドチャート、直近のリクエスト数変化を表示",
|
||||
"costTrend": "APIコストトレンドチャート、直近のAPI呼び出しコスト変化を表示",
|
||||
"tokenUsage": "Token消費トレンドチャート、直近のToken使用量変化を表示"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,5 +473,96 @@
|
||||
"sidebarNav": "메인 내비게이션",
|
||||
"closeMenu": "메뉴 닫기",
|
||||
"navigatedTo": "{{page}}으로 이동했습니다"
|
||||
},
|
||||
"home": {
|
||||
"title": "실시간 모니터",
|
||||
"subtitle": "MaiBot 실행 상태 및 통계 데이터 개요",
|
||||
"loading": "통계 데이터 로딩 중...",
|
||||
"loadingHint": "MaiBot 실행 데이터를 가져오는 중",
|
||||
"timeRange": {
|
||||
"24h": "24시간",
|
||||
"7d": "7일",
|
||||
"30d": "30일"
|
||||
},
|
||||
"autoRefresh": "자동 새로고침",
|
||||
"botStatus": {
|
||||
"title": "MaiBot 상태",
|
||||
"running": "실행 중",
|
||||
"stopped": "중지됨",
|
||||
"uptime": "가동 {{time}}"
|
||||
},
|
||||
"quickActions": {
|
||||
"title": "빠른 작업",
|
||||
"restart": "MaiBot 재시작",
|
||||
"restarting": "재시작 중...",
|
||||
"expressionReview": "표현 검토",
|
||||
"viewLogs": "로그 보기",
|
||||
"pluginManage": "플러그인 관리",
|
||||
"systemSettings": "시스템 설정"
|
||||
},
|
||||
"survey": {
|
||||
"title": "피드백 설문",
|
||||
"description": "제품 경험 개선에 도움을 주세요",
|
||||
"webui": "WebUI 피드백",
|
||||
"maibot": "MaiBot 피드백"
|
||||
},
|
||||
"stats": {
|
||||
"totalRequests": "총 요청 수",
|
||||
"totalCost": "총 비용",
|
||||
"tokenUsage": "토큰 소비",
|
||||
"avgResponse": "평균 응답",
|
||||
"avgResponseDesc": "API 평균 지연",
|
||||
"onlineTime": "온라인 시간",
|
||||
"messageProcessing": "메시지 처리",
|
||||
"costEfficiency": "비용 효율",
|
||||
"recentPeriod": "최근 {{range}}",
|
||||
"perHour": "{{value}}/시간",
|
||||
"noData": "데이터 없음",
|
||||
"replied": "답장 {{num}}건",
|
||||
"per100Messages": "100건당",
|
||||
"seconds": "초",
|
||||
"hours": "시간",
|
||||
"days": "일"
|
||||
},
|
||||
"charts": {
|
||||
"tabs": {
|
||||
"trends": "트렌드",
|
||||
"models": "모델",
|
||||
"activity": "활동",
|
||||
"daily": "일간 통계"
|
||||
},
|
||||
"requestTrend": "요청 트렌드",
|
||||
"requestTrendDesc": "최근 {{hours}}시간의 요청량 변화",
|
||||
"costTrend": "비용 트렌드",
|
||||
"costTrendDesc": "API 호출 비용 변화",
|
||||
"tokenUsage": "토큰 소비",
|
||||
"tokenUsageDesc": "토큰 사용량 변화",
|
||||
"modelDistribution": "모델 요청 분포",
|
||||
"modelDistributionDesc": "모델별 사용 비율 ({{count}}개 모델)",
|
||||
"modelDetails": "모델 상세 통계",
|
||||
"modelDetailsDesc": "요청 수, 비용 및 성능",
|
||||
"recentActivity": "최근 활동",
|
||||
"recentActivityDesc": "최신 API 호출 기록",
|
||||
"dailyStats": "일간 통계",
|
||||
"dailyStatsDesc": "최근 7일 데이터 요약",
|
||||
"requests": "요청 수",
|
||||
"cost": "비용(¥)",
|
||||
"requestCount": "요청 수",
|
||||
"costLabel": "비용",
|
||||
"avgTime": "평균 소요",
|
||||
"timeCost": "소요 시간",
|
||||
"status": "상태"
|
||||
},
|
||||
"time": {
|
||||
"hoursMinutes": "{{hours}}시간 {{minutes}}분"
|
||||
},
|
||||
"hitokotoFallback": "인생은 초콜릿 상자와 같아서, 다음에 무엇이 나올지 모릅니다.",
|
||||
"hitokotoFallbackFrom": "포레스트 검프",
|
||||
"unknownSource": "알 수 없음",
|
||||
"ariaLabel": {
|
||||
"requestTrend": "시간별 요청 트렌드 차트, 최근 시간대별 요청 수 변화 표시",
|
||||
"costTrend": "API 비용 트렌드 차트, 최근 시간대별 API 호출 비용 변화 표시",
|
||||
"tokenUsage": "토큰 소비 트렌드 차트, 최근 시간대별 토큰 사용량 변화 표시"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,5 +473,96 @@
|
||||
"sidebarNav": "主导航",
|
||||
"closeMenu": "关闭菜单",
|
||||
"navigatedTo": "已导航至 {{page}}"
|
||||
},
|
||||
"home": {
|
||||
"title": "实时监控面板",
|
||||
"subtitle": "麦麦运行状态和统计数据一览",
|
||||
"loading": "加载统计数据中...",
|
||||
"loadingHint": "正在获取麦麦运行数据",
|
||||
"timeRange": {
|
||||
"24h": "24小时",
|
||||
"7d": "7天",
|
||||
"30d": "30天"
|
||||
},
|
||||
"autoRefresh": "自动刷新",
|
||||
"botStatus": {
|
||||
"title": "麦麦状态",
|
||||
"running": "运行中",
|
||||
"stopped": "已停止",
|
||||
"uptime": "运行 {{time}}"
|
||||
},
|
||||
"quickActions": {
|
||||
"title": "快速操作",
|
||||
"restart": "重启麦麦",
|
||||
"restarting": "重启中...",
|
||||
"expressionReview": "表达审核",
|
||||
"viewLogs": "查看日志",
|
||||
"pluginManage": "插件管理",
|
||||
"systemSettings": "系统设置"
|
||||
},
|
||||
"survey": {
|
||||
"title": "反馈问卷",
|
||||
"description": "帮助我们改进产品体验",
|
||||
"webui": "WebUI 反馈",
|
||||
"maibot": "麦麦反馈"
|
||||
},
|
||||
"stats": {
|
||||
"totalRequests": "总请求数",
|
||||
"totalCost": "总花费",
|
||||
"tokenUsage": "Token消耗",
|
||||
"avgResponse": "平均响应",
|
||||
"avgResponseDesc": "API平均耗时",
|
||||
"onlineTime": "在线时长",
|
||||
"messageProcessing": "消息处理",
|
||||
"costEfficiency": "成本效率",
|
||||
"recentPeriod": "最近{{range}}",
|
||||
"perHour": "{{value}}/小时",
|
||||
"noData": "暂无数据",
|
||||
"replied": "回复 {{num}} 条",
|
||||
"per100Messages": "每100条消息",
|
||||
"seconds": "秒",
|
||||
"hours": "小时",
|
||||
"days": "天"
|
||||
},
|
||||
"charts": {
|
||||
"tabs": {
|
||||
"trends": "趋势",
|
||||
"models": "模型",
|
||||
"activity": "活动",
|
||||
"daily": "日统计"
|
||||
},
|
||||
"requestTrend": "请求趋势",
|
||||
"requestTrendDesc": "最近{{hours}}小时的请求量变化",
|
||||
"costTrend": "花费趋势",
|
||||
"costTrendDesc": "API调用成本变化",
|
||||
"tokenUsage": "Token消耗",
|
||||
"tokenUsageDesc": "Token使用量变化",
|
||||
"modelDistribution": "模型请求分布",
|
||||
"modelDistributionDesc": "各模型使用占比 (共 {{count}} 个模型)",
|
||||
"modelDetails": "模型详细统计",
|
||||
"modelDetailsDesc": "请求数、花费和性能",
|
||||
"recentActivity": "最近活动",
|
||||
"recentActivityDesc": "最新的API调用记录",
|
||||
"dailyStats": "每日统计",
|
||||
"dailyStatsDesc": "最近7天的数据汇总",
|
||||
"requests": "请求数",
|
||||
"cost": "花费(¥)",
|
||||
"requestCount": "请求数",
|
||||
"costLabel": "花费",
|
||||
"avgTime": "平均耗时",
|
||||
"timeCost": "耗时",
|
||||
"status": "状态"
|
||||
},
|
||||
"time": {
|
||||
"hoursMinutes": "{{hours}}小时{{minutes}}分钟"
|
||||
},
|
||||
"hitokotoFallback": "人生就像一盒巧克力,你永远不知道下一颗是什么味道。",
|
||||
"hitokotoFallbackFrom": "阿甘正传",
|
||||
"unknownSource": "未知",
|
||||
"ariaLabel": {
|
||||
"requestTrend": "每小时请求量趋势图,显示最近若干小时的请求次数变化",
|
||||
"costTrend": "API花费趋势图,显示最近若干小时的API调用成本变化",
|
||||
"tokenUsage": "Token消耗趋势图,显示最近若干小时的Token使用量变化"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useEffect, useState, useCallback, useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import axios from 'axios'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
@@ -129,6 +130,7 @@ const generatePieColors = (count: number): string[] => {
|
||||
|
||||
// 内部实现组件
|
||||
function IndexPageContent() {
|
||||
const { t } = useTranslation()
|
||||
const [dashboardData, setDashboardData] = useState<DashboardData | null>(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [loadingProgress, setLoadingProgress] = useState(0)
|
||||
@@ -179,15 +181,15 @@ function IndexPageContent() {
|
||||
if (isMountedRef.current) {
|
||||
setHitokoto({
|
||||
hitokoto: response.data.hitokoto,
|
||||
from: response.data.from || response.data.from_who || '未知'
|
||||
from: response.data.from || response.data.from_who || t('home.unknownSource')
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取一言失败:', error)
|
||||
if (isMountedRef.current) {
|
||||
setHitokoto({
|
||||
hitokoto: '人生就像一盒巧克力,你永远不知道下一颗是什么味道。',
|
||||
from: '阿甘正传'
|
||||
hitokoto: t('home.hitokotoFallback'),
|
||||
from: t('home.hitokotoFallbackFrom')
|
||||
})
|
||||
}
|
||||
} finally {
|
||||
@@ -195,7 +197,7 @@ function IndexPageContent() {
|
||||
setHitokotoLoading(false)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
}, [t])
|
||||
|
||||
// 获取机器人状态
|
||||
const fetchBotStatus = useCallback(async () => {
|
||||
@@ -310,8 +312,8 @@ function IndexPageContent() {
|
||||
<div className="text-center space-y-6 w-full max-w-md px-4">
|
||||
<RefreshCw className="h-12 w-12 animate-spin mx-auto text-primary" />
|
||||
<div className="space-y-2">
|
||||
<p className="text-lg font-medium">加载统计数据中...</p>
|
||||
<p className="text-sm text-muted-foreground">正在获取麦麦运行数据</p>
|
||||
<p className="text-lg font-medium">{t('home.loading')}</p>
|
||||
<p className="text-sm text-muted-foreground">{t('home.loadingHint')}</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Progress value={loadingProgress} className="h-2" />
|
||||
@@ -348,7 +350,7 @@ function IndexPageContent() {
|
||||
const formatTime = (seconds: number) => {
|
||||
const hours = Math.floor(seconds / 3600)
|
||||
const minutes = Math.floor((seconds % 3600) / 60)
|
||||
return `${hours}小时${minutes}分钟`
|
||||
return t('home.time.hoursMinutes', { hours, minutes })
|
||||
}
|
||||
|
||||
// 格式化大数字(自动选择合适单位)
|
||||
@@ -403,11 +405,11 @@ function IndexPageContent() {
|
||||
// 图表配置
|
||||
const chartConfig = {
|
||||
requests: {
|
||||
label: '请求数',
|
||||
label: t('home.charts.requests'),
|
||||
color: 'hsl(var(--color-chart-1))',
|
||||
},
|
||||
cost: {
|
||||
label: '花费(¥)',
|
||||
label: t('home.charts.cost'),
|
||||
color: 'hsl(var(--color-chart-2))',
|
||||
},
|
||||
tokens: {
|
||||
@@ -422,17 +424,17 @@ function IndexPageContent() {
|
||||
{/* 标题和控制栏 */}
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||
<div>
|
||||
<h1 className="text-2xl sm:text-3xl font-bold">实时监控面板</h1>
|
||||
<h1 className="text-2xl sm:text-3xl font-bold">{t('home.title')}</h1>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
麦麦运行状态和统计数据一览
|
||||
{t('home.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Tabs value={timeRange.toString()} onValueChange={(v) => setTimeRange(Number(v))}>
|
||||
<TabsList className="grid grid-cols-3 w-full sm:w-auto">
|
||||
<TabsTrigger value="24">24小时</TabsTrigger>
|
||||
<TabsTrigger value="168">7天</TabsTrigger>
|
||||
<TabsTrigger value="720">30天</TabsTrigger>
|
||||
<TabsTrigger value="24">{t('home.timeRange.24h')}</TabsTrigger>
|
||||
<TabsTrigger value="168">{t('home.timeRange.7d')}</TabsTrigger>
|
||||
<TabsTrigger value="720">{t('home.timeRange.30d')}</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
<Button
|
||||
@@ -442,7 +444,7 @@ function IndexPageContent() {
|
||||
className="gap-2"
|
||||
>
|
||||
<RefreshCw className={`h-4 w-4 ${autoRefresh ? 'animate-spin' : ''}`} />
|
||||
<span className="hidden sm:inline">自动刷新</span>
|
||||
<span className="hidden sm:inline">{t('home.autoRefresh')}</span>
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onClick={fetchDashboardData}>
|
||||
<RefreshCw className="h-4 w-4" />
|
||||
@@ -477,7 +479,7 @@ function IndexPageContent() {
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-sm font-medium flex items-center gap-2">
|
||||
<Power className="h-4 w-4" />
|
||||
麦麦状态
|
||||
{t('home.botStatus.title')}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
@@ -488,7 +490,7 @@ function IndexPageContent() {
|
||||
<div className="h-3 w-3 rounded-full bg-green-500 animate-pulse" />
|
||||
<Badge variant="outline" className="text-green-600 border-green-300 bg-green-50">
|
||||
<CheckCircle2 className="h-3 w-3 mr-1" />
|
||||
运行中
|
||||
{t('home.botStatus.running')}
|
||||
</Badge>
|
||||
</>
|
||||
) : (
|
||||
@@ -496,7 +498,7 @@ function IndexPageContent() {
|
||||
<div className="h-3 w-3 rounded-full bg-red-500" />
|
||||
<Badge variant="outline" className="text-red-600 border-red-300 bg-red-50">
|
||||
<AlertCircle className="h-3 w-3 mr-1" />
|
||||
已停止
|
||||
{t('home.botStatus.stopped')}
|
||||
</Badge>
|
||||
</>
|
||||
)}
|
||||
@@ -505,7 +507,7 @@ function IndexPageContent() {
|
||||
<div className="text-xs text-muted-foreground">
|
||||
<span>v{botStatus.version}</span>
|
||||
<span className="mx-2">|</span>
|
||||
<span>运行 {formatTime(botStatus.uptime)}</span>
|
||||
<span>{t('home.botStatus.uptime', { time: formatTime(botStatus.uptime) })}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -517,7 +519,7 @@ function IndexPageContent() {
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-sm font-medium flex items-center gap-2">
|
||||
<Zap className="h-4 w-4" />
|
||||
快速操作
|
||||
{t('home.quickActions.title')}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
@@ -530,7 +532,7 @@ function IndexPageContent() {
|
||||
className="gap-2"
|
||||
>
|
||||
<RotateCcw className={`h-4 w-4 ${isRestarting ? 'animate-spin' : ''}`} />
|
||||
{isRestarting ? '重启中...' : '重启麦麦'}
|
||||
{isRestarting ? t('home.quickActions.restarting') : t('home.quickActions.restart')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -539,7 +541,7 @@ function IndexPageContent() {
|
||||
className="gap-2"
|
||||
>
|
||||
<ClipboardCheck className="h-4 w-4" />
|
||||
表达审核
|
||||
{t('home.quickActions.expressionReview')}
|
||||
{uncheckedCount > 0 && (
|
||||
<span className="ml-1 px-1.5 py-0.5 text-xs rounded-full bg-orange-500 text-white">
|
||||
{uncheckedCount > 99 ? '99+' : uncheckedCount}
|
||||
@@ -549,19 +551,19 @@ function IndexPageContent() {
|
||||
<Button variant="outline" size="sm" asChild className="gap-2">
|
||||
<Link to="/logs">
|
||||
<FileText className="h-4 w-4" />
|
||||
查看日志
|
||||
{t('home.quickActions.viewLogs')}
|
||||
</Link>
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" asChild className="gap-2">
|
||||
<Link to="/plugins">
|
||||
<Puzzle className="h-4 w-4" />
|
||||
插件管理
|
||||
{t('home.quickActions.pluginManage')}
|
||||
</Link>
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" asChild className="gap-2">
|
||||
<Link to="/settings">
|
||||
<Settings className="h-4 w-4" />
|
||||
系统设置
|
||||
{t('home.quickActions.systemSettings')}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
@@ -573,10 +575,10 @@ function IndexPageContent() {
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-sm font-medium flex items-center gap-2">
|
||||
<ClipboardList className="h-4 w-4" />
|
||||
反馈问卷
|
||||
{t('home.survey.title')}
|
||||
</CardTitle>
|
||||
<CardDescription className="text-xs">
|
||||
帮助我们改进产品体验
|
||||
{t('home.survey.description')}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
@@ -584,13 +586,13 @@ function IndexPageContent() {
|
||||
<Button variant="outline" size="sm" asChild className="gap-2">
|
||||
<Link to="/survey/webui-feedback">
|
||||
<FileText className="h-4 w-4" />
|
||||
WebUI 反馈
|
||||
{t('home.survey.webui')}
|
||||
</Link>
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" asChild className="gap-2">
|
||||
<Link to="/survey/maibot-feedback">
|
||||
<MessageSquare className="h-4 w-4" />
|
||||
麦麦反馈
|
||||
{t('home.survey.maibot')}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
@@ -602,7 +604,7 @@ function IndexPageContent() {
|
||||
<div className="grid gap-4 grid-cols-1 xs:grid-cols-2 lg:grid-cols-4">
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium">总请求数</CardTitle>
|
||||
<CardTitle className="text-sm font-medium">{t('home.stats.totalRequests')}</CardTitle>
|
||||
<Activity className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
@@ -613,14 +615,14 @@ function IndexPageContent() {
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
最近{timeRange < 48 ? timeRange + '小时' : Math.floor(timeRange / 24) + '天'}
|
||||
{t('home.stats.recentPeriod', { range: timeRange < 48 ? timeRange + t('home.stats.hours') : Math.floor(timeRange / 24) + t('home.stats.days') })}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium">总花费</CardTitle>
|
||||
<CardTitle className="text-sm font-medium">{t('home.stats.totalCost')}</CardTitle>
|
||||
<DollarSign className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
@@ -631,14 +633,14 @@ function IndexPageContent() {
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
{summary.cost_per_hour > 0 ? `¥${summary.cost_per_hour.toFixed(2)}/小时` : '暂无数据'}
|
||||
{summary.cost_per_hour > 0 ? t('home.stats.perHour', { value: `¥${summary.cost_per_hour.toFixed(2)}` }) : t('home.stats.noData')}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium">Token消耗</CardTitle>
|
||||
<CardTitle className="text-sm font-medium">{t('home.stats.tokenUsage')}</CardTitle>
|
||||
<Database className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
@@ -650,20 +652,20 @@ function IndexPageContent() {
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
{summary.tokens_per_hour > 0
|
||||
? `${formatNumber(summary.tokens_per_hour).display}/小时`
|
||||
: '暂无数据'}
|
||||
? t('home.stats.perHour', { value: formatNumber(summary.tokens_per_hour).display })
|
||||
: t('home.stats.noData')}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium">平均响应</CardTitle>
|
||||
<CardTitle className="text-sm font-medium">{t('home.stats.avgResponse')}</CardTitle>
|
||||
<Zap className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{summary.avg_response_time.toFixed(2)}s</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">API平均耗时</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">{t('home.stats.avgResponseDesc')}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
@@ -672,20 +674,20 @@ function IndexPageContent() {
|
||||
<div className="grid gap-4 grid-cols-1 sm:grid-cols-3">
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium">在线时长</CardTitle>
|
||||
<CardTitle className="text-sm font-medium">{t('home.stats.onlineTime')}</CardTitle>
|
||||
<Clock className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-xl font-bold">
|
||||
{formatTime(summary.online_time)}
|
||||
<span className="text-xs font-normal text-muted-foreground ml-1">({summary.online_time.toLocaleString()}秒)</span>
|
||||
<span className="text-xs font-normal text-muted-foreground ml-1">({summary.online_time.toLocaleString()}{t('home.stats.seconds')})</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium">消息处理</CardTitle>
|
||||
<CardTitle className="text-sm font-medium">{t('home.stats.messageProcessing')}</CardTitle>
|
||||
<MessageSquare className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
@@ -696,17 +698,17 @@ function IndexPageContent() {
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
回复 {formatNumber(summary.total_replies).display}
|
||||
{t('home.stats.replied', { num: formatNumber(summary.total_replies).display })}
|
||||
{formatNumber(summary.total_replies).needsExact && (
|
||||
<span>({formatNumber(summary.total_replies).exact})</span>
|
||||
)} 条
|
||||
)}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium">成本效率</CardTitle>
|
||||
<CardTitle className="text-sm font-medium">{t('home.stats.costEfficiency')}</CardTitle>
|
||||
<TrendingUp className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
@@ -715,7 +717,7 @@ function IndexPageContent() {
|
||||
? `¥${((summary.total_cost / summary.total_messages) * 100).toFixed(2)}`
|
||||
: '¥0.00'}
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">每100条消息</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">{t('home.stats.per100Messages')}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
@@ -723,21 +725,21 @@ function IndexPageContent() {
|
||||
{/* 图表区域 */}
|
||||
<Tabs defaultValue="trends" className="space-y-4">
|
||||
<TabsList className="grid w-full grid-cols-2 sm:grid-cols-4">
|
||||
<TabsTrigger value="trends">趋势</TabsTrigger>
|
||||
<TabsTrigger value="models">模型</TabsTrigger>
|
||||
<TabsTrigger value="activity">活动</TabsTrigger>
|
||||
<TabsTrigger value="daily">日统计</TabsTrigger>
|
||||
<TabsTrigger value="trends">{t('home.charts.tabs.trends')}</TabsTrigger>
|
||||
<TabsTrigger value="models">{t('home.charts.tabs.models')}</TabsTrigger>
|
||||
<TabsTrigger value="activity">{t('home.charts.tabs.activity')}</TabsTrigger>
|
||||
<TabsTrigger value="daily">{t('home.charts.tabs.daily')}</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
{/* 趋势图表 */}
|
||||
<TabsContent value="trends" className="space-y-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>请求趋势</CardTitle>
|
||||
<CardDescription>最近{timeRange}小时的请求量变化</CardDescription>
|
||||
<CardTitle>{t('home.charts.requestTrend')}</CardTitle>
|
||||
<CardDescription>{t('home.charts.requestTrendDesc', { hours: timeRange })}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ZoomableChart aria-label="每小时请求量趋势图,显示最近若干小时的请求次数变化">
|
||||
<ZoomableChart aria-label={t('home.ariaLabel.requestTrend')}>
|
||||
<ChartContainer config={chartConfig} className="h-[300px] sm:h-[400px] w-full aspect-auto">
|
||||
<LineChart data={hourly_data}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="hsl(var(--color-muted-foreground) / 0.2)" />
|
||||
@@ -769,11 +771,11 @@ function IndexPageContent() {
|
||||
<div className="grid gap-4 grid-cols-1 lg:grid-cols-2">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>花费趋势</CardTitle>
|
||||
<CardDescription>API调用成本变化</CardDescription>
|
||||
<CardTitle>{t('home.charts.costTrend')}</CardTitle>
|
||||
<CardDescription>{t('home.charts.costTrendDesc')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ZoomableChart aria-label="API花费趋势图,显示最近若干小时的API调用成本变化">
|
||||
<ZoomableChart aria-label={t('home.ariaLabel.costTrend')}>
|
||||
<ChartContainer config={chartConfig} className="h-[250px] sm:h-[300px] w-full aspect-auto">
|
||||
<BarChart data={hourly_data}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="hsl(var(--color-muted-foreground) / 0.2)" />
|
||||
@@ -799,11 +801,11 @@ function IndexPageContent() {
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Token消耗</CardTitle>
|
||||
<CardDescription>Token使用量变化</CardDescription>
|
||||
<CardTitle>{t('home.charts.tokenUsage')}</CardTitle>
|
||||
<CardDescription>{t('home.charts.tokenUsageDesc')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ZoomableChart aria-label="Token消耗趋势图,显示最近若干小时的Token使用量变化">
|
||||
<ZoomableChart aria-label={t('home.ariaLabel.tokenUsage')}>
|
||||
<ChartContainer config={chartConfig} className="h-[250px] sm:h-[300px] w-full aspect-auto">
|
||||
<BarChart data={hourly_data}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="hsl(var(--color-muted-foreground) / 0.2)" />
|
||||
@@ -834,8 +836,8 @@ function IndexPageContent() {
|
||||
<div className="grid gap-4 grid-cols-1 lg:grid-cols-2">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>模型请求分布</CardTitle>
|
||||
<CardDescription>各模型使用占比 (共 {model_stats.length} 个模型)</CardDescription>
|
||||
<CardTitle>{t('home.charts.modelDistribution')}</CardTitle>
|
||||
<CardDescription>{t('home.charts.modelDistributionDesc', { count: model_stats.length })}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChartContainer
|
||||
@@ -878,8 +880,8 @@ function IndexPageContent() {
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>模型详细统计</CardTitle>
|
||||
<CardDescription>请求数、花费和性能</CardDescription>
|
||||
<CardTitle>{t('home.charts.modelDetails')}</CardTitle>
|
||||
<CardDescription>{t('home.charts.modelDetailsDesc')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ScrollArea className="h-[300px] sm:h-[400px]">
|
||||
@@ -902,13 +904,13 @@ function IndexPageContent() {
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-2 text-xs">
|
||||
<div>
|
||||
<span className="text-muted-foreground">请求数:</span>
|
||||
<span className="text-muted-foreground">{t('home.charts.requestCount')}:</span>
|
||||
<span className="ml-1 font-medium">
|
||||
{stat.request_count.toLocaleString()}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-muted-foreground">花费:</span>
|
||||
<span className="text-muted-foreground">{t('home.charts.costLabel')}:</span>
|
||||
<span className="ml-1 font-medium">¥{stat.total_cost.toFixed(2)}</span>
|
||||
</div>
|
||||
<div>
|
||||
@@ -918,7 +920,7 @@ function IndexPageContent() {
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-muted-foreground">平均耗时:</span>
|
||||
<span className="text-muted-foreground">{t('home.charts.avgTime')}:</span>
|
||||
<span className="ml-1 font-medium">
|
||||
{stat.avg_response_time.toFixed(2)}s
|
||||
</span>
|
||||
@@ -935,8 +937,8 @@ function IndexPageContent() {
|
||||
<TabsContent value="activity">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>最近活动</CardTitle>
|
||||
<CardDescription>最新的API调用记录</CardDescription>
|
||||
<CardTitle>{t('home.charts.recentActivity')}</CardTitle>
|
||||
<CardDescription>{t('home.charts.recentActivityDesc')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ScrollArea className="h-[400px] sm:h-[500px]">
|
||||
@@ -963,15 +965,15 @@ function IndexPageContent() {
|
||||
<span className="ml-1">{activity.tokens}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-muted-foreground">花费:</span>
|
||||
<span className="text-muted-foreground">{t('home.charts.costLabel')}:</span>
|
||||
<span className="ml-1">¥{activity.cost.toFixed(4)}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-muted-foreground">耗时:</span>
|
||||
<span className="text-muted-foreground">{t('home.charts.timeCost')}:</span>
|
||||
<span className="ml-1">{activity.time_cost.toFixed(2)}s</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-muted-foreground">状态:</span>
|
||||
<span className="text-muted-foreground">{t('home.charts.status')}:</span>
|
||||
<span
|
||||
className={`ml-1 ${activity.status === 'success' ? 'text-green-600' : 'text-red-600'}`}
|
||||
>
|
||||
@@ -991,18 +993,18 @@ function IndexPageContent() {
|
||||
<TabsContent value="daily">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>每日统计</CardTitle>
|
||||
<CardDescription>最近7天的数据汇总</CardDescription>
|
||||
<CardTitle>{t('home.charts.dailyStats')}</CardTitle>
|
||||
<CardDescription>{t('home.charts.dailyStatsDesc')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChartContainer
|
||||
config={{
|
||||
requests: {
|
||||
label: '请求数',
|
||||
label: t('home.charts.requests'),
|
||||
color: 'hsl(var(--color-chart-1))',
|
||||
},
|
||||
cost: {
|
||||
label: '花费(¥)',
|
||||
label: t('home.charts.cost'),
|
||||
color: 'hsl(var(--color-chart-2))',
|
||||
},
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user