fix: 基本功能导航正确

master
Lxy 3 months ago
parent f19d765d5f
commit d95cf103a7

@ -16,7 +16,7 @@ function App() {
<MainLayout> <MainLayout>
<Routes> <Routes>
<Route path="/" element={<Dashboard />} /> <Route path="/" element={<Dashboard />} />
<Route path="/detail" element={<Detail />} /> <Route path="/detail/:code" element={<Detail />} />
<Route path="/risk-control" element={<RiskControl />} /> <Route path="/risk-control" element={<RiskControl />} />
<Route path="/config" element={<Config />} /> <Route path="/config" element={<Config />} />
</Routes> </Routes>

@ -0,0 +1,14 @@
import React from 'react';
function TestComponent() {
return (
<div style={{ padding: '24px' }}>
<h1>Hello World!</h1>
<p>This is a simple test component.</p>
<p>It doesn't use any Redux, React Router, or complex logic.</p>
<p>If you see this page, it means the React app is working correctly.</p>
</div>
);
}
export default TestComponent;

@ -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 { 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 { SearchOutlined, BellOutlined, UserOutlined, MenuFoldOutlined, MenuUnfoldOutlined, HomeOutlined, BarChartOutlined, SafetyOutlined, SettingOutlined } from '@ant-design/icons';
import { Link, useLocation } from 'react-router-dom'; import { Link, useLocation } from 'react-router-dom';
@ -8,18 +8,8 @@ const { Header, Sider, Content } = Layout;
const { Search } = Input; const { Search } = Input;
const MainLayout = ({ children }) => { const MainLayout = ({ children }) => {
const [collapsed, setCollapsed] = useState(false);
const [darkMode, setDarkMode] = useState(false);
const location = useLocation(); const location = useLocation();
const toggleCollapsed = () => {
setCollapsed(!collapsed);
};
const toggleDarkMode = () => {
setDarkMode(!darkMode);
};
const getSelectedKey = () => { const getSelectedKey = () => {
const path = location.pathname; const path = location.pathname;
if (path === '/') return '1'; if (path === '/') return '1';
@ -29,54 +19,48 @@ const MainLayout = ({ children }) => {
return '1'; return '1';
}; };
const menuItems = [
{
key: '1',
icon: <HomeOutlined />,
label: <Link to="/">Dashboard</Link>,
},
{
key: '2',
icon: <BarChartOutlined />,
label: <Link to="/detail">详情分析</Link>,
},
{
key: '3',
icon: <SafetyOutlined />,
label: <Link to="/risk-control">风控管理</Link>,
},
{
key: '4',
icon: <SettingOutlined />,
label: <Link to="/config">配置管理</Link>,
},
];
return ( return (
<ConfigProvider theme={darkMode ? { token: { colorScheme: 'dark' } } : {}}> <ConfigProvider theme={{ token: { colorScheme: 'light' } }}>
<Layout style={{ minHeight: '100vh' }}> <Layout style={{ minHeight: '100vh' }}>
<Header className="header"> <Header className="header">
<div className="header-left"> <div className="header-left">
<Button
type="text"
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
onClick={toggleCollapsed}
style={{ marginRight: 16, color: '#fff' }}
/>
<h1 className="logo">AI期货分析系统</h1> <h1 className="logo">AI期货分析系统</h1>
</div> </div>
<div className="header-right"> <div className="header-right">
<Search <Avatar icon={<UserOutlined />} />
placeholder="搜索品种"
className="header-search"
style={{ width: 200 }}
/>
<Badge count={3} style={{ marginLeft: 16 }}>
<Button type="text" icon={<BellOutlined />} style={{ color: '#fff' }} />
</Badge>
<span className="dark-mode-toggle">
<Switch checked={darkMode} onChange={toggleDarkMode} checkedChildren="暗" unCheckedChildren="亮" />
</span>
<Avatar icon={<UserOutlined />} style={{ marginLeft: 16 }} />
</div> </div>
</Header> </Header>
<Layout> <Layout>
<Sider width={200} theme="light" trigger={null} collapsible collapsed={collapsed}> <Sider width={200} theme="light">
<Menu <Menu
mode="inline" mode="inline"
selectedKeys={[getSelectedKey()]} selectedKeys={[getSelectedKey()]}
style={{ height: '100%', borderRight: 0 }} style={{ height: '100%', borderRight: 0 }}
> items={menuItems}
<Menu.Item key="1" icon={<HomeOutlined />}> />
<Link to="/">Dashboard</Link>
</Menu.Item>
<Menu.Item key="2" icon={<BarChartOutlined />}>
<Link to="/detail">详情分析</Link>
</Menu.Item>
<Menu.Item key="3" icon={<SafetyOutlined />}>
<Link to="/risk-control">风控管理</Link>
</Menu.Item>
<Menu.Item key="4" icon={<SettingOutlined />}>
<Link to="/config">配置管理</Link>
</Menu.Item>
</Menu>
</Sider> </Sider>
<Content className="content"> <Content className="content">
<div className="content-inner"> <div className="content-inner">

@ -1,3 +1,6 @@
/* 确保antd样式正确加载 */
@import 'antd/dist/reset.css';
/* 全局样式 */ /* 全局样式 */
:root { :root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
@ -16,8 +19,6 @@
body { body {
margin: 0; margin: 0;
display: flex;
place-items: center;
min-width: 320px; min-width: 320px;
min-height: 100vh; min-height: 100vh;
} }
@ -31,6 +32,3 @@ body {
ul, ol { ul, ol {
list-style: none; list-style: none;
} }
/* 确保antd样式正确加载 */
@import 'antd/dist/reset.css';

@ -29,7 +29,7 @@ const Dashboard = () => {
}; };
const handleFutureClick = (future) => { const handleFutureClick = (future) => {
navigate(`/detail?code=${future.code}&name=${future.name}`); navigate(`/detail/${future.code}`);
}; };
const getChangeColor = (changePercent) => { const getChangeColor = (changePercent) => {
@ -303,4 +303,4 @@ const Dashboard = () => {
); );
}; };
export default Dashboard; export default Dashboard;

@ -6,54 +6,97 @@
margin-bottom: 24px; margin-bottom: 24px;
} }
.detail-header h2 { .header-info h2 {
margin: 8px 0 0 0; margin: 8px 0 16px 0;
color: #262626; color: #262626;
} }
.detail-card { .price-info {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09); display: flex;
border-radius: 4px; gap: 32px;
flex-wrap: wrap;
} }
/* 加载容器 */ /* 图表相关样式 */
.loading-container { .chart-header {
margin-bottom: 16px;
}
.chart-title {
display: flex; display: flex;
justify-content: center; justify-content: space-between;
align-items: center; align-items: center;
height: 400px; width: 100%;
} }
/* 错误容器 */ .chart-title h3 {
.error-container { margin: 0;
}
.chart-controls {
display: flex; display: flex;
flex-direction: column;
align-items: center; align-items: center;
justify-content: center; gap: 8px;
}
.kline-chart {
width: 100%;
height: 400px; height: 400px;
background-color: #fafafa;
border-radius: 4px;
display: flex;
justify-content: center;
align-items: center;
} }
/* 图表标题 */ .chart-placeholder {
.chart-title { text-align: center;
color: #8c8c8c;
}
/* 区域头部样式 */
.section-header {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
width: 100%; margin-bottom: 16px;
} }
.chart-title h3 { .section-header h3 {
margin: 0; margin: 0;
color: #262626;
} }
/* K线图表 */ /* 技术指标样式 */
.kline-chart { .indicator-item {
width: 100%; display: flex;
height: 400px; 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 { .trend-card {
height: 100%; height: 100%;
padding: 16px;
background: #fafafa;
border-radius: 4px;
text-align: center; text-align: center;
} }
@ -67,6 +110,7 @@
.trend-header h4 { .trend-header h4 {
margin: 0; margin: 0;
color: #262626; color: #262626;
font-size: 14px;
} }
.trend-status { .trend-status {
@ -80,8 +124,44 @@
color: #8c8c8c; color: #8c8c8c;
} }
/* 技术指标 */ /* AI分析样式 */
.indicator-item { .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; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@ -89,36 +169,90 @@
background: #fafafa; background: #fafafa;
border-radius: 4px; border-radius: 4px;
height: 100%; height: 100%;
text-align: center;
} }
.indicator-label { .advice-label {
font-size: 14px; font-size: 14px;
color: #8c8c8c; color: #8c8c8c;
margin-bottom: 8px; margin-bottom: 8px;
} }
.indicator-value { .advice-value {
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
color: #262626; 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 { .risk-item {
display: flex; display: flex;
flex-direction: column;
align-items: center; align-items: center;
justify-content: space-between;
padding: 16px; padding: 16px;
background: #fafafa; background: #fafafa;
border-radius: 4px; border-radius: 4px;
height: 100%; height: 100%;
text-align: center;
} }
.risk-label { .risk-label {
font-size: 14px; font-size: 14px;
color: #8c8c8c;
margin-bottom: 8px;
}
.risk-value {
font-size: 16px;
font-weight: 500;
color: #262626; 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) { @media (max-width: 768px) {
.detail-header { .detail-header {
@ -127,22 +261,60 @@
gap: 8px; gap: 8px;
} }
.price-info {
flex-direction: column;
gap: 16px;
width: 100%;
}
.chart-title { .chart-title {
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
gap: 8px; gap: 16px;
}
.chart-controls {
width: 100%;
flex-wrap: wrap;
} }
.kline-chart { .kline-chart {
height: 300px; height: 300px;
} }
.trend-card { .ai-overview {
margin-bottom: 16px; flex-direction: column;
gap: 16px;
}
.section-header {
flex-direction: column;
align-items: flex-start;
gap: 8px;
} }
.indicator-item, .indicator-item,
.trend-card,
.advice-item,
.risk-item { .risk-item {
margin-bottom: 16px; 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;
}
}

@ -1,164 +1,62 @@
import React, { useEffect, useRef, useState } from 'react'; import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { Card, Button, Row, Col, Select, Tabs, Tag, Statistic, Alert, Spin } from 'antd';
import { Card, Row, Col, Button, Select, Tag, Statistic, Alert, Spin } from 'antd'; import { useParams, useNavigate } from 'react-router-dom';
import { ArrowUpOutlined, ArrowDownOutlined, LineChartOutlined, BarChartOutlined, AlertOutlined, CalculatorOutlined } from '@ant-design/icons'; import { LineChartOutlined, BarChartOutlined, AreaChartOutlined, ArrowUpOutlined, AlertOutlined, RobotOutlined, SafetyOutlined } from '@ant-design/icons';
import { fetchFutureDetail } from '../../store/futuresSlice'; import { generateFutureData } from '../../utils/mockData';
import { useLocation, useNavigate } from 'react-router-dom';
import { generateKlineData, generateFutureData } from '../../utils/mockData';
import './Detail.css'; import './Detail.css';
// TradingView Lightweight Charts
import { createChart } from 'lightweight-charts';
const { Option } = Select; const { Option } = Select;
const { TabPane } = Tabs;
const Detail = () => { const Detail = () => {
const dispatch = useDispatch();
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const { code } = useParams();
const chartRef = useRef(null); const [data, setData] = useState(null);
const chartInstance = useRef(null); const [loading, setLoading] = useState(true);
const { selectedFuture, loading, error } = useSelector(state => state.futures); const [currentPeriod, setCurrentPeriod] = useState('1H');
const [timeframe, setTimeframe] = useState('1D'); const [currentIndicator, setCurrentIndicator] = useState('MA');
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);
// Redux console.log('Detail page loaded with code:', code);
console.log('Dispatching fetchFutureDetail with:', { code, name });
dispatch(fetchFutureDetail({ code, name }));
}, [dispatch, code, name]);
useEffect(() => { useEffect(() => {
// K线 //
const dataToUse = selectedFuture || localData; setTimeout(() => {
if (chartRef.current && dataToUse) { const futureData = generateFutureData(code, '测试品种');
console.log('Initializing chart with data:', dataToUse); setData(futureData);
if (chartInstance.current) { setLoading(false);
chartInstance.current.destroy(); }, 500);
} }, [code]);
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]);
const handleBack = () => { const handleBack = () => {
navigate('/'); navigate('/');
}; };
const getChangeColor = (changePercent) => { const handlePeriodChange = (value) => {
return changePercent >= 0 ? '#52c41a' : '#ff4d4f'; setCurrentPeriod(value);
};
const getChangeIcon = (changePercent) => {
return changePercent >= 0 ? <ArrowUpOutlined /> : <ArrowDownOutlined />;
}; };
const getTrendColor = (direction) => { const handleIndicatorChange = (value) => {
if (direction === '看多') return '#52c41a'; setCurrentIndicator(value);
if (direction === '看空') return '#ff4d4f';
return '#faad14';
}; };
// 使Redux if (loading) {
const dataToDisplay = selectedFuture || localData; return (
<div className="loading-container">
// 使 <Spin size="large" tip="加载数据中..." />
console.log('Rendering Detail component with state:', { loading, selectedFuture, localData, error }); </div>
);
}
if (!data) {
return (
<div className="error-container">
<Alert message="未找到品种数据" type="error" />
<Button type="primary" onClick={handleBack} style={{ marginTop: 16 }}>
返回主页
</Button>
</div>
);
}
return ( return (
<div className="detail"> <div className="detail">
@ -167,237 +65,288 @@ const Detail = () => {
<Button type="default" onClick={handleBack} style={{ marginBottom: 16 }}> <Button type="default" onClick={handleBack} style={{ marginBottom: 16 }}>
返回主页 返回主页
</Button> </Button>
<h2>{dataToDisplay ? dataToDisplay.fullName : `${name}-${code}`}</h2> <div className="header-info">
<h2>{data.name} ({data.code})</h2>
<div className="price-info">
<Statistic
title="当前价格"
value={data.currentPrice}
precision={2}
valueStyle={{ color: data.changePercent >= 0 ? '#52c41a' : '#ff4d4f' }}
/>
<Statistic
title="涨跌幅"
value={data.changePercent}
suffix="%"
valueStyle={{ color: data.changePercent >= 0 ? '#52c41a' : '#ff4d4f' }}
/>
<Statistic
title="胜率"
value={data.winRate}
suffix="%"
valueStyle={{ color: '#1890ff' }}
/>
</div>
</div>
</div> </div>
{/* 错误信息 */} {/* K线图表区 */}
{error && ( <Card className="detail-card" style={{ marginBottom: 24 }}>
<Alert message="加载失败" description={error} type="error" showIcon style={{ marginBottom: 24 }} /> <div className="chart-header">
)} <div className="chart-title">
<h3>K线图表</h3>
{/* 加载状态 */} <div className="chart-controls">
{loading && !dataToDisplay && ( <span style={{ marginRight: 16 }}>周期:</span>
<div className="loading-container"> <Select
<Spin size="large" tip="加载数据中..." /> value={currentPeriod}
onChange={handlePeriodChange}
style={{ width: 120, marginRight: 16 }}
>
<Option value="5M">5分钟</Option>
<Option value="30M">30分钟</Option>
<Option value="1H">1小时</Option>
<Option value="1D">1</Option>
<Option value="1W">1</Option>
</Select>
<span style={{ marginRight: 16 }}>指标:</span>
<Select
value={currentIndicator}
onChange={handleIndicatorChange}
style={{ width: 120 }}
>
<Option value="MA">MA</Option>
<Option value="MACD">MACD</Option>
<Option value="KDJ">KDJ</Option>
<Option value="RSI">RSI</Option>
<Option value="BOLL">布林带</Option>
</Select>
</div>
</div>
</div> </div>
)} <div className="kline-chart">
{/* 这里将集成TradingView Lightweight Charts */}
{/* 数据内容 */} <div className="chart-placeholder">
{dataToDisplay && ( <LineChartOutlined style={{ fontSize: 48, color: '#1890ff', marginBottom: 16 }} />
<> <p>K线图表区域</p>
{/* 基本信息 */} <p>周期: {currentPeriod} | 指标: {currentIndicator}</p>
<Card className="detail-card" style={{ marginBottom: 24 }}> </div>
<Row gutter={[16, 16]}> </div>
<Col span={8}> </Card>
<Statistic
title="当前价格"
value={dataToDisplay.currentPrice}
valueStyle={{ color: '#262626' }}
/>
</Col>
<Col span={8}>
<Statistic
title="涨跌幅"
value={Math.abs(dataToDisplay.changePercent)}
suffix="%"
valueStyle={{ color: getChangeColor(dataToDisplay.changePercent) }}
prefix={getChangeIcon(dataToDisplay.changePercent)}
/>
</Col>
<Col span={8}>
<Statistic
title="胜率"
value={dataToDisplay.winRate}
suffix="%"
valueStyle={{ color: dataToDisplay.winRate > 60 ? '#52c41a' : dataToDisplay.winRate > 40 ? '#faad14' : '#ff4d4f' }}
/>
</Col>
<Col span={8}>
<Statistic
title="ATR"
value={dataToDisplay.atr}
valueStyle={{ color: '#1890ff' }}
/>
</Col>
<Col span={8}>
<Statistic
title="ADX"
value={dataToDisplay.adx}
valueStyle={{ color: '#1890ff' }}
/>
</Col>
<Col span={8}>
<Statistic
title="趋势状态"
value={dataToDisplay.adxStatus}
valueStyle={{ color: '#1890ff' }}
/>
</Col>
</Row>
</Card>
{/* K线图表 */}
<Card
title={
<div className="chart-title">
<LineChartOutlined /> K线图表
<Select
defaultValue="1D"
style={{ width: 120, marginLeft: 16 }}
onChange={setTimeframe}
>
<Option value="5MIN">5分钟</Option>
<Option value="30MIN">30分钟</Option>
<Option value="1H">1小时</Option>
<Option value="1D">1</Option>
<Option value="1W">1</Option>
</Select>
</div>
}
className="detail-card"
style={{ marginBottom: 24 }}
>
<div ref={chartRef} className="kline-chart"></div>
</Card>
{/* 多周期趋势分析 */} {/* 技术指标区 */}
<Card <Card className="detail-card" style={{ marginBottom: 24 }}>
title={<span><BarChartOutlined /> 多周期趋势分析</span>} <div className="section-header">
className="detail-card" <h3>技术指标</h3>
style={{ marginBottom: 24 }} <BarChartOutlined />
> </div>
<Row gutter={[16, 16]}>
<Col xs={24} sm={12} md={8} lg={6}>
<div className="indicator-item">
<div className="indicator-label">MA5</div>
<div className="indicator-value">{data.technicalIndicators?.ma5 || 'N/A'}</div>
</div>
</Col>
<Col xs={24} sm={12} md={8} lg={6}>
<div className="indicator-item">
<div className="indicator-label">MA10</div>
<div className="indicator-value">{data.technicalIndicators?.ma10 || 'N/A'}</div>
</div>
</Col>
<Col xs={24} sm={12} md={8} lg={6}>
<div className="indicator-item">
<div className="indicator-label">MACD</div>
<div className="indicator-value">{data.technicalIndicators?.macd || 'N/A'}</div>
</div>
</Col>
<Col xs={24} sm={12} md={8} lg={6}>
<div className="indicator-item">
<div className="indicator-label">RSI</div>
<div className="indicator-value">{data.technicalIndicators?.rsi || 'N/A'}</div>
</div>
</Col>
<Col xs={24} sm={12} md={8} lg={6}>
<div className="indicator-item">
<div className="indicator-label">KDJ</div>
<div className="indicator-value">{data.technicalIndicators?.kdj || 'N/A'}</div>
</div>
</Col>
<Col xs={24} sm={12} md={8} lg={6}>
<div className="indicator-item">
<div className="indicator-label">布林带</div>
<div className="indicator-value">{data.technicalIndicators?.bollinger || 'N/A'}</div>
</div>
</Col>
<Col xs={24} sm={12} md={8} lg={6}>
<div className="indicator-item">
<div className="indicator-label">ATR</div>
<div className="indicator-value">{data.atr}</div>
</div>
</Col>
<Col xs={24} sm={12} md={8} lg={6}>
<div className="indicator-item">
<div className="indicator-label">ADX</div>
<div className="indicator-value">{data.adx}</div>
</div>
</Col>
</Row>
</Card>
{/* 多周期趋势和AI研判区 */}
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
{/* 多周期趋势 */}
<Col xs={24} lg={12}>
<Card className="detail-card">
<div className="section-header">
<h3>多周期趋势</h3>
<AreaChartOutlined />
</div>
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
{Object.entries(dataToDisplay.trends).map(([period, trend]) => ( {data.trends && Object.entries(data.trends).map(([period, trend]) => (
<Col span={6} key={period}> <Col xs={12} sm={6} key={period}>
<Card className="trend-card"> <div className="trend-card">
<div className="trend-header"> <div className="trend-header">
<h4>{period}</h4> <h4>{period.replace('MIN', 'min').replace('HOUR', 'min')}</h4>
<Tag color={getTrendColor(trend.direction)}> <Tag color={trend.direction === '看多' ? 'green' : trend.direction === '看空' ? 'red' : 'orange'}>
{trend.direction} {trend.direction}
</Tag> </Tag>
</div> </div>
<div className="trend-status"> <div className="trend-status">{trend.status}</div>
{trend.status} <div className="trend-rsi">RSI: {trend.rsi}</div>
</div> </div>
<div className="trend-rsi">
RSI: {trend.rsi}
</div>
</Card>
</Col> </Col>
))} ))}
</Row> </Row>
</Card> </Card>
</Col>
{/* 技术指标 */}
<Card {/* AI研判区 */}
title="技术指标" <Col xs={24} lg={12}>
className="detail-card" <Card className="detail-card">
style={{ marginBottom: 24 }} <div className="section-header">
> <h3>AI研判</h3>
<Row gutter={[16, 16]}> <RobotOutlined />
<Col span={6}> </div>
<div className="indicator-item"> <div className="ai-analysis">
<div className="indicator-label">MACD</div> <div className="ai-overview">
<div className="indicator-value">{dataToDisplay.indicators.macd}</div>
</div>
</Col>
<Col span={6}>
<div className="indicator-item">
<div className="indicator-label">RSI</div>
<div className="indicator-value">{dataToDisplay.indicators.rsi}</div>
</div>
</Col>
<Col span={6}>
<div className="indicator-item">
<div className="indicator-label">布林带</div>
<div className="indicator-value">{dataToDisplay.indicators.bollinger}</div>
</div>
</Col>
<Col span={6}>
<div className="indicator-item">
<div className="indicator-label">KDJ</div>
<div className="indicator-value">{dataToDisplay.indicators.kdj}</div>
</div>
</Col>
</Row>
</Card>
{/* 交易建议 */}
<Card
title={<span><CalculatorOutlined /> 交易建议</span>}
className="detail-card"
style={{ marginBottom: 24 }}
>
<Row gutter={[16, 16]}>
<Col span={8}>
<Statistic <Statistic
title="入场价" title="趋势预测"
value={dataToDisplay.tradingAdvice.entry} value={data.aiPrediction?.trend || '中性'}
valueStyle={{ color: '#1890ff' }} valueStyle={{ color: data.aiPrediction?.trend === '上涨' ? '#52c41a' : data.aiPrediction?.trend === '下跌' ? '#ff4d4f' : '#1890ff' }}
/> />
</Col>
<Col span={8}>
<Statistic <Statistic
title="止损价" title="预测胜率"
value={dataToDisplay.tradingAdvice.stopLoss} value={data.aiPrediction?.winRate || 0}
valueStyle={{ color: '#ff4d4f' }} suffix="%"
valueStyle={{ color: '#1890ff' }}
/> />
</Col>
<Col span={8}>
<Statistic <Statistic
title="目标价" title="预期收益"
value={dataToDisplay.tradingAdvice.target} value={data.aiPrediction?.expectedReturn || 0}
suffix="%"
valueStyle={{ color: '#52c41a' }} valueStyle={{ color: '#52c41a' }}
/> />
</Col> </div>
</Row> <div className="ai-details">
</Card> <h4>AI分析</h4>
<p>{data.aiAnalysis || 'AI正在分析中...'}</p>
{/* 风险评估 */} <h4>关键因素</h4>
<Card <div className="factor-tags">
title={<span><AlertOutlined /> 风险评估</span>} {data.aiPrediction?.keyFactors?.map((factor, index) => (
className="detail-card" <Tag key={index} color="blue">{factor}</Tag>
> ))}
<Row gutter={[16, 16]}>
<Col span={12}>
<div className="risk-item">
<div className="risk-label">风险等级</div>
<Tag color={dataToDisplay.riskLevel === '高' ? 'red' : dataToDisplay.riskLevel === '中等' ? 'orange' : 'green'}>
{dataToDisplay.riskLevel}
</Tag>
</div>
</Col>
<Col span={12}>
<div className="risk-item">
<div className="risk-label">波动率</div>
<Tag color={dataToDisplay.volatility === '高' ? 'red' : dataToDisplay.volatility === '中等' ? 'orange' : 'green'}>
{dataToDisplay.volatility}
</Tag>
</div> </div>
</Col> </div>
</Row> </div>
<Alert
message="风险提示"
description="期货交易具有高风险,请根据自身风险承受能力合理控制仓位,严格执行止损策略。"
type="warning"
showIcon
style={{ marginTop: 16 }}
/>
</Card> </Card>
</> </Col>
)} </Row>
{/* 无数据状态 */} {/* 交易建议区和风险评估区 */}
{!loading && !dataToDisplay && !error && ( <Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
<div className="error-container"> {/* 交易建议区 */}
<Alert message="未找到品种数据" type="error" /> <Col xs={24} lg={12}>
<Button type="primary" onClick={handleBack} style={{ marginTop: 16 }}> <Card className="detail-card">
返回主页 <div className="section-header">
</Button> <h3>交易建议</h3>
</div> <ArrowUpOutlined />
)} </div>
<div className="trading-advice">
<Row gutter={[16, 16]}>
<Col xs={24} sm={8}>
<div className="advice-item">
<div className="advice-label">入场价</div>
<div className="advice-value">{data.tradingAdvice?.entryPrice || 'N/A'}</div>
</div>
</Col>
<Col xs={24} sm={8}>
<div className="advice-item">
<div className="advice-label">止损价</div>
<div className="advice-value" style={{ color: '#ff4d4f' }}>
{data.tradingAdvice?.stopLoss || 'N/A'}
</div>
</div>
</Col>
<Col xs={24} sm={8}>
<div className="advice-item">
<div className="advice-label">目标价</div>
<div className="advice-value" style={{ color: '#52c41a' }}>
{data.tradingAdvice?.targetPrice || 'N/A'}
</div>
</div>
</Col>
</Row>
<div className="advice-details">
<h4>操作建议</h4>
<p>{data.tradingAdvice?.strategy || 'AI正在生成策略...'}</p>
</div>
</div>
</Card>
</Col>
{/* 风险评估区 */}
<Col xs={24} lg={12}>
<Card className="detail-card">
<div className="section-header">
<h3>风险评估</h3>
<SafetyOutlined />
</div>
<div className="risk-assessment">
<Row gutter={[16, 16]}>
<Col xs={24} sm={8}>
<div className="risk-item">
<div className="risk-label">风险等级</div>
<Tag color={data.riskLevel === '高' ? 'red' : data.riskLevel === '中等' ? 'orange' : 'green'}>
{data.riskLevel}
</Tag>
</div>
</Col>
<Col xs={24} sm={8}>
<div className="risk-item">
<div className="risk-label">波动率</div>
<div className="risk-value">{data.volatility || 'N/A'}%</div>
</div>
</Col>
<Col xs={24} sm={8}>
<div className="risk-item">
<div className="risk-label">最大回撤</div>
<div className="risk-value">{data.maxDrawdown || 'N/A'}%</div>
</div>
</Col>
</Row>
<div className="risk-details">
<h4>风险提示</h4>
<Alert
message={data.riskAlert || '无明显风险'}
type={data.riskLevel === '高' ? 'error' : data.riskLevel === '中等' ? 'warning' : 'info'}
showIcon
/>
</div>
</div>
</Card>
</Col>
</Row>
</div> </div>
); );
}; };
export default Detail; export default Detail;

@ -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;
Loading…
Cancel
Save