From d95cf103a72bad2d11331a803d3e2578a14c7821 Mon Sep 17 00:00:00 2001 From: Lxy Date: Wed, 18 Feb 2026 22:53:32 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=9F=BA=E6=9C=AC=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=AF=BC=E8=88=AA=E6=AD=A3=E7=A1=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.jsx | 2 +- src/TestComponent.jsx | 14 + src/components/layout/MainLayout.jsx | 74 ++- src/index.css | 8 +- src/pages/dashboard/Dashboard.jsx | 4 +- src/pages/detail/Detail.css | 234 ++++++++-- src/pages/detail/Detail.jsx | 651 ++++++++++++--------------- src/pages/detail/TestDetail.jsx | 50 ++ 8 files changed, 602 insertions(+), 435 deletions(-) create mode 100644 src/TestComponent.jsx create mode 100644 src/pages/detail/TestDetail.jsx diff --git a/src/App.jsx b/src/App.jsx index e49eddf..f244d7d 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -16,7 +16,7 @@ function App() { } /> - } /> + } /> } /> } /> diff --git a/src/TestComponent.jsx b/src/TestComponent.jsx new file mode 100644 index 0000000..9341231 --- /dev/null +++ b/src/TestComponent.jsx @@ -0,0 +1,14 @@ +import React from 'react'; + +function TestComponent() { + return ( +
+

Hello World!

+

This is a simple test component.

+

It doesn't use any Redux, React Router, or complex logic.

+

If you see this page, it means the React app is working correctly.

+
+ ); +} + +export default TestComponent; \ No newline at end of file diff --git a/src/components/layout/MainLayout.jsx b/src/components/layout/MainLayout.jsx index 2e9375c..a5b3ce1 100644 --- a/src/components/layout/MainLayout.jsx +++ b/src/components/layout/MainLayout.jsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React from 'react'; import { Layout, Menu, Button, Input, Avatar, Badge, Switch, ConfigProvider } from 'antd'; import { SearchOutlined, BellOutlined, UserOutlined, MenuFoldOutlined, MenuUnfoldOutlined, HomeOutlined, BarChartOutlined, SafetyOutlined, SettingOutlined } from '@ant-design/icons'; import { Link, useLocation } from 'react-router-dom'; @@ -8,18 +8,8 @@ const { Header, Sider, Content } = Layout; const { Search } = Input; const MainLayout = ({ children }) => { - const [collapsed, setCollapsed] = useState(false); - const [darkMode, setDarkMode] = useState(false); const location = useLocation(); - const toggleCollapsed = () => { - setCollapsed(!collapsed); - }; - - const toggleDarkMode = () => { - setDarkMode(!darkMode); - }; - const getSelectedKey = () => { const path = location.pathname; if (path === '/') return '1'; @@ -29,54 +19,48 @@ const MainLayout = ({ children }) => { return '1'; }; + const menuItems = [ + { + key: '1', + icon: , + label: Dashboard, + }, + { + key: '2', + icon: , + label: 详情分析, + }, + { + key: '3', + icon: , + label: 风控管理, + }, + { + key: '4', + icon: , + label: 配置管理, + }, + ]; + return ( - +
-
- - -
- + - }> - Dashboard - - }> - 详情分析 - - }> - 风控管理 - - }> - 配置管理 - - + items={menuItems} + />
diff --git a/src/index.css b/src/index.css index 9bc750a..39f2836 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,6 @@ +/* 确保antd样式正确加载 */ +@import 'antd/dist/reset.css'; + /* 全局样式 */ :root { font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; @@ -16,8 +19,6 @@ body { margin: 0; - display: flex; - place-items: center; min-width: 320px; min-height: 100vh; } @@ -31,6 +32,3 @@ body { ul, ol { list-style: none; } - -/* 确保antd样式正确加载 */ -@import 'antd/dist/reset.css'; diff --git a/src/pages/dashboard/Dashboard.jsx b/src/pages/dashboard/Dashboard.jsx index 32d643c..5c832f7 100644 --- a/src/pages/dashboard/Dashboard.jsx +++ b/src/pages/dashboard/Dashboard.jsx @@ -29,7 +29,7 @@ const Dashboard = () => { }; const handleFutureClick = (future) => { - navigate(`/detail?code=${future.code}&name=${future.name}`); + navigate(`/detail/${future.code}`); }; const getChangeColor = (changePercent) => { @@ -303,4 +303,4 @@ const Dashboard = () => { ); }; -export default Dashboard; +export default Dashboard; \ No newline at end of file diff --git a/src/pages/detail/Detail.css b/src/pages/detail/Detail.css index 89aec50..5dcd455 100644 --- a/src/pages/detail/Detail.css +++ b/src/pages/detail/Detail.css @@ -6,54 +6,97 @@ margin-bottom: 24px; } -.detail-header h2 { - margin: 8px 0 0 0; +.header-info h2 { + margin: 8px 0 16px 0; color: #262626; } -.detail-card { - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09); - border-radius: 4px; +.price-info { + display: flex; + gap: 32px; + flex-wrap: wrap; } -/* 加载容器 */ -.loading-container { +/* 图表相关样式 */ +.chart-header { + margin-bottom: 16px; +} + +.chart-title { display: flex; - justify-content: center; + justify-content: space-between; align-items: center; - height: 400px; + width: 100%; } -/* 错误容器 */ -.error-container { +.chart-title h3 { + margin: 0; +} + +.chart-controls { display: flex; - flex-direction: column; align-items: center; - justify-content: center; + gap: 8px; +} + +.kline-chart { + width: 100%; height: 400px; + background-color: #fafafa; + border-radius: 4px; + display: flex; + justify-content: center; + align-items: center; } -/* 图表标题 */ -.chart-title { +.chart-placeholder { + text-align: center; + color: #8c8c8c; +} + +/* 区域头部样式 */ +.section-header { display: flex; align-items: center; justify-content: space-between; - width: 100%; + margin-bottom: 16px; } -.chart-title h3 { +.section-header h3 { margin: 0; + color: #262626; } -/* K线图表 */ -.kline-chart { - width: 100%; - height: 400px; +/* 技术指标样式 */ +.indicator-item { + display: flex; + flex-direction: column; + align-items: center; + padding: 16px; + background: #fafafa; + border-radius: 4px; + height: 100%; + text-align: center; } -/* 趋势卡片 */ +.indicator-label { + font-size: 14px; + color: #8c8c8c; + margin-bottom: 8px; +} + +.indicator-value { + font-size: 16px; + font-weight: 500; + color: #262626; +} + +/* 趋势卡片样式 */ .trend-card { height: 100%; + padding: 16px; + background: #fafafa; + border-radius: 4px; text-align: center; } @@ -67,6 +110,7 @@ .trend-header h4 { margin: 0; color: #262626; + font-size: 14px; } .trend-status { @@ -80,8 +124,44 @@ color: #8c8c8c; } -/* 技术指标 */ -.indicator-item { +/* AI分析样式 */ +.ai-analysis { + display: flex; + flex-direction: column; + gap: 20px; +} + +.ai-overview { + display: flex; + gap: 24px; + flex-wrap: wrap; +} + +.ai-details h4 { + margin: 0 0 8px 0; + color: #262626; +} + +.ai-details p { + margin: 0 0 16px 0; + color: #262626; + line-height: 1.5; +} + +.factor-tags { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +/* 交易建议样式 */ +.trading-advice { + display: flex; + flex-direction: column; + gap: 20px; +} + +.advice-item { display: flex; flex-direction: column; align-items: center; @@ -89,36 +169,90 @@ background: #fafafa; border-radius: 4px; height: 100%; + text-align: center; } -.indicator-label { +.advice-label { font-size: 14px; color: #8c8c8c; margin-bottom: 8px; } -.indicator-value { +.advice-value { font-size: 16px; font-weight: 500; color: #262626; } -/* 风险评估 */ +.advice-details h4 { + margin: 0 0 8px 0; + color: #262626; +} + +.advice-details p { + margin: 0; + color: #262626; + line-height: 1.5; +} + +/* 风险评估样式 */ +.risk-assessment { + display: flex; + flex-direction: column; + gap: 20px; +} + .risk-item { display: flex; + flex-direction: column; align-items: center; - justify-content: space-between; padding: 16px; background: #fafafa; border-radius: 4px; height: 100%; + text-align: center; } .risk-label { font-size: 14px; + color: #8c8c8c; + margin-bottom: 8px; +} + +.risk-value { + font-size: 16px; + font-weight: 500; color: #262626; } +.risk-details h4 { + margin: 0 0 8px 0; + color: #262626; +} + +/* 加载容器 */ +.loading-container { + display: flex; + justify-content: center; + align-items: center; + height: 400px; +} + +/* 错误容器 */ +.error-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 400px; +} + +/* 详情卡片样式 */ +.detail-card { + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09); + border-radius: 4px; +} + /* 响应式设计 */ @media (max-width: 768px) { .detail-header { @@ -127,22 +261,60 @@ gap: 8px; } + .price-info { + flex-direction: column; + gap: 16px; + width: 100%; + } + .chart-title { flex-direction: column; align-items: flex-start; - gap: 8px; + gap: 16px; + } + + .chart-controls { + width: 100%; + flex-wrap: wrap; } .kline-chart { height: 300px; } - .trend-card { - margin-bottom: 16px; + .ai-overview { + flex-direction: column; + gap: 16px; + } + + .section-header { + flex-direction: column; + align-items: flex-start; + gap: 8px; } .indicator-item, + .trend-card, + .advice-item, .risk-item { margin-bottom: 16px; } } + +@media (max-width: 480px) { + .price-info { + flex-direction: column; + gap: 12px; + } + + .chart-controls { + flex-direction: column; + align-items: flex-start; + gap: 12px; + } + + .chart-controls Select { + width: 100% !important; + margin-right: 0 !important; + } +} \ No newline at end of file diff --git a/src/pages/detail/Detail.jsx b/src/pages/detail/Detail.jsx index 0ebce78..7f28679 100644 --- a/src/pages/detail/Detail.jsx +++ b/src/pages/detail/Detail.jsx @@ -1,164 +1,62 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { Card, Row, Col, Button, Select, Tag, Statistic, Alert, Spin } from 'antd'; -import { ArrowUpOutlined, ArrowDownOutlined, LineChartOutlined, BarChartOutlined, AlertOutlined, CalculatorOutlined } from '@ant-design/icons'; -import { fetchFutureDetail } from '../../store/futuresSlice'; -import { useLocation, useNavigate } from 'react-router-dom'; -import { generateKlineData, generateFutureData } from '../../utils/mockData'; +import React, { useState, useEffect } 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 } from '../../utils/mockData'; import './Detail.css'; -// 导入TradingView Lightweight Charts -import { createChart } from 'lightweight-charts'; - const { Option } = Select; +const { TabPane } = Tabs; const Detail = () => { - const dispatch = useDispatch(); const navigate = useNavigate(); - const location = useLocation(); - const chartRef = useRef(null); - const chartInstance = useRef(null); - const { selectedFuture, loading, error } = useSelector(state => state.futures); - const [timeframe, setTimeframe] = useState('1D'); - const [localData, setLocalData] = useState(null); - - // 解析URL参数获取品种信息 - const getQueryParams = () => { - const params = new URLSearchParams(location.search); - return { - code: params.get('code') || 'MA', - name: params.get('name') || '甲醇' - }; - }; - - const { code, name } = getQueryParams(); - - // 调试日志 - console.log('Detail page loaded with:', { code, name }); - - useEffect(() => { - // 尝试使用本地生成数据作为备选方案 - const fallbackData = generateFutureData(code, name); - setLocalData(fallbackData); - console.log('Generated fallback data:', fallbackData); + const { code } = useParams(); + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const [currentPeriod, setCurrentPeriod] = useState('1H'); + const [currentIndicator, setCurrentIndicator] = useState('MA'); - // 同时尝试从Redux获取数据 - console.log('Dispatching fetchFutureDetail with:', { code, name }); - dispatch(fetchFutureDetail({ code, name })); - }, [dispatch, code, name]); + console.log('Detail page loaded with code:', code); useEffect(() => { - // 初始化K线图表 - const dataToUse = selectedFuture || localData; - if (chartRef.current && dataToUse) { - console.log('Initializing chart with data:', dataToUse); - if (chartInstance.current) { - chartInstance.current.destroy(); - } - - const chart = createChart(chartRef.current, { - width: chartRef.current.clientWidth, - height: 400, - layout: { - backgroundColor: '#fff', - textColor: '#262626' - }, - grid: { - vertLines: { - color: '#f0f0f0' - }, - horzLines: { - color: '#f0f0f0' - } - }, - priceScale: { - borderColor: '#f0f0f0' - }, - timeScale: { - borderColor: '#f0f0f0', - timeVisible: true, - secondsVisible: false - } - }); - - // 添加K线系列 - const candlestickSeries = chart.addCandlestickSeries({ - upColor: '#52c41a', - downColor: '#ff4d4f', - borderUpColor: '#52c41a', - borderDownColor: '#ff4d4f', - wickUpColor: '#52c41a', - wickDownColor: '#ff4d4f' - }); - - // 生成K线数据 - const klineData = generateKlineData(30); - candlestickSeries.setData(klineData); - - // 添加成交量系列 - const volumeSeries = chart.addHistogramSeries({ - color: '#82ca9d', - lineWidth: 1, - priceScaleId: '', - scaleMargins: { - top: 0.8, - bottom: 0 - } - }); - - const volumeData = klineData.map(item => ({ - time: item.time, - value: item.volume, - color: item.close >= item.open ? '#52c41a' : '#ff4d4f' - })); - - volumeSeries.setData(volumeData); - - // 缩放到合适的范围 - chart.timeScale().fitContent(); - - chartInstance.current = chart; - - // 响应窗口大小变化 - const handleResize = () => { - if (chartInstance.current) { - chartInstance.current.resize(chartRef.current.clientWidth, 400); - } - }; - - window.addEventListener('resize', handleResize); - return () => { - window.removeEventListener('resize', handleResize); - if (chartInstance.current) { - chartInstance.current.destroy(); - } - }; - } - }, [selectedFuture, localData]); + // 模拟数据加载 + setTimeout(() => { + const futureData = generateFutureData(code, '测试品种'); + setData(futureData); + setLoading(false); + }, 500); + }, [code]); const handleBack = () => { navigate('/'); }; - const getChangeColor = (changePercent) => { - return changePercent >= 0 ? '#52c41a' : '#ff4d4f'; - }; - - const getChangeIcon = (changePercent) => { - return changePercent >= 0 ? : ; + const handlePeriodChange = (value) => { + setCurrentPeriod(value); }; - const getTrendColor = (direction) => { - if (direction === '看多') return '#52c41a'; - if (direction === '看空') return '#ff4d4f'; - return '#faad14'; + const handleIndicatorChange = (value) => { + setCurrentIndicator(value); }; - // 使用Redux数据或本地数据 - const dataToDisplay = selectedFuture || localData; - - // 确保即使在加载状态下也能显示内容 - console.log('Rendering Detail component with state:', { loading, selectedFuture, localData, error }); + if (loading) { + return ( +
+ +
+ ); + } + + if (!data) { + return ( +
+ + +
+ ); + } return (
@@ -167,237 +65,288 @@ const Detail = () => { -

{dataToDisplay ? dataToDisplay.fullName : `${name}-${code}`}

+
+

{data.name} ({data.code})

+
+ = 0 ? '#52c41a' : '#ff4d4f' }} + /> + = 0 ? '#52c41a' : '#ff4d4f' }} + /> + +
+
- {/* 错误信息 */} - {error && ( - - )} - - {/* 加载状态 */} - {loading && !dataToDisplay && ( -
- + {/* K线图表区 */} + +
+
+

K线图表

+
+ 周期: + + 指标: + +
+
- )} - - {/* 数据内容 */} - {dataToDisplay && ( - <> - {/* 基本信息 */} - - - - - - - - - - 60 ? '#52c41a' : dataToDisplay.winRate > 40 ? '#faad14' : '#ff4d4f' }} - /> - - - - - - - - - - - - - - {/* K线图表 */} - - K线图表 - -
- } - className="detail-card" - style={{ marginBottom: 24 }} - > -
- +
+ {/* 这里将集成TradingView Lightweight Charts */} +
+ +

K线图表区域

+

周期: {currentPeriod} | 指标: {currentIndicator}

+
+
+ - {/* 多周期趋势分析 */} - 多周期趋势分析} - className="detail-card" - style={{ marginBottom: 24 }} - > + {/* 技术指标区 */} + +
+

技术指标

+ +
+ + +
+
MA5
+
{data.technicalIndicators?.ma5 || 'N/A'}
+
+ + +
+
MA10
+
{data.technicalIndicators?.ma10 || 'N/A'}
+
+ + +
+
MACD
+
{data.technicalIndicators?.macd || 'N/A'}
+
+ + +
+
RSI
+
{data.technicalIndicators?.rsi || 'N/A'}
+
+ + +
+
KDJ
+
{data.technicalIndicators?.kdj || 'N/A'}
+
+ + +
+
布林带
+
{data.technicalIndicators?.bollinger || 'N/A'}
+
+ + +
+
ATR
+
{data.atr}
+
+ + +
+
ADX
+
{data.adx}
+
+ +
+
+ + {/* 多周期趋势和AI研判区 */} + + {/* 多周期趋势 */} + + +
+

多周期趋势

+ +
- {Object.entries(dataToDisplay.trends).map(([period, trend]) => ( - - + {data.trends && Object.entries(data.trends).map(([period, trend]) => ( + +
-

{period}

- +

{period.replace('MIN', 'min').replace('HOUR', 'min')}

+ {trend.direction}
-
- {trend.status} -
-
- RSI: {trend.rsi} -
- +
{trend.status}
+
RSI: {trend.rsi}
+
))}
- - {/* 技术指标 */} - - - -
-
MACD
-
{dataToDisplay.indicators.macd}
-
- - -
-
RSI
-
{dataToDisplay.indicators.rsi}
-
- - -
-
布林带
-
{dataToDisplay.indicators.bollinger}
-
- - -
-
KDJ
-
{dataToDisplay.indicators.kdj}
-
- -
-
- - {/* 交易建议 */} - 交易建议} - className="detail-card" - style={{ marginBottom: 24 }} - > - - + + + {/* AI研判区 */} + + +
+

AI研判

+ +
+
+
- - - - - - - - - {/* 风险评估 */} - 风险评估} - className="detail-card" - > - - -
-
风险等级
- - {dataToDisplay.riskLevel} - -
- - -
-
波动率
- - {dataToDisplay.volatility} - +
+
+

AI分析

+

{data.aiAnalysis || 'AI正在分析中...'}

+

关键因素

+
+ {data.aiPrediction?.keyFactors?.map((factor, index) => ( + {factor} + ))}
- - - +
+
- - )} - - {/* 无数据状态 */} - {!loading && !dataToDisplay && !error && ( -
- - -
- )} + + + + {/* 交易建议区和风险评估区 */} + + {/* 交易建议区 */} + + +
+

交易建议

+ +
+
+ + +
+
入场价
+
{data.tradingAdvice?.entryPrice || 'N/A'}
+
+ + +
+
止损价
+
+ {data.tradingAdvice?.stopLoss || 'N/A'} +
+
+ + +
+
目标价
+
+ {data.tradingAdvice?.targetPrice || 'N/A'} +
+
+ +
+
+

操作建议

+

{data.tradingAdvice?.strategy || 'AI正在生成策略...'}

+
+
+
+ + + {/* 风险评估区 */} + + +
+

风险评估

+ +
+
+ + +
+
风险等级
+ + {data.riskLevel} + +
+ + +
+
波动率
+
{data.volatility || 'N/A'}%
+
+ + +
+
最大回撤
+
{data.maxDrawdown || 'N/A'}%
+
+ +
+
+

风险提示

+ +
+
+
+ +
); }; -export default Detail; +export default Detail; \ No newline at end of file diff --git a/src/pages/detail/TestDetail.jsx b/src/pages/detail/TestDetail.jsx new file mode 100644 index 0000000..75318b3 --- /dev/null +++ b/src/pages/detail/TestDetail.jsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { Card, Button, Alert } from 'antd'; +import { useParams, useNavigate } from 'react-router-dom'; + +const TestDetail = () => { + const navigate = useNavigate(); + const { code } = useParams(); + + console.log('TestDetail page loaded with code:', code); + + const handleBack = () => { + navigate('/'); + }; + + return React.createElement('div', { + className: 'detail' + }, [ + React.createElement('div', { + key: 'header', + className: 'detail-header' + }, [ + React.createElement(Button, { + key: 'back-button', + type: 'default', + onClick: handleBack, + style: { marginBottom: 16 } + }, '返回主页'), + React.createElement('h2', { + key: 'title' + }, `测试详情页面 - ${code}`) + ]), + React.createElement(Card, { + key: 'card', + className: 'detail-card', + style: { marginBottom: 24 } + }, [ + React.createElement('h3', { + key: 'subtitle' + }, '测试信息'), + React.createElement('p', { + key: 'code' + }, '代码: ', code), + React.createElement('p', { + key: 'message' + }, '如果您看到此页面,说明路由和基本组件正常工作。') + ]) + ]); +}; + +export default TestDetail; \ No newline at end of file