You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

182 lines
5.8 KiB

import { useState } from 'react';
import {
ComposedChart,
Bar,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
Line
} from 'recharts';
import type { KLineData } from '@/types';
interface KLineChartProps {
data: KLineData[];
}
// 简化的K线图 - 使用折线图模拟
export function KLineChart({ data }: KLineChartProps) {
const [timeRange, setTimeRange] = useState<'1D' | '5D' | '1M' | '3M' | '6M' | '1Y'>('1M');
// 处理数据,添加颜色标记
const chartData = data.map(item => ({
...item,
isUp: item.close >= item.open,
change: item.close - item.open,
changePercent: ((item.close - item.open) / item.open * 100).toFixed(2)
}));
const CustomTooltip = ({ active, payload }: any) => {
if (active && payload && payload.length) {
const d = payload[0].payload;
const isUp = d.close >= d.open;
return (
<div className="bg-[#141414] border border-white/10 rounded-lg p-3 shadow-xl">
<div className="text-white/60 text-sm mb-2">{d.date}</div>
<div className="space-y-1 text-sm">
<div className="flex justify-between gap-4">
<span className="text-white/60">:</span>
<span className="text-white">{d.open.toFixed(2)}</span>
</div>
<div className="flex justify-between gap-4">
<span className="text-white/60">:</span>
<span className="text-white">{d.high.toFixed(2)}</span>
</div>
<div className="flex justify-between gap-4">
<span className="text-white/60">:</span>
<span className="text-white">{d.low.toFixed(2)}</span>
</div>
<div className="flex justify-between gap-4">
<span className="text-white/60">:</span>
<span className={isUp ? 'text-red-400' : 'text-green-400'}>{d.close.toFixed(2)}</span>
</div>
<div className="flex justify-between gap-4">
<span className="text-white/60">:</span>
<span className={isUp ? 'text-red-400' : 'text-green-400'}>
{isUp ? '+' : ''}{d.changePercent}%
</span>
</div>
<div className="flex justify-between gap-4">
<span className="text-white/60">:</span>
<span className="text-white">{(d.volume / 10000).toFixed(0)}</span>
</div>
</div>
</div>
);
}
return null;
};
// 计算均线
const ma5 = chartData.map((_, index) => {
if (index < 4) return null;
const sum = chartData.slice(index - 4, index + 1).reduce((acc, d) => acc + d.close, 0);
return sum / 5;
});
const ma10 = chartData.map((_, index) => {
if (index < 9) return null;
const sum = chartData.slice(index - 9, index + 1).reduce((acc, d) => acc + d.close, 0);
return sum / 10;
});
const chartDataWithMA = chartData.map((item, index) => ({
...item,
ma5: ma5[index],
ma10: ma10[index]
}));
const timeRanges: Array<'1D' | '5D' | '1M' | '3M' | '6M' | '1Y'> = ['1D', '5D', '1M', '3M', '6M', '1Y'];
return (
<div className="w-full">
{/* 时间范围选择 */}
<div className="flex items-center gap-2 mb-4">
{timeRanges.map((range) => (
<button
key={range}
onClick={() => setTimeRange(range)}
className={`px-3 py-1.5 rounded-lg text-sm transition-colors ${
timeRange === range
? 'bg-blue-500 text-white'
: 'bg-white/5 text-white/60 hover:bg-white/10 hover:text-white'
}`}
>
{range}
</button>
))}
</div>
{/* K线图 */}
<div className="h-[350px]">
<ResponsiveContainer width="100%" height="100%">
<ComposedChart data={chartDataWithMA} margin={{ top: 10, right: 10, left: 0, bottom: 0 }}>
<CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" />
<XAxis
dataKey="date"
stroke="rgba(255,255,255,0.3)"
tick={{ fill: 'rgba(255,255,255,0.5)', fontSize: 10 }}
tickLine={false}
tickFormatter={(value) => value.slice(5)}
/>
<YAxis
domain={['auto', 'auto']}
stroke="rgba(255,255,255,0.3)"
tick={{ fill: 'rgba(255,255,255,0.5)', fontSize: 11 }}
tickLine={false}
tickFormatter={(value) => value.toFixed(2)}
orientation="right"
/>
<Tooltip content={<CustomTooltip />} />
{/* K线 - 使用Bar模拟 */}
<Bar
dataKey="close"
fill="#ef4444"
stroke="#ef4444"
barSize={8}
/>
{/* MA5 */}
<Line
type="monotone"
dataKey="ma5"
stroke="#f97316"
strokeWidth={1.5}
dot={false}
connectNulls
/>
{/* MA10 */}
<Line
type="monotone"
dataKey="ma10"
stroke="#3b82f6"
strokeWidth={1.5}
dot={false}
connectNulls
/>
</ComposedChart>
</ResponsiveContainer>
</div>
{/* 图例 */}
<div className="flex items-center justify-center gap-6 mt-4 text-xs">
<div className="flex items-center gap-2">
<div className="w-4 h-0.5 bg-orange-500"></div>
<span className="text-white/50">MA5</span>
</div>
<div className="flex items-center gap-2">
<div className="w-4 h-0.5 bg-blue-500"></div>
<span className="text-white/50">MA10</span>
</div>
</div>
</div>
);
}