diff --git a/backend/service_implementation/service/data/futures_analysis.db b/backend/service_implementation/service/data/futures_analysis.db index b533c4c..c6450c5 100644 Binary files a/backend/service_implementation/service/data/futures_analysis.db and b/backend/service_implementation/service/data/futures_analysis.db differ diff --git a/package-lock.json b/package-lock.json index d47f332..51aced3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@reduxjs/toolkit": "^2.11.2", "antd": "^6.3.0", + "echarts": "^6.0.0", "lightweight-charts": "^5.1.0", "react": "^19.2.0", "react-dom": "^19.2.0", @@ -2211,6 +2212,22 @@ "node": ">=8" } }, + "node_modules/echarts": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-6.0.0.tgz", + "integrity": "sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "2.3.0", + "zrender": "6.0.0" + } + }, + "node_modules/echarts/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "license": "0BSD" + }, "node_modules/electron-to-chromium": { "version": "1.5.286", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", @@ -3805,6 +3822,21 @@ "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } + }, + "node_modules/zrender": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-6.0.0.tgz", + "integrity": "sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==", + "license": "BSD-3-Clause", + "dependencies": { + "tslib": "2.3.0" + } + }, + "node_modules/zrender/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "license": "0BSD" } } } diff --git a/package.json b/package.json index 39d89f7..db9e214 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "@reduxjs/toolkit": "^2.11.2", "antd": "^6.3.0", + "echarts": "^6.0.0", "lightweight-charts": "^5.1.0", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/src/pages/detail/Detail.jsx b/src/pages/detail/Detail.jsx index bcbd2be..02a1435 100644 --- a/src/pages/detail/Detail.jsx +++ b/src/pages/detail/Detail.jsx @@ -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, - }); - } - 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, - }); + { + name: 'MA10', + type: 'line', + data: ma10Data, + smooth: true, + lineStyle: { + width: 1, + color: '#faad14' + }, + showSymbol: false } - 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(); } }; });