feat: 管理界面报刊风统一改造 — 移除独立暗色主题,预测/回测/监控/配置页增强
This commit is contained in:
@@ -1,17 +1,17 @@
|
||||
/**
|
||||
* Admin 后台 - 数据源管理页面
|
||||
* Admin 后台 - 数据源管理页面(报刊风)
|
||||
*
|
||||
* 功能:
|
||||
* - 显示当前数据源状态 (bzzoiro / understat / injuries)
|
||||
* - 显示 API Key 配置状态(脱敏显示)
|
||||
* - 测试连接按钮(调用采集 API 测试)
|
||||
* - 采集历史记录
|
||||
* - 显示 API Key 配置状态(脱敏显示)
|
||||
* - 测试连接按钮(调用采集 API 验证)
|
||||
* - 数据源说明
|
||||
*/
|
||||
|
||||
import { useEffect, useState, useCallback } from 'react'
|
||||
import { fetchDataSourceStatuses, testDataSource } from '../dal'
|
||||
import type { DataSourceStatus } from '../types'
|
||||
import { Card, CardBody, CardHeader, Badge, SectionHeader } from '../components'
|
||||
import { Card, CardBody, CardHeader, Badge, SectionHeader, Alert, Spinner, SkeletonBlock } from '../components'
|
||||
|
||||
export default function DataSourcesPage() {
|
||||
const [sources, setSources] = useState<DataSourceStatus[]>([])
|
||||
@@ -37,7 +37,7 @@ export default function DataSourcesPage() {
|
||||
setTestingSource(sourceName)
|
||||
setTestResults(prev => ({ ...prev, [sourceName]: { success: false, message: '测试中...' } }))
|
||||
try {
|
||||
await testDataSource(sourceName as any)
|
||||
await testDataSource(sourceName as 'bzzoiro' | 'understat' | 'injuries')
|
||||
setTestResults(prev => ({ ...prev, [sourceName]: { success: true, message: '连接成功' } }))
|
||||
} catch (err: unknown) {
|
||||
const msg = err instanceof Error ? err.message : '连接失败'
|
||||
@@ -51,7 +51,7 @@ export default function DataSourcesPage() {
|
||||
<div className="space-y-6">
|
||||
<SectionHeader
|
||||
title="数据源管理"
|
||||
description="管理数据采集源配置与连接状态"
|
||||
description="数据采集源的配置状态与连通性测试。"
|
||||
/>
|
||||
|
||||
{/* 数据源卡片 */}
|
||||
@@ -60,10 +60,10 @@ export default function DataSourcesPage() {
|
||||
{[1, 2, 3].map(i => (
|
||||
<Card key={i}>
|
||||
<CardBody>
|
||||
<div className="animate-pulse space-y-3">
|
||||
<div className="h-4 w-24 rounded bg-gray-800" />
|
||||
<div className="h-3 w-32 rounded bg-gray-800" />
|
||||
<div className="h-8 w-full rounded bg-gray-800" />
|
||||
<div className="space-y-3">
|
||||
<SkeletonBlock className="h-4 w-24" />
|
||||
<SkeletonBlock className="h-3 w-32" />
|
||||
<SkeletonBlock className="h-8 w-full" />
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
@@ -77,47 +77,41 @@ export default function DataSourcesPage() {
|
||||
<Card key={source.name}>
|
||||
<CardBody className="space-y-4">
|
||||
{/* 头部 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="text-sm font-medium text-white">{source.label}</h3>
|
||||
<Badge status={source.keyConfigured ? 'success' : 'failed'}>
|
||||
{source.keyConfigured ? '已配置' : '未配置'}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-ink-200 pb-3">
|
||||
<h3 className="font-serif text-sm font-bold text-ink-900">{source.label}</h3>
|
||||
<Badge status={source.keyConfigured ? 'success' : 'error'}>
|
||||
{source.keyConfigured ? '已配置' : '未配置'}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
{/* API Key 状态 */}
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between text-xs">
|
||||
<span className="text-gray-500">API Key</span>
|
||||
<span className="font-mono text-gray-400">{source.maskedKey}</span>
|
||||
<span className="text-ink-400">API Key</span>
|
||||
<span className="font-mono text-ink-600">{source.maskedKey}</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-xs">
|
||||
<span className="text-gray-500">最近采集</span>
|
||||
<span className="text-gray-400">{source.lastIngestion || '暂无记录'}</span>
|
||||
<span className="text-ink-400">最近采集</span>
|
||||
<span className="text-ink-600">{source.lastIngestion || '暂无记录'}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 测试结果 */}
|
||||
{result && (
|
||||
<div
|
||||
className={`rounded p-2 text-xs ${
|
||||
result.success
|
||||
? 'bg-emerald-500/10 text-emerald-400 border border-emerald-500/30'
|
||||
: 'bg-red-500/10 text-red-400 border border-red-500/30'
|
||||
}`}
|
||||
>
|
||||
{result.message}
|
||||
</div>
|
||||
<Alert
|
||||
kind={result.success ? 'ok' : 'error'}
|
||||
title={result.success ? '连接成功' : '连接失败'}
|
||||
message={result.success ? undefined : result.message}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<button
|
||||
onClick={() => handleTest(source.name)}
|
||||
disabled={testingSource === source.name}
|
||||
className="w-full rounded-md border border-gray-700 bg-gray-800 px-4 py-2.5 text-sm text-gray-300 transition-colors hover:bg-gray-700 hover:text-white disabled:opacity-50 min-h-[44px]"
|
||||
className="btn btn-sm w-full"
|
||||
>
|
||||
{testingSource === source.name ? '测试中...' : '测试连接'}
|
||||
{testingSource === source.name ? (<><Spinner /> 测试中</>) : '测试连接'}
|
||||
</button>
|
||||
</CardBody>
|
||||
</Card>
|
||||
@@ -130,45 +124,28 @@ export default function DataSourcesPage() {
|
||||
<Card>
|
||||
<CardHeader title="数据源说明" />
|
||||
<CardBody>
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<div className="rounded-lg border border-gray-800 p-4">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Badge status="info">Bzzoiro</Badge>
|
||||
</div>
|
||||
<p className="text-xs text-gray-400">
|
||||
<div className="grid gap-x-6 gap-y-3 sm:grid-cols-3">
|
||||
<div className="border-t border-ink-200 pt-3">
|
||||
<Badge status="info">Bzzoiro</Badge>
|
||||
<p className="mt-2 text-xs leading-relaxed text-ink-600">
|
||||
历史赛程与比分数据,覆盖全球主要联赛。需要 API Key 配置。
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border border-gray-800 p-4">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Badge status="info">Understat</Badge>
|
||||
</div>
|
||||
<p className="text-xs text-gray-400">
|
||||
xG (预期进球) 进阶数据,无需 API Key,通过网页抓取获取。
|
||||
<div className="border-t border-ink-200 pt-3">
|
||||
<Badge status="info">Understat</Badge>
|
||||
<p className="mt-2 text-xs leading-relaxed text-ink-600">
|
||||
xG(预期进球)进阶数据,无需 API Key,通过网页抓取获取。
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border border-gray-800 p-4">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Badge status="info">Injuries</Badge>
|
||||
</div>
|
||||
<p className="text-xs text-gray-400">
|
||||
<div className="border-t border-ink-200 pt-3">
|
||||
<Badge status="info">Injuries</Badge>
|
||||
<p className="mt-2 text-xs leading-relaxed text-ink-600">
|
||||
球员伤停信息,用于预测时考虑阵容完整性。需要 API Key 配置。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
{/* 采集历史 */}
|
||||
<Card>
|
||||
<CardHeader title="采集历史" />
|
||||
<CardBody>
|
||||
<div className="text-center py-8 text-sm text-gray-500">
|
||||
<p>暂无采集历史记录</p>
|
||||
<p className="mt-1 text-xs">后端暂无采集历史端点,采集任务完成后将在此显示</p>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user