init: AI培训与智能巡检系统

This commit is contained in:
selfrelease
2026-06-16 00:55:20 +08:00
commit c55598494b
201 changed files with 53131 additions and 0 deletions
+126
View File
@@ -0,0 +1,126 @@
import { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { api } from '../api/client';
import type { ModelListItem } from '../types';
import {
computeSpeedEvolution,
ERAS,
pickRepresentatives,
} from '../lib/eras';
import { Thumb } from '../components/Thumb';
import { CategoryIcon, IconForward } from '../components/icons';
import { fmtEra, fmtValueUnit } from '../lib/format';
import { EvolutionCurve } from '../components/EvolutionCurve';
export function HomePage() {
const [models, setModels] = useState<ModelListItem[]>([]);
useEffect(() => {
api.models({ pageSize: 1000 }).then((d) => setModels(d.items)).catch(() => {});
}, []);
const evo = computeSpeedEvolution(models);
const total = models.length;
const years = models
.map((m) => m.first_year)
.filter((y): y is number => typeof y === 'number');
const minYear = years.length ? Math.min(...years) : 1881;
const topSpeed = Math.max(0, ...models.map((m) => m.max_speed_value ?? 0));
return (
<div className="home">
{/* 英雄区 + 时速演进:并排一行 */}
<section className="hero-row">
<div className="hero">
<h1></h1>
<p className="hero-sub">
线
<strong></strong>
</p>
<div className="hero-stats">
<div>
<b>{total || '—'}</b>
<span></span>
</div>
<div>
<b>{minYear}</b>
<span></span>
</div>
<div>
<b>{topSpeed || '—'}</b>
<span> km/h</span>
</div>
<div>
<b>4</b>
<span></span>
</div>
</div>
<Link to="/explore" className="hero-cta">
<IconForward size={16} />
</Link>
</div>
<div className="hero-evo">
<h2></h2>
<p className="muted eras-intro">
线
</p>
{evo.length >= 2 && <EvolutionCurve points={evo} />}
</div>
</section>
{/* 主线:四个时代章节 */}
<section className="eras">
<h2>沿</h2>
<div className="era-rail">
{ERAS.map((era, i) => {
const reps = pickRepresentatives(models, era, 4);
return (
<article
className="era"
key={era.key}
style={{ ['--accent' as string]: era.accent }}
>
<div className="era-head">
<span className="era-icon">
<CategoryIcon category={era.categories[0]} size={26} />
</span>
<div>
<h3>
<span className="era-step">{i + 1}</span>
{era.title}
</h3>
<span className="era-period">{era.period}</span>
</div>
</div>
<p className="era-blurb">{era.blurb}</p>
<div className="era-reps">
{reps.map((m) => (
<Link key={m.id} to={`/models/${m.id}`} className="mini">
{m.cover_url ? (
<img src={m.cover_url} alt={m.model_code} loading="lazy" />
) : (
<Thumb category={m.category} code={m.model_code} size="sm" />
)}
<span className="mini-code">{m.model_code}</span>
<span className="mini-meta">
{fmtValueUnit(m.max_speed_value, m.max_speed_unit)} ·{' '}
{fmtEra(m.first_year, m.last_year)}
</span>
</Link>
))}
</div>
<Link
to={`/explore?category=${encodeURIComponent(era.categories[0])}`}
className="era-more"
>
{era.title.replace('时代', '')} <IconForward size={14} />
</Link>
</article>
);
})}
</div>
</section>
</div>
);
}