|
|
|
|
@ -1,9 +1,9 @@
|
|
|
|
|
import React, { useState, useEffect, useRef } from 'react';
|
|
|
|
|
import React, { useState, useEffect, useRef } from 'react';
|
|
|
|
|
import { Card, Button, Row, Col, Select, Tabs, Tag, Statistic, Alert, Spin } from 'antd';
|
|
|
|
|
import { useParams, useNavigate } from 'react-router-dom';
|
|
|
|
|
import { LineChartOutlined, BarChartOutlined, AreaChartOutlined, ArrowUpOutlined, AlertOutlined, RobotOutlined, SafetyOutlined } from '@ant-design/icons';
|
|
|
|
|
import { generateFutureData, generateKlineData } from '../../utils/mockData';
|
|
|
|
|
import { createChart } from 'lightweight-charts';
|
|
|
|
|
import * as echarts from 'echarts';
|
|
|
|
|
import './Detail.css';
|
|
|
|
|
|
|
|
|
|
const { Option } = Select;
|
|
|
|
|
@ -52,7 +52,7 @@ const Detail = () => {
|
|
|
|
|
|
|
|
|
|
// 销毁旧图表
|
|
|
|
|
if (chartInstance.current) {
|
|
|
|
|
chartInstance.current.destroy();
|
|
|
|
|
chartInstance.current.dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 从后端API获取K线数据
|
|
|
|
|
@ -85,93 +85,106 @@ const Detail = () => {
|
|
|
|
|
// 检查chartRef.current是否存在
|
|
|
|
|
if (!chartRef.current) return;
|
|
|
|
|
|
|
|
|
|
// 创建图表
|
|
|
|
|
const chart = createChart(chartRef.current, {
|
|
|
|
|
width: chartRef.current.clientWidth,
|
|
|
|
|
height: 400,
|
|
|
|
|
timeScale: {
|
|
|
|
|
timeVisible: true,
|
|
|
|
|
secondsVisible: false,
|
|
|
|
|
// 创建ECharts实例
|
|
|
|
|
const chart = echarts.init(chartRef.current);
|
|
|
|
|
console.log('ECharts instance created:', chart);
|
|
|
|
|
|
|
|
|
|
// 准备K线数据
|
|
|
|
|
const candlestickData = klineData.map(item => [
|
|
|
|
|
new Date(item.timestamp * 1000).getTime(), // 时间戳
|
|
|
|
|
item.open, // 开盘价
|
|
|
|
|
item.close, // 收盘价
|
|
|
|
|
item.low, // 最低价
|
|
|
|
|
item.high // 最高价
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
console.log('Candlestick data for ECharts:', candlestickData);
|
|
|
|
|
|
|
|
|
|
// 准备MA5数据
|
|
|
|
|
const ma5Data = [];
|
|
|
|
|
for (let i = 4; i < klineData.length; i++) {
|
|
|
|
|
const sum = klineData.slice(i - 4, i + 1).reduce((acc, item) => acc + item.close, 0);
|
|
|
|
|
ma5Data.push([
|
|
|
|
|
new Date(klineData[i].timestamp * 1000).getTime(),
|
|
|
|
|
sum / 5
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 准备MA10数据
|
|
|
|
|
const ma10Data = [];
|
|
|
|
|
for (let i = 9; i < klineData.length; i++) {
|
|
|
|
|
const sum = klineData.slice(i - 9, i + 1).reduce((acc, item) => acc + item.close, 0);
|
|
|
|
|
ma10Data.push([
|
|
|
|
|
new Date(klineData[i].timestamp * 1000).getTime(),
|
|
|
|
|
sum / 10
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 配置选项
|
|
|
|
|
const option = {
|
|
|
|
|
tooltip: {
|
|
|
|
|
trigger: 'axis',
|
|
|
|
|
axisPointer: {
|
|
|
|
|
type: 'cross'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
legend: {
|
|
|
|
|
data: ['K线', 'MA5', 'MA10']
|
|
|
|
|
},
|
|
|
|
|
grid: {
|
|
|
|
|
vertLines: {
|
|
|
|
|
color: 'rgba(42, 46, 57, 0.1)',
|
|
|
|
|
left: '3%',
|
|
|
|
|
right: '4%',
|
|
|
|
|
bottom: '3%',
|
|
|
|
|
containLabel: true
|
|
|
|
|
},
|
|
|
|
|
xAxis: {
|
|
|
|
|
type: 'time',
|
|
|
|
|
boundaryGap: false
|
|
|
|
|
},
|
|
|
|
|
yAxis: {
|
|
|
|
|
type: 'value',
|
|
|
|
|
scale: true
|
|
|
|
|
},
|
|
|
|
|
series: [
|
|
|
|
|
{
|
|
|
|
|
name: 'K线',
|
|
|
|
|
type: 'candlestick',
|
|
|
|
|
data: candlestickData,
|
|
|
|
|
itemStyle: {
|
|
|
|
|
color: '#52c41a',
|
|
|
|
|
color0: '#ff4d4f',
|
|
|
|
|
borderColor: '#52c41a',
|
|
|
|
|
borderColor0: '#ff4d4f'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
horzLines: {
|
|
|
|
|
color: 'rgba(42, 46, 57, 0.1)',
|
|
|
|
|
{
|
|
|
|
|
name: 'MA5',
|
|
|
|
|
type: 'line',
|
|
|
|
|
data: ma5Data,
|
|
|
|
|
smooth: true,
|
|
|
|
|
lineStyle: {
|
|
|
|
|
width: 1,
|
|
|
|
|
color: '#1890ff'
|
|
|
|
|
},
|
|
|
|
|
showSymbol: false
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 检查chart对象是否正确创建
|
|
|
|
|
console.log('Chart object:', chart);
|
|
|
|
|
console.log('Chart methods:', Object.keys(chart));
|
|
|
|
|
|
|
|
|
|
// 添加K线系列
|
|
|
|
|
try {
|
|
|
|
|
const candlestickSeries = chart.addCandlestickSeries({
|
|
|
|
|
upColor: '#52c41a',
|
|
|
|
|
downColor: '#ff4d4f',
|
|
|
|
|
borderVisible: false,
|
|
|
|
|
wickUpColor: '#52c41a',
|
|
|
|
|
wickDownColor: '#ff4d4f',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 设置K线数据
|
|
|
|
|
candlestickSeries.setData(klineData.map(item => ({
|
|
|
|
|
time: new Date(item.timestamp * 1000).toISOString().split('T')[0],
|
|
|
|
|
open: item.open,
|
|
|
|
|
high: item.high,
|
|
|
|
|
low: item.low,
|
|
|
|
|
close: item.close,
|
|
|
|
|
})));
|
|
|
|
|
|
|
|
|
|
// 根据当前指标添加相应的技术指标
|
|
|
|
|
if (currentIndicator === 'MA') {
|
|
|
|
|
// 添加MA5
|
|
|
|
|
const ma5Series = chart.addLineSeries({
|
|
|
|
|
color: '#1890ff',
|
|
|
|
|
lineWidth: 1,
|
|
|
|
|
});
|
|
|
|
|
// 计算MA5数据
|
|
|
|
|
const ma5Data = [];
|
|
|
|
|
for (let i = 4; i < klineData.length; i++) {
|
|
|
|
|
const sum = klineData.slice(i - 4, i + 1).reduce((acc, item) => acc + item.close, 0);
|
|
|
|
|
ma5Data.push({
|
|
|
|
|
time: new Date(klineData[i].timestamp * 1000).toISOString().split('T')[0],
|
|
|
|
|
value: sum / 5,
|
|
|
|
|
});
|
|
|
|
|
{
|
|
|
|
|
name: 'MA10',
|
|
|
|
|
type: 'line',
|
|
|
|
|
data: ma10Data,
|
|
|
|
|
smooth: true,
|
|
|
|
|
lineStyle: {
|
|
|
|
|
width: 1,
|
|
|
|
|
color: '#faad14'
|
|
|
|
|
},
|
|
|
|
|
showSymbol: false
|
|
|
|
|
}
|
|
|
|
|
ma5Series.setData(ma5Data);
|
|
|
|
|
|
|
|
|
|
// 添加MA10
|
|
|
|
|
const ma10Series = chart.addLineSeries({
|
|
|
|
|
color: '#faad14',
|
|
|
|
|
lineWidth: 1,
|
|
|
|
|
});
|
|
|
|
|
// 计算MA10数据
|
|
|
|
|
const ma10Data = [];
|
|
|
|
|
for (let i = 9; i < klineData.length; i++) {
|
|
|
|
|
const sum = klineData.slice(i - 9, i + 1).reduce((acc, item) => acc + item.close, 0);
|
|
|
|
|
ma10Data.push({
|
|
|
|
|
time: new Date(klineData[i].timestamp * 1000).toISOString().split('T')[0],
|
|
|
|
|
value: sum / 10,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
ma10Series.setData(ma10Data);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error creating chart series:', error);
|
|
|
|
|
// 失败时使用模拟数据创建简单的线图
|
|
|
|
|
const lineSeries = chart.addLineSeries({
|
|
|
|
|
color: '#1890ff',
|
|
|
|
|
lineWidth: 1,
|
|
|
|
|
});
|
|
|
|
|
lineSeries.setData(klineData.map(item => ({
|
|
|
|
|
time: new Date(item.timestamp * 1000).toISOString().split('T')[0],
|
|
|
|
|
value: item.close,
|
|
|
|
|
})));
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 设置配置
|
|
|
|
|
chart.setOption(option);
|
|
|
|
|
console.log('ECharts option set successfully');
|
|
|
|
|
|
|
|
|
|
// 保存图表实例
|
|
|
|
|
chartInstance.current = chart;
|
|
|
|
|
@ -179,7 +192,7 @@ const Detail = () => {
|
|
|
|
|
// 处理窗口大小变化
|
|
|
|
|
const handleResize = () => {
|
|
|
|
|
if (chartInstance.current) {
|
|
|
|
|
chartInstance.current.resize(chartRef.current.clientWidth, 400);
|
|
|
|
|
chartInstance.current.resize();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@ -189,7 +202,7 @@ const Detail = () => {
|
|
|
|
|
return () => {
|
|
|
|
|
window.removeEventListener('resize', handleResize);
|
|
|
|
|
if (chartInstance.current) {
|
|
|
|
|
chartInstance.current.destroy();
|
|
|
|
|
chartInstance.current.dispose();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|