F1: 统一前端 HTTP 客户端 - 新增 frontend/src/lib/http.ts(共享客户端,统一超时/错误/401 处理) - Matches.tsx 的列表/分页/进行中/预测请求改用 http 客户端 - 公开站与 Admin 行为一致 D2: events 采集按联赛分批提交 - _run_bzzoiro 改为 per-league 独立事务,避免超长事务 - 单联赛失败不影响其他联赛 F3: 仪表盘真实计数 - admin_stats 补充 matches/stats/standings 真实聚合 - Dashboard 展示比赛总数/已完赛/统计行/积分榜 F4: LLM 连通性测试不依赖 match_id=1 - 新增 POST /admin/llm/ping 端点(只发一次 chat,不依赖比赛) - testLLMConnection 优先调用 ping,失败回退旧方式 F5: 导航改用 React Router Link - App.tsx 中 <a href> 全部替换为 <Link to> D5: 删除 away_possession 死代码 - bzzoiro.py 中移除计算与注释 D1: 管线基础设施表文档化 - RawEvent/IngestFailure/DataQualityCheck/DataLineage 添加「预留未启用」注释 Co-Authored-By: new-provider/LongCat-2.0 <<EMAIL>>
136 lines
4.9 KiB
TypeScript
136 lines
4.9 KiB
TypeScript
/**
|
|
* 主应用入口
|
|
*
|
|
* 页面结构:
|
|
* - / → 先知主站(报纸风赛程 + 预测),报头含「评估」「管理」入口
|
|
* - /admin/* → 管理后台(鉴权门禁,未登录引导登录)
|
|
*
|
|
* 导航策略:
|
|
* - 首页报头放「评估」「管理」入口(极简,不另加导航条)
|
|
* - 管理后台由 AdminLayout 侧边栏处理所有管理页导航
|
|
* - 未登录访问管理 → AdminLayout 门禁 → 登录页(不静默失败)
|
|
*/
|
|
|
|
import { BrowserRouter, Routes, Route, Navigate, Link } from 'react-router-dom'
|
|
import { ErrorBoundary } from './components/ErrorBoundary'
|
|
import Matches from './pages/Matches'
|
|
import Standings from './pages/Standings'
|
|
import { adminRoutes } from './admin/routes'
|
|
|
|
/** 报眉日期行 */
|
|
function dateLine(): string {
|
|
return new Date().toLocaleDateString('zh-CN', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric',
|
|
weekday: 'long',
|
|
})
|
|
}
|
|
|
|
function StandingsLayout({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<div className="min-h-screen bg-paper-50">
|
|
<header className="masthead-rule">
|
|
<div className="mx-auto max-w-5xl px-5 sm:px-8">
|
|
<div className="border-b border-ink-900 py-5 text-center sm:py-6">
|
|
<h1 className="font-brush text-5xl text-ink-900 sm:text-6xl">
|
|
先知
|
|
</h1>
|
|
</div>
|
|
<div className="flex items-center justify-between border-b border-ink-200 py-2 text-2xs text-ink-500">
|
|
<span>{dateLine()}</span>
|
|
<nav className="flex items-center gap-4" aria-label="页面导航">
|
|
<Link to="/" className="text-ink-500 hover:text-press transition-colors">
|
|
比赛 / 预测
|
|
</Link>
|
|
<Link to="/admin/eval" className="text-ink-500 hover:text-press transition-colors">
|
|
评估
|
|
</Link>
|
|
<Link to="/admin" className="flex items-center gap-1 text-ink-500 hover:text-press transition-colors">
|
|
<span aria-hidden="true">⚙</span> 管理
|
|
</Link>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main className="mx-auto max-w-5xl px-5 py-6 sm:px-8 sm:py-8">
|
|
{children}
|
|
</main>
|
|
|
|
<footer className="mx-auto max-w-5xl px-5 pb-10 sm:px-8">
|
|
<div className="border-t border-ink-200 pt-3 text-center text-2xs leading-relaxed text-ink-400">
|
|
数据由 bzzoiro 提供 · 仅供研究参考
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function HomePage() {
|
|
return (
|
|
<div className="min-h-screen bg-paper-50">
|
|
{/* ── 报头:粗线 + 居中刊名 + 日期与分区链接 ── */}
|
|
<header className="masthead-rule">
|
|
<div className="mx-auto max-w-5xl px-5 sm:px-8">
|
|
<div className="border-b border-ink-900 py-5 text-center sm:py-6">
|
|
<h1 className="font-brush text-5xl text-ink-900 sm:text-6xl">
|
|
先知
|
|
</h1>
|
|
</div>
|
|
<div className="flex items-center justify-between border-b border-ink-200 py-2 text-2xs text-ink-500">
|
|
<span>{dateLine()}</span>
|
|
<nav className="flex items-center gap-4" aria-label="页面导航">
|
|
<Link to="/standings" className="text-ink-500 hover:text-press transition-colors">
|
|
积分榜
|
|
</Link>
|
|
<Link to="/admin/eval" className="text-ink-500 hover:text-press transition-colors">
|
|
评估
|
|
</Link>
|
|
<Link to="/admin" className="flex items-center gap-1 text-ink-500 hover:text-press transition-colors">
|
|
<span aria-hidden="true">⚙</span> 管理
|
|
</Link>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main className="mx-auto max-w-5xl px-5 py-6 sm:px-8 sm:py-8">
|
|
<Matches />
|
|
</main>
|
|
|
|
<footer className="mx-auto max-w-5xl px-5 pb-10 sm:px-8">
|
|
<div className="border-t border-ink-200 pt-3 text-center text-2xs leading-relaxed text-ink-400">
|
|
预测结果由大语言模型生成 · 仅供研究参考 · 不构成任何投注建议
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function App() {
|
|
return (
|
|
<ErrorBoundary>
|
|
<BrowserRouter>
|
|
<Routes>
|
|
<Route path="/" element={<HomePage />} />
|
|
<Route path="/standings" element={<StandingsLayout><Standings /></StandingsLayout>} />
|
|
{adminRoutes.map(route => (
|
|
<Route key={route.path} path={route.path} element={route.element}>
|
|
{route.children.map(child => (
|
|
<Route
|
|
key={child.path ?? 'index'}
|
|
index={child.index}
|
|
path={child.path}
|
|
element={child.element}
|
|
/>
|
|
))}
|
|
</Route>
|
|
))}
|
|
<Route path="*" element={<Navigate to="/" replace />} />
|
|
</Routes>
|
|
</BrowserRouter>
|
|
</ErrorBoundary>
|
|
)
|
|
}
|