|
|
|
|
|
import React, { useState, useEffect } from 'react';
|
|
|
|
|
|
import { Card, Row, Col, Button, Input, Select, Table, Badge, Statistic, Typography, Spin } from 'antd';
|
|
|
|
|
|
import { ReloadOutlined, FilterOutlined, EyeOutlined, EyeInvisibleOutlined, ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
|
|
|
|
|
|
import { allFuturesData, aiAnalysis, marketHotspots, riskAlerts } from '../../utils/mockData';
|
|
|
|
|
|
import useTheme from '../../hooks/useTheme';
|
|
|
|
|
|
|
|
|
|
|
|
const { Title, Text } = Typography;
|
|
|
|
|
|
const { Search } = Input;
|
|
|
|
|
|
const { Option } = Select;
|
|
|
|
|
|
const { Column } = Table;
|
|
|
|
|
|
|
|
|
|
|
|
const Dashboard = () => {
|
|
|
|
|
|
const [futuresData, setFuturesData] = useState(allFuturesData);
|
|
|
|
|
|
const [filteredData, setFilteredData] = useState(allFuturesData);
|
|
|
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
|
|
const [viewMode, setViewMode] = useState('card'); // card or list
|
|
|
|
|
|
const [filterType, setFilterType] = useState('');
|
|
|
|
|
|
const [filterKeyword, setFilterKeyword] = useState('');
|
|
|
|
|
|
|
|
|
|
|
|
// 模拟数据刷新
|
|
|
|
|
|
const handleRefresh = () => {
|
|
|
|
|
|
setLoading(true);
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
const refreshedData = allFuturesData.map(item => ({
|
|
|
|
|
|
...item,
|
|
|
|
|
|
currentPrice: (parseFloat(item.currentPrice) * (1 + (Math.random() * 0.02 - 0.01))).toFixed(2),
|
|
|
|
|
|
changePercent: (parseFloat(item.changePercent) + (Math.random() * 2 - 1)).toFixed(2)
|
|
|
|
|
|
}));
|
|
|
|
|
|
setFuturesData(refreshedData);
|
|
|
|
|
|
setFilteredData(refreshedData);
|
|
|
|
|
|
setLoading(false);
|
|
|
|
|
|
}, 1000);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 过滤数据
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
let result = futuresData;
|
|
|
|
|
|
|
|
|
|
|
|
if (filterKeyword) {
|
|
|
|
|
|
const keyword = filterKeyword.toLowerCase();
|
|
|
|
|
|
result = result.filter(item =>
|
|
|
|
|
|
item.code.toLowerCase().includes(keyword) ||
|
|
|
|
|
|
item.name.toLowerCase().includes(keyword)
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (filterType) {
|
|
|
|
|
|
result = result.filter(item => item.type === filterType);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setFilteredData(result);
|
|
|
|
|
|
}, [futuresData, filterKeyword, filterType]);
|
|
|
|
|
|
|
|
|
|
|
|
// 品种类型选项
|
|
|
|
|
|
const typeOptions = Array.from(new Set(allFuturesData.map(item => item.type))).map(type => (
|
|
|
|
|
|
<Option key={type} value={type}>{type}</Option>
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
// 渲染品种卡片
|
|
|
|
|
|
const renderCard = (item) => (
|
|
|
|
|
|
<Card
|
|
|
|
|
|
key={item.code}
|
|
|
|
|
|
style={{ marginBottom: 16, transition: 'all 0.3s', cursor: 'pointer' }}
|
|
|
|
|
|
hoverable
|
|
|
|
|
|
onClick={() => console.log('查看详情:', item.code)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Row gutter={[16, 16]}>
|
|
|
|
|
|
<Col span={24}>
|
|
|
|
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
|
|
|
|
|
|
<div style={{ fontWeight: 'bold', fontSize: '16px' }}>{item.name} ({item.code})</div>
|
|
|
|
|
|
<Badge
|
|
|
|
|
|
status={item.changePercent > 0 ? 'success' : 'error'}
|
|
|
|
|
|
text={`${item.changePercent > 0 ? '+' : ''}${item.changePercent}%`}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
|
|
|
|
|
|
<Col span={24}>
|
|
|
|
|
|
<Statistic
|
|
|
|
|
|
title="当前价格"
|
|
|
|
|
|
value={item.currentPrice}
|
|
|
|
|
|
valueStyle={{ color: item.changePercent > 0 ? '#52c41a' : '#ff4d4f' }}
|
|
|
|
|
|
suffix="元"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
|
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
|
<div style={{ fontSize: '12px', color: '#8c8c8c' }}>胜率: {item.winRate}%</div>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
|
<div style={{ fontSize: '12px', color: '#8c8c8c' }}>ATR: {item.atr}</div>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
|
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
|
<div style={{ fontSize: '12px', color: '#8c8c8c' }}>ADX: {item.adx}</div>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
|
<div style={{ fontSize: '12px', color: '#8c8c8c' }}>趋势: {item.adxStatus}</div>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
</Row>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div>
|
|
|
|
|
|
{/* 页面头部 */}
|
|
|
|
|
|
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
|
|
|
|
|
|
<Col flex="auto">
|
|
|
|
|
|
<Title level={3}>欢迎使用AI期货分析系统</Title>
|
|
|
|
|
|
<Text>当前市场整体处于{aiAnalysis.overallView.split(',')[0]}</Text>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
<Col flex="none">
|
|
|
|
|
|
<div style={{ display: 'flex', gap: 12 }}>
|
|
|
|
|
|
<Button onClick={handleRefresh} icon={<ReloadOutlined />} loading={loading}>
|
|
|
|
|
|
刷新数据
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
onClick={() => setViewMode(viewMode === 'card' ? 'list' : 'card')}
|
|
|
|
|
|
icon={viewMode === 'card' ? <EyeInvisibleOutlined /> : <EyeOutlined />}
|
|
|
|
|
|
>
|
|
|
|
|
|
{viewMode === 'card' ? '列表视图' : '卡片视图'}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
</Row>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 筛选栏 */}
|
|
|
|
|
|
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
|
|
|
|
|
|
<Col span={8}>
|
|
|
|
|
|
<Search
|
|
|
|
|
|
placeholder="搜索品种代码或名称"
|
|
|
|
|
|
value={filterKeyword}
|
|
|
|
|
|
onChange={(e) => setFilterKeyword(e.target.value)}
|
|
|
|
|
|
style={{ width: '100%' }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
<Col span={8}>
|
|
|
|
|
|
<Select
|
|
|
|
|
|
placeholder="按品种类型筛选"
|
|
|
|
|
|
style={{ width: '100%' }}
|
|
|
|
|
|
value={filterType}
|
|
|
|
|
|
onChange={setFilterType}
|
|
|
|
|
|
allowClear
|
|
|
|
|
|
>
|
|
|
|
|
|
{typeOptions}
|
|
|
|
|
|
</Select>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
</Row>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 品种概览区 */}
|
|
|
|
|
|
<Card title="品种概览" style={{ marginBottom: 24 }}>
|
|
|
|
|
|
<Spin spinning={loading}>
|
|
|
|
|
|
{viewMode === 'card' ? (
|
|
|
|
|
|
<Row gutter={[16, 16]}>
|
|
|
|
|
|
{filteredData.map(item => (
|
|
|
|
|
|
<Col key={item.code} xs={24} sm={12} md={8} lg={6}>
|
|
|
|
|
|
{renderCard(item)}
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</Row>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<Table dataSource={filteredData} rowKey="code" pagination={{ pageSize: 10 }}>
|
|
|
|
|
|
<Column title="品种" dataIndex="name" key="name" render={(text, record) => `${text} (${record.code})`} />
|
|
|
|
|
|
<Column title="当前价格" dataIndex="currentPrice" key="currentPrice" />
|
|
|
|
|
|
<Column
|
|
|
|
|
|
title="涨跌幅"
|
|
|
|
|
|
dataIndex="changePercent"
|
|
|
|
|
|
key="changePercent"
|
|
|
|
|
|
render={(text) => (
|
|
|
|
|
|
<span style={{ color: text > 0 ? '#52c41a' : '#ff4d4f' }}>
|
|
|
|
|
|
{text > 0 ? '+' : ''}{text}%
|
|
|
|
|
|
</span>
|
|
|
|
|
|
)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Column title="胜率" dataIndex="winRate" key="winRate" render={(text) => `${text}%`} />
|
|
|
|
|
|
<Column title="ATR" dataIndex="atr" key="atr" />
|
|
|
|
|
|
<Column title="ADX" dataIndex="adx" key="adx" />
|
|
|
|
|
|
<Column title="趋势状态" dataIndex="adxStatus" key="adxStatus" />
|
|
|
|
|
|
</Table>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Spin>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 市场热点和AI研判区 */}
|
|
|
|
|
|
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
|
|
|
|
|
|
<Col xs={24} lg={12}>
|
|
|
|
|
|
<Card title="AI研判">
|
|
|
|
|
|
<div style={{ marginBottom: 16 }}>
|
|
|
|
|
|
<Text strong>市场整体观点:</Text>
|
|
|
|
|
|
<Text>{aiAnalysis.overallView}</Text>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style={{ marginBottom: 16 }}>
|
|
|
|
|
|
<Text strong>投资建议:</Text>
|
|
|
|
|
|
<ul style={{ marginLeft: 20, marginTop: 8 }}>
|
|
|
|
|
|
{aiAnalysis.recommendations.map((item, index) => (
|
|
|
|
|
|
<li key={index} style={{ marginBottom: 4 }}>{item}</li>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<Text strong>置信度:</Text>
|
|
|
|
|
|
<Badge status="success" text={`${aiAnalysis.confidence}%`} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
<Col xs={24} lg={12}>
|
|
|
|
|
|
<Card title="风险预警">
|
|
|
|
|
|
<Row gutter={[16, 16]}>
|
|
|
|
|
|
{riskAlerts.map(alert => (
|
|
|
|
|
|
<Col key={alert.id} span={24}>
|
|
|
|
|
|
<Card
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
style={{
|
|
|
|
|
|
borderLeft: `4px solid ${alert.level === '高' ? '#ff4d4f' : alert.level === '中等' ? '#faad14' : '#52c41a'}`
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
|
|
|
|
|
|
<Text strong>{alert.title}</Text>
|
|
|
|
|
|
<Badge
|
|
|
|
|
|
status={alert.level === '高' ? 'error' : alert.level === '中等' ? 'warning' : 'success'}
|
|
|
|
|
|
text={alert.level}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<Text type="secondary">{alert.message}</Text>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</Row>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
</Row>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export default Dashboard;
|