92 lines
4.2 KiB
TypeScript
92 lines
4.2 KiB
TypeScript
"use client";
|
||
|
||
import { useEffect, useState } from "react";
|
||
import { HistoryPoint } from "@/lib/api";
|
||
import {
|
||
LineChart, Line, XAxis, YAxis, Tooltip, Legend,
|
||
ResponsiveContainer, ReferenceLine
|
||
} from "recharts";
|
||
|
||
export default function HistoryPage() {
|
||
const [btc, setBtc] = useState<HistoryPoint[]>([]);
|
||
const [eth, setEth] = useState<HistoryPoint[]>([]);
|
||
const [loading, setLoading] = useState(true);
|
||
|
||
useEffect(() => {
|
||
fetch("/api/history")
|
||
.then((r) => r.json())
|
||
.then((d) => { setBtc(d.BTC ?? []); setEth(d.ETH ?? []); setLoading(false); })
|
||
.catch(() => setLoading(false));
|
||
}, []);
|
||
|
||
const btcMap = new Map(btc.map((p) => [p.timestamp.slice(0, 13), p.fundingRate * 100]));
|
||
const ethMap = new Map(eth.map((p) => [p.timestamp.slice(0, 13), p.fundingRate * 100]));
|
||
const allTimes = Array.from(new Set([...btcMap.keys(), ...ethMap.keys()])).sort();
|
||
const chartData = allTimes.slice(-42).map((t) => ({
|
||
time: t.slice(5).replace("T", " "),
|
||
BTC: btcMap.get(t) ?? null,
|
||
ETH: ethMap.get(t) ?? null,
|
||
}));
|
||
|
||
const tableData = allTimes.slice().reverse().slice(0, 50).map((t) => ({
|
||
time: t.replace("T", " "),
|
||
btc: btcMap.get(t),
|
||
eth: ethMap.get(t),
|
||
}));
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
<div>
|
||
<h1 className="text-2xl font-bold text-slate-100">历史费率</h1>
|
||
<p className="text-slate-400 text-sm mt-1">过去7天 BTC / ETH 资金费率记录</p>
|
||
</div>
|
||
|
||
{loading ? (
|
||
<div className="text-slate-400">加载中...</div>
|
||
) : (
|
||
<>
|
||
<div className="rounded-xl border border-slate-700 bg-slate-800/50 p-6">
|
||
<h2 className="text-slate-200 font-semibold mb-4">费率走势(过去7天)</h2>
|
||
<ResponsiveContainer width="100%" height={260}>
|
||
<LineChart data={chartData} margin={{ top: 4, right: 8, bottom: 4, left: 8 }}>
|
||
<XAxis dataKey="time" tick={{ fill: "#64748b", fontSize: 10 }} tickLine={false} interval="preserveStartEnd" />
|
||
<YAxis tick={{ fill: "#64748b", fontSize: 10 }} tickLine={false} axisLine={false} tickFormatter={(v) => `${v.toFixed(3)}%`} width={60} />
|
||
<Tooltip formatter={(v) => [`${Number(v).toFixed(4)}%`]} contentStyle={{ background: "#1e293b", border: "1px solid #475569", borderRadius: 8 }} />
|
||
<Legend wrapperStyle={{ fontSize: 12, color: "#94a3b8" }} />
|
||
<ReferenceLine y={0} stroke="#475569" strokeDasharray="4 2" />
|
||
<Line type="monotone" dataKey="BTC" stroke="#06b6d4" strokeWidth={1.5} dot={false} connectNulls />
|
||
<Line type="monotone" dataKey="ETH" stroke="#8b5cf6" strokeWidth={1.5} dot={false} connectNulls />
|
||
</LineChart>
|
||
</ResponsiveContainer>
|
||
</div>
|
||
|
||
<div className="rounded-xl border border-slate-700 bg-slate-800/50 overflow-hidden">
|
||
<table className="w-full text-sm">
|
||
<thead>
|
||
<tr className="border-b border-slate-700 bg-slate-800">
|
||
<th className="text-left px-4 py-3 text-slate-400 font-medium">时间</th>
|
||
<th className="text-right px-4 py-3 text-cyan-400 font-medium">BTC 费率</th>
|
||
<th className="text-right px-4 py-3 text-violet-400 font-medium">ETH 费率</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{tableData.map((row, i) => (
|
||
<tr key={i} className="border-b border-slate-700/50 hover:bg-slate-700/30">
|
||
<td className="px-4 py-2 text-slate-400 font-mono text-xs">{row.time}</td>
|
||
<td className={`px-4 py-2 text-right font-mono text-xs ${row.btc == null ? "text-slate-500" : row.btc >= 0 ? "text-emerald-400" : "text-red-400"}`}>
|
||
{row.btc != null ? `${row.btc.toFixed(4)}%` : "--"}
|
||
</td>
|
||
<td className={`px-4 py-2 text-right font-mono text-xs ${row.eth == null ? "text-slate-500" : row.eth >= 0 ? "text-emerald-400" : "text-red-400"}`}>
|
||
{row.eth != null ? `${row.eth.toFixed(4)}%` : "--"}
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|