128 lines
4.1 KiB
TypeScript

import React, { useState, useMemo } from 'react';
import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Brush } from 'recharts';
interface StockChartProps {
data: Array<{ date: string; value: number }>;
color?: string;
showBrush?: boolean;
height?: string;
showTimeRange?: boolean; // 是否显示时间范围选择
legend?: string; // 图例文本
}
export const StockChart: React.FC<StockChartProps> = ({
data,
color = '#882323',
showBrush = true,
height = '100%',
showTimeRange = true,
legend
}) => {
const [selectedRange, setSelectedRange] = useState<string>('ALL');
// 根据选择的时间范围过滤数据
const filteredData = useMemo(() => {
if (selectedRange === 'ALL') return data;
const rangeMap: Record<string, number> = {
'1Y': 12,
'3Y': 36,
'5Y': 60
};
const months = rangeMap[selectedRange];
if (!months) return data;
return data.slice(-months);
}, [data, selectedRange]);
return (
<div className="relative w-full h-full flex flex-col">
{/* 顶部栏:图例和时间范围选择 */}
<div className="flex items-center justify-between mb-2">
{/* 图例 - 左上角 */}
{legend && (
<div className="flex items-center gap-2 text-xs text-slate-500">
<span className="w-2 h-2 rounded-full" style={{ backgroundColor: color }}></span>
<span>{legend}</span>
</div>
)}
{/* 时间范围选择按钮 - 右上角 */}
{showTimeRange && (
<div className="flex items-center gap-1 ml-auto">
{['1Y', '3Y', '5Y', 'ALL'].map(range => (
<button
key={range}
onClick={() => setSelectedRange(range)}
className={`px-2 py-1 text-xs font-medium rounded transition-all ${
selectedRange === range
? 'bg-slate-900 text-white shadow-sm'
: 'bg-white text-slate-600 hover:bg-slate-100 border border-slate-200'
}`}
>
{range}
</button>
))}
</div>
)}
</div>
{/* 图表容器 */}
<div className="flex-1 min-h-0">
<ResponsiveContainer width="100%" height="100%">
<AreaChart data={filteredData} margin={{ top: 5, right: 0, left: -20, bottom: showBrush ? 35 : 10 }}>
<defs>
<linearGradient id="brushGradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#6366f1" stopOpacity={0.15} />
<stop offset="100%" stopColor="#6366f1" stopOpacity={0.05} />
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#f1f5f9" />
<XAxis
dataKey="date"
axisLine={false}
tickLine={false}
tick={{fontSize: 10, fill: '#cbd5e1'}}
dy={10}
interval="preserveStartEnd"
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{fontSize: 10, fill: '#cbd5e1'}}
domain={['dataMin - 1', 'dataMax + 1']}
/>
<Tooltip
contentStyle={{
borderRadius: '12px',
border: 'none',
boxShadow: '0 10px 15px -3px rgb(0 0 0 / 0.1)',
padding: '8px 12px'
}}
itemStyle={{ color: color, fontSize: '12px', fontWeight: 600 }}
labelStyle={{ color: '#64748b', fontSize: '10px', marginBottom: '4px' }}
/>
{showBrush && (
<Brush
dataKey="date"
height={20}
stroke="#c0c8e5"
fill="url(#brushGradient)"
travellerWidth={16}
/>
)}
<Area
type="monotone"
dataKey="value"
stroke={color}
strokeWidth={2}
fill="none"
/>
</AreaChart>
</ResponsiveContainer>
</div>
</div>
);
};