修改架构,移动端优先。

save
This commit is contained in:
cheney
2026-08-03 18:05:50 +08:00
committed by Cheney
parent 1c07b417ce
commit 08b3655a38
71 changed files with 2524 additions and 290 deletions
+108
View File
@@ -0,0 +1,108 @@
import { useEffect, useState } from 'react'
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'
import { api } from '../../api/client.js'
// 策略研究: 顶部 chip 行选策略, 下面显示 4 张图 (debt / gdp / debtToGdp / interestToGdp) + 表格
const SERIES = [
{ key: 'debt', label: '政府债务 (万亿美元)', color: '#8884d8' },
{ key: 'gdp', label: 'GDP (万亿美元)', color: '#3cb44b' },
{ key: 'debtToGdp', label: '债务/GDP (%)', color: '#4363d8' },
{ key: 'interestToGdp', label: '债务利息/GDP (%)', color: '#f58231' },
]
export default function StrategyView() {
const [strategies, setStrategies] = useState([])
const [selected, setSelected] = useState(null)
const [err, setErr] = useState('')
const [loading, setLoading] = useState(true)
useEffect(() => {
let cancelled = false
setLoading(true)
api('/strategies')
.then((data) => {
if (cancelled) return
setStrategies(data.strategies || [])
if (data.strategies && data.strategies[0]) setSelected(data.strategies[0].name)
})
.catch((e) => !cancelled && setErr(e.message))
.finally(() => !cancelled && setLoading(false))
return () => { cancelled = true }
}, [])
if (loading) return <div className="loading">加载中...</div>
if (err) return <div className="error-banner">{err}</div>
if (!strategies.length) return <div className="empty">暂无策略数据</div>
const cur = strategies.find((s) => s.name === selected)
const rows = cur ? cur.data.rows : []
return (
<div>
<div className="page-title">策略研究</div>
<div className="chip-row" style={{ paddingLeft: 12 }}>
{strategies.map((s) => (
<button
key={s.name}
className={'chip' + (selected === s.name ? ' active' : '')}
onClick={() => setSelected(s.name)}
>
{s.description || s.name}
</button>
))}
</div>
{cur && (
<div style={{ padding: '0 12px 12px' }}>
<div className="card">
<div style={{ fontWeight: 600, marginBottom: 4 }}>{cur.description || cur.name}</div>
<div style={{ fontSize: 12, color: 'var(--color-muted)', marginBottom: 8 }}>
数据 {rows.length} 条 · 来源 {cur.data.source || 'seed'}
</div>
{SERIES.map((s) => (
<div key={s.key} style={{ marginBottom: 12 }}>
<div style={{ fontSize: 13, fontWeight: 500, marginBottom: 4 }}>{s.label}</div>
<div className="chart-wrap">
<ResponsiveContainer width="100%" height={220}>
<LineChart data={rows} margin={{ top: 8, right: 12, left: 0, bottom: 0 }}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="year" tick={{ fontSize: 11 }} />
<YAxis tick={{ fontSize: 11 }} />
<Tooltip />
<Legend />
<Line type="monotone" dataKey={s.key} name={s.label} stroke={s.color} strokeWidth={2} dot={{ r: 3 }} />
</LineChart>
</ResponsiveContainer>
</div>
</div>
))}
<div style={{ maxHeight: 220, overflow: 'auto', marginTop: 8 }}>
<table className="data-table">
<thead>
<tr>
<th>年份</th>
<th style={{ textAlign: 'right' }}>债务</th>
<th style={{ textAlign: 'right' }}>GDP</th>
<th style={{ textAlign: 'right' }}>债务/GDP</th>
<th style={{ textAlign: 'right' }}>利息/GDP</th>
</tr>
</thead>
<tbody>
{rows.map((r) => (
<tr key={r.year}>
<td>{r.year}</td>
<td style={{ textAlign: 'right' }}>{r.debt}</td>
<td style={{ textAlign: 'right' }}>{r.gdp}</td>
<td style={{ textAlign: 'right' }}>{r.debtToGdp}</td>
<td style={{ textAlign: 'right' }}>{r.interestToGdp}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
)}
</div>
)
}