修改架构,移动端优先。
save
This commit is contained in:
@@ -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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user