更换 UI
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'
|
||||
import { CapsuleTabs, Card } from 'antd-mobile'
|
||||
import { api } from '../../api/client.js'
|
||||
|
||||
// 经济数据: 从 /api/indicators 拉所有指标, 顶上一个 chip 行切换, 下面渲染图表+表格
|
||||
// 经济数据: 顶部 CapsuleTabs 选指标, 下面渲染图表 + 表格
|
||||
// 使用 antd-mobile: CapsuleTabs / Card
|
||||
// 多 groupName 的指标: 在 chip 文本中携带分组前缀以便区分
|
||||
export default function DataView() {
|
||||
const [indicators, setIndicators] = useState([])
|
||||
const [selected, setSelected] = useState(null)
|
||||
@@ -27,37 +30,32 @@ export default function DataView() {
|
||||
if (err) return <div className="error-banner">{err}</div>
|
||||
if (!indicators.length) return <div className="empty">暂无指标数据, 请先执行 npm run db:seed</div>
|
||||
|
||||
// 按 groupName 分组, chip 行按组排列
|
||||
const groups = groupBy(indicators, 'groupName')
|
||||
// 为 chip label 计算: 多个 group 时显示 "分组 / 标题", 单个 group 只显示标题
|
||||
const groupSet = new Set(indicators.map((i) => i.groupName || ''))
|
||||
const showGroupPrefix = groupSet.size > 1
|
||||
const labelOf = (i) => {
|
||||
const t = (i.data && i.data.title) || i.name
|
||||
if (showGroupPrefix && i.groupName) return i.groupName + ' · ' + t
|
||||
return t
|
||||
}
|
||||
|
||||
const cur = indicators.find((i) => i.name === selected)
|
||||
const chartData = cur ? buildMonthlySeries(cur.data) : []
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="page-title">经济数据</div>
|
||||
{Object.entries(groups).map(([groupName, list]) => (
|
||||
<div key={groupName}>
|
||||
<div className="section-title">{groupName}</div>
|
||||
<div className="chip-row">
|
||||
{list.map((i) => (
|
||||
<button
|
||||
key={i.name}
|
||||
className={'chip' + (selected === i.name ? ' active' : '')}
|
||||
onClick={() => setSelected(i.name)}
|
||||
>
|
||||
{shortTitle(i)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<CapsuleTabs activeKey={selected || undefined} onChange={setSelected}>
|
||||
{indicators.map((i) => (
|
||||
<CapsuleTabs.Tab key={i.name} title={labelOf(i)} />
|
||||
))}
|
||||
</CapsuleTabs>
|
||||
|
||||
{cur && (
|
||||
<div style={{ padding: '0 12px 12px' }}>
|
||||
<div className="card">
|
||||
<div style={{ fontWeight: 600, marginBottom: 4 }}>{cur.data.title || cur.name}</div>
|
||||
<Card className="chart-card">
|
||||
<div style={{ fontSize: 12, color: 'var(--color-muted)', marginBottom: 8 }}>
|
||||
单位 {cur.data.unit || '-'} · 来源 {cur.data.source || 'seed'}
|
||||
{labelOf(cur)} · 单位 {cur.data.unit || '-'} · 来源 {cur.data.source || 'seed'}
|
||||
</div>
|
||||
<div className="chart-wrap">
|
||||
<ResponsiveContainer width="100%" height={260}>
|
||||
@@ -67,7 +65,7 @@ export default function DataView() {
|
||||
<YAxis tick={{ fontSize: 11 }} />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Line type="monotone" dataKey="value" name={cur.data.unit || '值'} stroke="#2563eb" strokeWidth={2} dot={{ r: 3 }} />
|
||||
<Line type="monotone" dataKey="value" name={cur.data.unit || '值'} stroke="#1677ff" strokeWidth={2} dot={{ r: 3 }} />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
@@ -83,27 +81,14 @@ export default function DataView() {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function groupBy(arr, key) {
|
||||
return arr.reduce((acc, x) => {
|
||||
const k = x[key] || '其它'
|
||||
;(acc[k] = acc[k] || []).push(x)
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
function shortTitle(i) {
|
||||
if (i.data && i.data.title) return i.data.title
|
||||
return i.name
|
||||
}
|
||||
|
||||
// 把 data.rows: [{month, value}, ...] 直接返回, 服务端已经按月排好
|
||||
function buildMonthlySeries(data) {
|
||||
return (data && data.rows) || []
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user