From 1a7a77e18347eecdd1126ae0c1bf08c1aa917867 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 27 Feb 2026 08:25:11 +0000 Subject: [PATCH] feat: merge history into dashboard, remove history nav entry --- frontend/app/history/page.tsx | 90 +--------------------------------- frontend/app/page.tsx | 77 +++++++++++++++++++++++++++-- frontend/components/Navbar.tsx | 1 - 3 files changed, 74 insertions(+), 94 deletions(-) diff --git a/frontend/app/history/page.tsx b/frontend/app/history/page.tsx index 94da428..999973b 100644 --- a/frontend/app/history/page.tsx +++ b/frontend/app/history/page.tsx @@ -1,91 +1,5 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { HistoryPoint } from "@/lib/api"; -import { - LineChart, Line, XAxis, YAxis, Tooltip, Legend, - ResponsiveContainer, ReferenceLine -} from "recharts"; +import { redirect } from "next/navigation"; export default function HistoryPage() { - const [btc, setBtc] = useState([]); - const [eth, setEth] = useState([]); - 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 ( -
-
-

历史费率

-

过去7天 BTC / ETH 资金费率记录

-
- - {loading ? ( -
加载中...
- ) : ( - <> -
-

费率走势(过去7天)

- - - - `${v.toFixed(3)}%`} width={60} /> - [`${Number(v).toFixed(4)}%`]} contentStyle={{ background: "#1e293b", border: "1px solid #475569", borderRadius: 8 }} /> - - - - - - -
- -
- - - - - - - - - - {tableData.map((row, i) => ( - - - - - - ))} - -
时间BTC 费率ETH 费率
{row.time}= 0 ? "text-emerald-400" : "text-red-400"}`}> - {row.btc != null ? `${row.btc.toFixed(4)}%` : "--"} - = 0 ? "text-emerald-400" : "text-red-400"}`}> - {row.eth != null ? `${row.eth.toFixed(4)}%` : "--"} -
-
- - )} -
- ); + redirect("/"); } diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 571fae7..71de056 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -1,10 +1,14 @@ "use client"; import { useEffect, useState, useCallback } from "react"; -import { api, RatesResponse, StatsResponse, HistoryResponse } from "@/lib/api"; +import { api, RatesResponse, StatsResponse, HistoryResponse, HistoryPoint } from "@/lib/api"; import RateCard from "@/components/RateCard"; import StatsCard from "@/components/StatsCard"; import FundingChart from "@/components/FundingChart"; +import { + LineChart, Line, XAxis, YAxis, Tooltip, Legend, + ResponsiveContainer, ReferenceLine +} from "recharts"; export default function Dashboard() { const [rates, setRates] = useState(null); @@ -29,9 +33,7 @@ export default function Dashboard() { const [s, h] = await Promise.all([api.stats(), api.history()]); setStats(s); setHistory(h); - } catch { - // stats/history 失败不影响主界面 - } + } catch {} }, []); useEffect(() => { @@ -42,6 +44,21 @@ export default function Dashboard() { return () => { clearInterval(rateInterval); clearInterval(slowInterval); }; }, [fetchRates, fetchAll]); + // 历史费率表格数据 + const btcMap = new Map((history?.BTC ?? []).map((p: HistoryPoint) => [p.timestamp.slice(0, 13), p.fundingRate * 100])); + const ethMap = new Map((history?.ETH ?? []).map((p: HistoryPoint) => [p.timestamp.slice(0, 13), p.fundingRate * 100])); + const allTimes = Array.from(new Set([...btcMap.keys(), ...ethMap.keys()])).sort(); + const historyChartData = 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 (
{/* Header */} @@ -77,7 +94,7 @@ export default function Dashboard() {
)} - {/* History Chart */} + {/* 7天走势图(FundingChart) */} {history && (

过去7天资金费率走势

@@ -85,6 +102,55 @@ export default function Dashboard() {
)} + {/* 历史费率折线图 */} + {allTimes.length > 0 && ( +
+

历史费率走势(过去7天,每8小时结算)

+ + + + `${v.toFixed(3)}%`} width={60} /> + [`${Number(v).toFixed(4)}%`]} contentStyle={{ background: "#fff", border: "1px solid #e2e8f0", borderRadius: 8, fontSize: 12 }} /> + + + + + + +
+ )} + + {/* 历史费率明细表 */} + {tableData.length > 0 && ( +
+
+

历史费率明细(最近50条)

+
+ + + + + + + + + + {tableData.map((row, i) => ( + + + + + + ))} + +
时间BTC 费率ETH 费率
{row.time}= 0 ? "text-emerald-600" : "text-red-500"}`}> + {row.btc != null ? `${row.btc.toFixed(4)}%` : "--"} + = 0 ? "text-emerald-600" : "text-red-500"}`}> + {row.eth != null ? `${row.eth.toFixed(4)}%` : "--"} +
+
+ )} + {/* Strategy note */}
策略原理: @@ -93,3 +159,4 @@ export default function Dashboard() {
); } + diff --git a/frontend/components/Navbar.tsx b/frontend/components/Navbar.tsx index 7c76f9d..bdc3ec1 100644 --- a/frontend/components/Navbar.tsx +++ b/frontend/components/Navbar.tsx @@ -6,7 +6,6 @@ const navLinks = [ { href: "/", label: "仪表盘" }, { href: "/kline", label: "K线" }, { href: "/live", label: "实时" }, - { href: "/history", label: "历史" }, { href: "/signals", label: "信号" }, { href: "/about", label: "说明" }, ];