import { useEffect, useState, useCallback } from 'react' import { useParams, useNavigate } from 'react-router-dom' import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts' import { Card, List, NavBar } from 'antd-mobile' import { api } from '../../api/client.js' import TrendBadge, { calcTrend, lastPoint } from '../../components/TrendBadge.jsx' import useRefreshOnVisible from '../../hooks/useRefreshOnVisible.js' // 经济数据 // 列表态 (默认): 列出所有指标, 每行显示最新值 + 趋势徽标, 点击进入详情 // 详情态: 顶部 NavBar (返回) + 折线图 + 数据表 // 频率中文标签 (对应 Indicator.frequency) const FREQ_LABEL = { year: '年', quarter: '季', month: '月', day: '日' } // 统一数据行的时间字段: 兼容 {month}/{year}/{period} 三种存储形状 function periodOf(r) { return (r && (r.period || r.month || r.year)) || '' } export default function DataView() { const [indicators, setIndicators] = useState([]) const [err, setErr] = useState('') const [loading, setLoading] = useState(true) // 路由驱动: /macro/data 列表, /macro/data/:name 详情, 浏览器/手机返回键天然可用 const { name } = useParams() const navigate = useNavigate() const load = useCallback(() => { setLoading(true) api('/indicators') .then((data) => { setErr(''); setIndicators(data.indicators || []) }) .catch((e) => setErr(e.message)) .finally(() => setLoading(false)) }, []) useEffect(() => { load() }, [load]) // 后台回前台时重新拉取 useRefreshOnVisible(load) if (loading) return
加载中...
if (err) return
{err}
if (!indicators.length) return
暂无指标数据, 请先执行 npm run db:seed
// 详情态 (路径 /macro/data/:name) if (name) { const cur = indicators.find((i) => i.name === name) if (cur) return navigate('/macro/data')} /> } // 列表态 const labelOf = (i) => i.title || i.name return (
经济数据
{indicators.map((i) => { const lp = lastPoint({ rows: i.points || [] }) const { trend, change } = calcTrend(i.points || []) const unit = i.unit || '' return ( {lp.value}{unit} · {periodOf(lp)} : '暂无数据'} extra={} arrowIcon clickable onClick={() => navigate('/macro/data/' + i.name)} /> ) })}
) } // 详情: 折线图 + 数据表 function DataDetail({ indicator, onBack }) { const i = indicator const rows = (i.points || []) // 图表 X 轴统一用 period 字段 const chartRows = rows.map((r) => ({ ...r, period: periodOf(r) })) // 表格按时间反序: 最新在前 const tableRows = [...rows].reverse() const [showAll, setShowAll] = useState(false) const visibleRows = showAll ? tableRows : tableRows.slice(0, 10) const lp = lastPoint({ rows }) const { trend } = calcTrend(rows) const title = i.title || i.name return (
{title}
单位 {i.unit || '-'} · 频率 {FREQ_LABEL[i.frequency] || i.frequency || '-'} · 来源 {i.source || 'seed'} · {lp && <> 最新 {lp.value}{i.unit || ''} ({periodOf(lp)}) }
{visibleRows.map((r) => ( ))}
月份数值
{periodOf(r)}{r.value}
{tableRows.length > 10 && !showAll && (
setShowAll(true)}> 展开全部(共 {tableRows.length} 行)
)}
) }