226 lines
6.5 KiB
TypeScript
226 lines
6.5 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import { useState, useEffect, useRef } from "react";
|
||
|
|
import { useApp } from "@/contexts/AppContext";
|
||
|
|
import * as echarts from "echarts";
|
||
|
|
|
||
|
|
export default function APYHistoryCard() {
|
||
|
|
const { t } = useApp();
|
||
|
|
const [activeTab, setActiveTab] = useState<"apy" | "price">("apy");
|
||
|
|
const chartRef = useRef<HTMLDivElement>(null);
|
||
|
|
const chartInstance = useRef<echarts.ECharts | null>(null);
|
||
|
|
|
||
|
|
// 生成从#FBEADE到#C65122的9个渐变颜色
|
||
|
|
const getColorGradient = () => {
|
||
|
|
const colors = [
|
||
|
|
"#FBEADE",
|
||
|
|
"#F5D4BE",
|
||
|
|
"#EFBF9E",
|
||
|
|
"#E9AA7E",
|
||
|
|
"#E3955E",
|
||
|
|
"#DD804E",
|
||
|
|
"#D76B3E",
|
||
|
|
"#D1562E",
|
||
|
|
"#C65122",
|
||
|
|
];
|
||
|
|
return colors;
|
||
|
|
};
|
||
|
|
|
||
|
|
const chartData = [4.2, 5.1, 5.8, 6.3, 7.1, 8.2, 9.5, 10.8, 12.4];
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (chartRef.current) {
|
||
|
|
chartInstance.current = echarts.init(chartRef.current);
|
||
|
|
updateChart();
|
||
|
|
}
|
||
|
|
|
||
|
|
const handleResize = () => {
|
||
|
|
chartInstance.current?.resize();
|
||
|
|
};
|
||
|
|
window.addEventListener("resize", handleResize);
|
||
|
|
|
||
|
|
return () => {
|
||
|
|
window.removeEventListener("resize", handleResize);
|
||
|
|
chartInstance.current?.dispose();
|
||
|
|
};
|
||
|
|
}, [activeTab]);
|
||
|
|
|
||
|
|
const updateChart = () => {
|
||
|
|
if (!chartInstance.current) return;
|
||
|
|
|
||
|
|
const colors = getColorGradient();
|
||
|
|
|
||
|
|
// 模拟价格数据
|
||
|
|
const priceData = [1.12, 1.15, 1.18, 1.14, 1.21, 1.19, 1.25, 1.28, 1.32];
|
||
|
|
|
||
|
|
const option: echarts.EChartsOption = {
|
||
|
|
grid: {
|
||
|
|
left: 0,
|
||
|
|
right: 0,
|
||
|
|
top: 10,
|
||
|
|
bottom: 0,
|
||
|
|
},
|
||
|
|
tooltip: {
|
||
|
|
trigger: "axis",
|
||
|
|
show: true,
|
||
|
|
confine: true,
|
||
|
|
backgroundColor: "rgba(17, 24, 39, 0.9)",
|
||
|
|
borderColor: "#374151",
|
||
|
|
textStyle: {
|
||
|
|
color: "#f9fafb",
|
||
|
|
fontSize: 12,
|
||
|
|
fontWeight: 500,
|
||
|
|
},
|
||
|
|
formatter: function(params: any) {
|
||
|
|
const data = params[0];
|
||
|
|
const suffix = activeTab === "apy" ? "%" : " USDC";
|
||
|
|
return `<div style="padding: 4px 8px;">
|
||
|
|
<span style="color: #9ca3af; font-size: 11px;">Day ${data.dataIndex + 1}</span><br/>
|
||
|
|
<span style="color: #10b981; font-weight: 600; font-size: 14px;">${data.value}${suffix}</span>
|
||
|
|
</div>`;
|
||
|
|
},
|
||
|
|
},
|
||
|
|
xAxis: {
|
||
|
|
type: "category",
|
||
|
|
data: chartData.map((_, i) => i + 1),
|
||
|
|
axisLine: {
|
||
|
|
show: false,
|
||
|
|
},
|
||
|
|
axisTick: {
|
||
|
|
show: false,
|
||
|
|
},
|
||
|
|
axisLabel: {
|
||
|
|
show: false,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
yAxis: {
|
||
|
|
type: "value",
|
||
|
|
axisLine: {
|
||
|
|
show: false,
|
||
|
|
},
|
||
|
|
axisTick: {
|
||
|
|
show: false,
|
||
|
|
},
|
||
|
|
axisLabel: {
|
||
|
|
show: false,
|
||
|
|
},
|
||
|
|
splitLine: {
|
||
|
|
show: false,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
series: [
|
||
|
|
activeTab === "apy"
|
||
|
|
? {
|
||
|
|
data: chartData.map((value, index) => ({
|
||
|
|
value,
|
||
|
|
itemStyle: {
|
||
|
|
color: colors[index],
|
||
|
|
borderRadius: [2, 2, 0, 0],
|
||
|
|
},
|
||
|
|
})),
|
||
|
|
type: "bar",
|
||
|
|
barWidth: "50%",
|
||
|
|
}
|
||
|
|
: {
|
||
|
|
data: priceData,
|
||
|
|
type: "line",
|
||
|
|
smooth: true,
|
||
|
|
symbol: "circle",
|
||
|
|
symbolSize: 6,
|
||
|
|
lineStyle: {
|
||
|
|
color: "#10b981",
|
||
|
|
width: 2,
|
||
|
|
},
|
||
|
|
itemStyle: {
|
||
|
|
color: "#10b981",
|
||
|
|
},
|
||
|
|
areaStyle: {
|
||
|
|
color: {
|
||
|
|
type: "linear",
|
||
|
|
x: 0,
|
||
|
|
y: 0,
|
||
|
|
x2: 0,
|
||
|
|
y2: 1,
|
||
|
|
colorStops: [
|
||
|
|
{ offset: 0, color: "rgba(16, 185, 129, 0.3)" },
|
||
|
|
{ offset: 1, color: "rgba(16, 185, 129, 0)" },
|
||
|
|
],
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
],
|
||
|
|
};
|
||
|
|
|
||
|
|
chartInstance.current.setOption(option);
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="bg-bg-surface dark:bg-gray-800 rounded-3xl border border-border-gray dark:border-gray-700 p-6 flex flex-col gap-6">
|
||
|
|
{/* Tabs */}
|
||
|
|
<div className="flex gap-6 border-b border-border-gray dark:border-gray-700">
|
||
|
|
<button
|
||
|
|
onClick={() => setActiveTab("apy")}
|
||
|
|
className={`pb-3 px-1 text-body-small font-bold transition-colors ${
|
||
|
|
activeTab === "apy"
|
||
|
|
? "text-text-primary dark:text-white border-b-2 border-text-primary dark:border-white -mb-[1px]"
|
||
|
|
: "text-text-tertiary dark:text-gray-400"
|
||
|
|
}`}
|
||
|
|
>
|
||
|
|
{t("apy.apyHistory")}
|
||
|
|
</button>
|
||
|
|
<button
|
||
|
|
onClick={() => setActiveTab("price")}
|
||
|
|
className={`pb-3 px-1 text-body-small font-bold transition-colors ${
|
||
|
|
activeTab === "price"
|
||
|
|
? "text-text-primary dark:text-white border-b-2 border-text-primary dark:border-white -mb-[1px]"
|
||
|
|
: "text-text-tertiary dark:text-gray-400"
|
||
|
|
}`}
|
||
|
|
>
|
||
|
|
{t("apy.priceHistory")}
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Chart Area */}
|
||
|
|
<div className="flex flex-col gap-4">
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<span className="text-caption-tiny font-medium text-text-tertiary dark:text-gray-400">
|
||
|
|
{t("apy.lastDays")}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* ECharts Chart */}
|
||
|
|
<div
|
||
|
|
ref={chartRef}
|
||
|
|
className="w-full"
|
||
|
|
style={{
|
||
|
|
height: "140px",
|
||
|
|
background: activeTab === "price"
|
||
|
|
? "linear-gradient(0deg, rgba(16, 185, 129, 0.1) 0%, transparent 100%)"
|
||
|
|
: undefined,
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
|
||
|
|
{/* Stats */}
|
||
|
|
<div className="flex flex-col gap-2">
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<span className="text-caption-tiny font-medium text-text-tertiary dark:text-gray-400">
|
||
|
|
{t("apy.highest")}
|
||
|
|
</span>
|
||
|
|
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||
|
|
{activeTab === "apy" ? "24.8%" : "$1.32"}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<span className="text-caption-tiny font-medium text-text-tertiary dark:text-gray-400">
|
||
|
|
{t("apy.lowest")}
|
||
|
|
</span>
|
||
|
|
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||
|
|
{activeTab === "apy" ? "18.2%" : "$1.12"}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|