feat: 管理界面报刊风统一改造 — 移除独立暗色主题,预测/回测/监控/配置页增强

This commit is contained in:
shangfangjian
2026-09-18 10:42:14 +08:00
parent 91e406f5ee
commit cf0751d1ff
15 changed files with 1275 additions and 828 deletions
+45 -46
View File
@@ -1,13 +1,13 @@
/**
* Admin 后台 - 仪表盘
* Admin 后台 - 仪表盘(报刊风)
*
* 响应式布局: 移动端 1 列 → 平板 2 列 → 桌面 4 列
* 响应式: 移动端 1 列 → 平板 2 列 → 桌面 4 列
*/
import { useEffect, useState } from 'react'
import { fetchDashboard, fetchHealth } from '../dal'
import type { DashboardStats } from '../types'
import { Card, CardBody, CardHeader, StatCard, Badge } from '../components'
import { Card, CardBody, CardHeader, StatCard, Alert, SkeletonBlock } from '../components'
export default function Dashboard() {
const [data, setData] = useState<DashboardStats | null>(null)
@@ -36,65 +36,58 @@ export default function Dashboard() {
if (error) {
return (
<div className="rounded-lg border border-red-500/30 bg-red-500/10 p-6 text-center text-red-400">
<p className="text-lg font-medium"></p>
<p className="mt-1 text-sm">{error}</p>
<button
onClick={() => window.location.reload()}
className="mt-3 rounded-md bg-red-500/20 px-4 py-2 text-sm hover:bg-red-500/30 min-h-[44px]"
>
<div className="space-y-4">
<Alert kind="error" title="加载仪表盘失败" message={error} />
<button onClick={() => window.location.reload()} className="btn btn-sm">
</button>
</div>
)
}
const healthText =
health?.status === 'healthy' || health?.status === 'ok' ? '正常' : '异常'
return (
<div className="space-y-6">
{/* 统计卡片: 移动端 1 列 → 平板 2 列 → 桌面 4 列 */}
{/* 统计:报纸数字版式 */}
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
<StatCard
label="健康状态"
value={loading ? '—' : (health?.status === 'healthy' ? '✅ 正常' : '⚠️ 异常')}
icon="●"
/>
<StatCard
label="联赛数"
value={loading ? '—' : data?.leagues.length ?? 0}
icon="◫"
/>
<StatCard
label="比赛数"
value={loading ? '—' : data?.total_matches ?? 0}
icon="◆"
/>
<StatCard
label="预测数"
value={loading ? '—' : data?.total_predictions ?? 0}
icon="◇"
value={loading ? '—' : healthText}
hint={loading ? undefined : '每 60 秒随报眉自动复检'}
/>
<StatCard label="联赛数" value={loading ? '—' : data?.leagues.length ?? 0} />
<StatCard label="比赛数" value={loading ? '—' : data?.total_matches ?? 0} />
<StatCard label="预测数" value={loading ? '—' : data?.total_predictions ?? 0} />
</div>
{/* 联赛列表 */}
<Card>
<CardHeader title="最近联赛" />
<CardHeader title="已入库联赛" />
<CardBody>
{loading ? (
<div className="flex flex-wrap gap-2">
{[1, 2, 3, 4].map(i => (
<div key={i} className="h-6 w-20 animate-pulse rounded bg-gray-800" />
<SkeletonBlock key={i} className="h-6 w-24" />
))}
</div>
) : data && data.leagues.length > 0 ? (
<div className="flex flex-wrap gap-2">
{data.leagues.map(l => (
<Badge key={l.code} status="info">
{l.name_zh || l.name} ({l.code})
</Badge>
<span
key={l.code}
className="border border-ink-200 px-2.5 py-1 text-xs text-ink-700"
>
{l.name_zh || l.name}
<span className="ml-1.5 font-mono text-2xs text-ink-400">{l.code}</span>
</span>
))}
</div>
) : (
<p className="text-gray-400">,</p>
<p className="text-xs text-ink-500">
,
</p>
)}
</CardBody>
</Card>
@@ -106,33 +99,39 @@ export default function Dashboard() {
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
<a
href="/admin/collection"
className="flex items-center gap-3 rounded-lg border border-gray-800 p-4 transition-colors hover:border-gray-700 hover:bg-gray-800/50"
className="group flex items-center justify-between border border-ink-300 p-4 transition-colors hover:border-ink-900 hover:bg-paper-100"
>
<span className="text-xl"></span>
<div>
<div className="text-sm font-medium text-gray-200"></div>
<div className="text-xs text-gray-500"></div>
<div className="font-serif text-sm font-bold text-ink-900"></div>
<div className="mt-0.5 text-2xs text-ink-500"></div>
</div>
<span className="text-ink-300 transition-colors group-hover:text-press" aria-hidden="true">
</span>
</a>
<a
href="/admin/predictions"
className="flex items-center gap-3 rounded-lg border border-gray-800 p-4 transition-colors hover:border-gray-700 hover:bg-gray-800/50"
className="group flex items-center justify-between border border-ink-300 p-4 transition-colors hover:border-ink-900 hover:bg-paper-100"
>
<span className="text-xl"></span>
<div>
<div className="text-sm font-medium text-gray-200"></div>
<div className="text-xs text-gray-500">使 LLM </div>
<div className="font-serif text-sm font-bold text-ink-900"></div>
<div className="mt-0.5 text-2xs text-ink-500"> LLM </div>
</div>
<span className="text-ink-300 transition-colors group-hover:text-press" aria-hidden="true">
</span>
</a>
<a
href="/admin/backtest"
className="flex items-center gap-3 rounded-lg border border-gray-800 p-4 transition-colors hover:border-gray-700 hover:bg-gray-800/50"
className="group flex items-center justify-between border border-ink-300 p-4 transition-colors hover:border-ink-900 hover:bg-paper-100"
>
<span className="text-xl"></span>
<div>
<div className="text-sm font-medium text-gray-200"></div>
<div className="text-xs text-gray-500"></div>
<div className="font-serif text-sm font-bold text-ink-900"></div>
<div className="mt-0.5 text-2xs text-ink-500"></div>
</div>
<span className="text-ink-300 transition-colors group-hover:text-press" aria-hidden="true">
</span>
</a>
</div>
</CardBody>