community增加事件详情
This commit is contained in:
44
app.py
44
app.py
@@ -11139,20 +11139,53 @@ def get_events_effectiveness_stats():
|
|||||||
|
|
||||||
# 计算汇总统计
|
# 计算汇总统计
|
||||||
total_events = len(events_query)
|
total_events = len(events_query)
|
||||||
|
event_ids = [e.id for e in events_query]
|
||||||
avg_chg_list = [e.related_avg_chg for e in events_query if e.related_avg_chg is not None]
|
avg_chg_list = [e.related_avg_chg for e in events_query if e.related_avg_chg is not None]
|
||||||
max_chg_list = [e.related_max_chg for e in events_query if e.related_max_chg is not None]
|
max_chg_list = [e.related_max_chg for e in events_query if e.related_max_chg is not None]
|
||||||
invest_scores = [e.invest_score for e in events_query if e.invest_score is not None]
|
|
||||||
surprise_scores = [e.expectation_surprise_score for e in events_query if e.expectation_surprise_score is not None]
|
|
||||||
|
|
||||||
positive_count = sum(1 for chg in avg_chg_list if chg > 0)
|
positive_count = sum(1 for chg in avg_chg_list if chg > 0)
|
||||||
|
|
||||||
|
# 查询关联股票数据
|
||||||
|
stock_stats = []
|
||||||
|
total_stocks = 0
|
||||||
|
if event_ids:
|
||||||
|
# 查询所有关联股票
|
||||||
|
related_stocks = db.session.query(EventStock).filter(
|
||||||
|
EventStock.event_id.in_(event_ids)
|
||||||
|
).all()
|
||||||
|
|
||||||
|
# 统计股票数量(去重)
|
||||||
|
unique_stocks = {}
|
||||||
|
for rs in related_stocks:
|
||||||
|
stock_key = rs.stock_code
|
||||||
|
if stock_key not in unique_stocks:
|
||||||
|
unique_stocks[stock_key] = {
|
||||||
|
'stockCode': rs.stock_code,
|
||||||
|
'stockName': rs.stock_name,
|
||||||
|
'maxChg': rs.chg_pct or 0,
|
||||||
|
'eventId': rs.event_id,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
# 保留最大涨幅的记录
|
||||||
|
if (rs.chg_pct or 0) > unique_stocks[stock_key]['maxChg']:
|
||||||
|
unique_stocks[stock_key]['maxChg'] = rs.chg_pct or 0
|
||||||
|
unique_stocks[stock_key]['eventId'] = rs.event_id
|
||||||
|
|
||||||
|
total_stocks = len(unique_stocks)
|
||||||
|
|
||||||
|
# 按涨幅排序,取 TOP10
|
||||||
|
stock_stats = sorted(
|
||||||
|
unique_stocks.values(),
|
||||||
|
key=lambda x: x['maxChg'],
|
||||||
|
reverse=True
|
||||||
|
)[:10]
|
||||||
|
|
||||||
summary = {
|
summary = {
|
||||||
'totalEvents': total_events,
|
'totalEvents': total_events,
|
||||||
|
'totalStocks': total_stocks,
|
||||||
'avgChg': round(sum(avg_chg_list) / len(avg_chg_list), 2) if avg_chg_list else 0,
|
'avgChg': round(sum(avg_chg_list) / len(avg_chg_list), 2) if avg_chg_list else 0,
|
||||||
'maxChg': round(max(max_chg_list), 2) if max_chg_list else 0,
|
'maxChg': round(max(max_chg_list), 2) if max_chg_list else 0,
|
||||||
'positiveRate': round(positive_count / len(avg_chg_list) * 100, 1) if avg_chg_list else 0,
|
'positiveRate': round(positive_count / len(avg_chg_list) * 100, 1) if avg_chg_list else 0,
|
||||||
'avgInvestScore': round(sum(invest_scores) / len(invest_scores)) if invest_scores else 0,
|
|
||||||
'avgSurpriseScore': round(sum(surprise_scores) / len(surprise_scores)) if surprise_scores else 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# 按日期分组统计
|
# 按日期分组统计
|
||||||
@@ -11225,7 +11258,8 @@ def get_events_effectiveness_stats():
|
|||||||
'currentDate': current_trading_day.strftime('%Y-%m-%d') if hasattr(current_trading_day, 'strftime') else str(current_trading_day),
|
'currentDate': current_trading_day.strftime('%Y-%m-%d') if hasattr(current_trading_day, 'strftime') else str(current_trading_day),
|
||||||
'summary': summary,
|
'summary': summary,
|
||||||
'dailyStats': daily_stats,
|
'dailyStats': daily_stats,
|
||||||
'topPerformers': top_performers_list
|
'topPerformers': top_performers_list,
|
||||||
|
'topStocks': stock_stats
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -12,25 +12,25 @@ import {
|
|||||||
Center,
|
Center,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Badge,
|
Badge,
|
||||||
Progress,
|
Tabs,
|
||||||
|
TabList,
|
||||||
|
Tab,
|
||||||
|
TabPanels,
|
||||||
|
TabPanel,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
FireOutlined,
|
FireOutlined,
|
||||||
RiseOutlined,
|
RiseOutlined,
|
||||||
CheckCircleOutlined,
|
|
||||||
ThunderboltOutlined,
|
ThunderboltOutlined,
|
||||||
TrophyOutlined,
|
TrophyOutlined,
|
||||||
StarOutlined,
|
StockOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { getApiBase } from '@utils/apiConfig';
|
import { getApiBase } from '@utils/apiConfig';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成事件详情页 URL
|
* 生成事件详情页 URL
|
||||||
* @param {number} eventId - 事件ID
|
|
||||||
* @returns {string} 事件详情页 URL
|
|
||||||
*/
|
*/
|
||||||
const getEventDetailUrl = (eventId) => {
|
const getEventDetailUrl = (eventId) => {
|
||||||
// 使用 base64 编码 ID,格式:ev-{id} -> base64
|
|
||||||
const encodedId = btoa(`ev-${eventId}`);
|
const encodedId = btoa(`ev-${eventId}`);
|
||||||
return `/event-detail?id=${encodedId}`;
|
return `/event-detail?id=${encodedId}`;
|
||||||
};
|
};
|
||||||
@@ -58,18 +58,104 @@ const getChgColor = (val) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取胜率颜色
|
* 胜率仪表盘组件
|
||||||
*/
|
*/
|
||||||
const getWinRateColor = (rate) => {
|
const WinRateGauge = ({ rate }) => {
|
||||||
if (rate >= 70) return '#52C41A'; // 绿色-优秀
|
const validRate = Math.min(100, Math.max(0, rate || 0));
|
||||||
if (rate >= 50) return '#FFD700'; // 金色-良好
|
const angle = (validRate / 100) * 180; // 0-180度
|
||||||
return '#FF4D4F'; // 红色-待提升
|
|
||||||
|
// 根据胜率确定颜色
|
||||||
|
const getGaugeColor = (r) => {
|
||||||
|
if (r >= 70) return '#52C41A';
|
||||||
|
if (r >= 50) return '#FFD700';
|
||||||
|
return '#FF4D4F';
|
||||||
|
};
|
||||||
|
|
||||||
|
const gaugeColor = getGaugeColor(validRate);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box position="relative" w="100%" h="90px" overflow="hidden">
|
||||||
|
{/* 仪表盘背景 */}
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
bottom="0"
|
||||||
|
left="50%"
|
||||||
|
transform="translateX(-50%)"
|
||||||
|
w="140px"
|
||||||
|
h="70px"
|
||||||
|
borderTopLeftRadius="70px"
|
||||||
|
borderTopRightRadius="70px"
|
||||||
|
bg="rgba(255,255,255,0.05)"
|
||||||
|
border="3px solid rgba(255,255,255,0.1)"
|
||||||
|
borderBottom="none"
|
||||||
|
overflow="hidden"
|
||||||
|
>
|
||||||
|
{/* 填充弧 */}
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
bottom="0"
|
||||||
|
left="0"
|
||||||
|
w="100%"
|
||||||
|
h="100%"
|
||||||
|
bg={`conic-gradient(from 180deg, ${gaugeColor} 0deg, ${gaugeColor} ${angle}deg, transparent ${angle}deg)`}
|
||||||
|
opacity="0.3"
|
||||||
|
style={{ transformOrigin: 'center bottom' }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* 指针 */}
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
bottom="3px"
|
||||||
|
left="50%"
|
||||||
|
w="3px"
|
||||||
|
h="50px"
|
||||||
|
bg={gaugeColor}
|
||||||
|
borderRadius="full"
|
||||||
|
transformOrigin="bottom center"
|
||||||
|
transform={`translateX(-50%) rotate(${angle - 90}deg)`}
|
||||||
|
boxShadow={`0 0 10px ${gaugeColor}`}
|
||||||
|
transition="transform 0.5s ease-out"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 中心点 */}
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
bottom="0"
|
||||||
|
left="50%"
|
||||||
|
transform="translateX(-50%)"
|
||||||
|
w="12px"
|
||||||
|
h="12px"
|
||||||
|
bg={gaugeColor}
|
||||||
|
borderRadius="full"
|
||||||
|
boxShadow={`0 0 8px ${gaugeColor}`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 数值显示 */}
|
||||||
|
<VStack
|
||||||
|
position="absolute"
|
||||||
|
bottom="15px"
|
||||||
|
left="50%"
|
||||||
|
transform="translateX(-50%)"
|
||||||
|
spacing={0}
|
||||||
|
>
|
||||||
|
<Text fontSize="2xl" fontWeight="bold" color={gaugeColor} lineHeight="1">
|
||||||
|
{validRate.toFixed(0)}%
|
||||||
|
</Text>
|
||||||
|
<Text fontSize="xs" color="gray.500">胜率</Text>
|
||||||
|
</VStack>
|
||||||
|
|
||||||
|
{/* 刻度标签 */}
|
||||||
|
<Text position="absolute" bottom="0" left="15px" fontSize="2xs" color="gray.600">0</Text>
|
||||||
|
<Text position="absolute" bottom="0" right="15px" fontSize="2xs" color="gray.600">100</Text>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 紧凑数据卡片
|
* 紧凑数据卡片
|
||||||
*/
|
*/
|
||||||
const CompactStatCard = ({ label, value, icon, color = '#FFD700', subText, progress }) => (
|
const CompactStatCard = ({ label, value, icon, color = '#FFD700', subText }) => (
|
||||||
<Box
|
<Box
|
||||||
bg="rgba(0,0,0,0.25)"
|
bg="rgba(0,0,0,0.25)"
|
||||||
borderRadius="md"
|
borderRadius="md"
|
||||||
@@ -95,27 +181,15 @@ const CompactStatCard = ({ label, value, icon, color = '#FFD700', subText, progr
|
|||||||
{subText}
|
{subText}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{progress !== undefined && (
|
|
||||||
<Progress
|
|
||||||
value={progress}
|
|
||||||
size="xs"
|
|
||||||
colorScheme={progress >= 60 ? 'green' : progress >= 40 ? 'yellow' : 'red'}
|
|
||||||
mt={1.5}
|
|
||||||
borderRadius="full"
|
|
||||||
bg="rgba(255,255,255,0.08)"
|
|
||||||
h="3px"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TOP事件列表项 - 支持点击跳转
|
* TOP事件列表项
|
||||||
*/
|
*/
|
||||||
const TopEventItem = ({ event, rank }) => {
|
const TopEventItem = ({ event, rank }) => {
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
if (event.id) {
|
if (event.id) {
|
||||||
// 在新标签页打开事件详情
|
|
||||||
window.open(getEventDetailUrl(event.id), '_blank');
|
window.open(getEventDetailUrl(event.id), '_blank');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -130,9 +204,6 @@ const TopEventItem = ({ event, rank }) => {
|
|||||||
_hover={{ bg: 'rgba(255,215,0,0.12)', cursor: 'pointer' }}
|
_hover={{ bg: 'rgba(255,215,0,0.12)', cursor: 'pointer' }}
|
||||||
transition="all 0.15s"
|
transition="all 0.15s"
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
role="button"
|
|
||||||
tabIndex={0}
|
|
||||||
onKeyPress={(e) => e.key === 'Enter' && handleClick()}
|
|
||||||
>
|
>
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme={rank === 1 ? 'yellow' : rank === 2 ? 'gray' : 'orange'}
|
colorScheme={rank === 1 ? 'yellow' : rank === 2 ? 'gray' : 'orange'}
|
||||||
@@ -155,28 +226,61 @@ const TopEventItem = ({ event, rank }) => {
|
|||||||
{event.title}
|
{event.title}
|
||||||
</Text>
|
</Text>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Text
|
<Text fontSize="xs" fontWeight="bold" color={getChgColor(event.maxChg)}>
|
||||||
fontSize="xs"
|
|
||||||
fontWeight="bold"
|
|
||||||
color={getChgColor(event.maxChg)}
|
|
||||||
>
|
|
||||||
{formatChg(event.maxChg)}
|
{formatChg(event.maxChg)}
|
||||||
</Text>
|
</Text>
|
||||||
</HStack>
|
</HStack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TOP股票列表项
|
||||||
|
*/
|
||||||
|
const TopStockItem = ({ stock, rank }) => {
|
||||||
|
return (
|
||||||
|
<HStack
|
||||||
|
spacing={2}
|
||||||
|
py={1.5}
|
||||||
|
px={2}
|
||||||
|
bg="rgba(0,0,0,0.2)"
|
||||||
|
borderRadius="md"
|
||||||
|
_hover={{ bg: 'rgba(255,215,0,0.12)' }}
|
||||||
|
transition="all 0.15s"
|
||||||
|
>
|
||||||
|
<Badge
|
||||||
|
colorScheme={rank === 1 ? 'yellow' : rank === 2 ? 'gray' : 'orange'}
|
||||||
|
fontSize="2xs"
|
||||||
|
px={1.5}
|
||||||
|
borderRadius="full"
|
||||||
|
minW="18px"
|
||||||
|
textAlign="center"
|
||||||
|
>
|
||||||
|
{rank}
|
||||||
|
</Badge>
|
||||||
|
<Text fontSize="xs" color="gray.400" w="60px">
|
||||||
|
{stock.stockCode?.split('.')[0] || '-'}
|
||||||
|
</Text>
|
||||||
|
<Text fontSize="xs" color="gray.300" flex="1" noOfLines={1}>
|
||||||
|
{stock.stockName || '-'}
|
||||||
|
</Text>
|
||||||
|
<Text fontSize="xs" fontWeight="bold" color={getChgColor(stock.maxChg)}>
|
||||||
|
{formatChg(stock.maxChg)}
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const EventDailyStats = () => {
|
const EventDailyStats = () => {
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [stats, setStats] = useState(null);
|
const [stats, setStats] = useState(null);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
|
const [activeTab, setActiveTab] = useState(0);
|
||||||
|
|
||||||
const fetchStats = useCallback(async () => {
|
const fetchStats = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
const apiBase = getApiBase();
|
const apiBase = getApiBase();
|
||||||
// 获取当日数据(days=1 表示当前交易日)
|
|
||||||
const response = await fetch(`${apiBase}/api/v1/events/effectiveness-stats?days=1`);
|
const response = await fetch(`${apiBase}/api/v1/events/effectiveness-stats?days=1`);
|
||||||
if (!response.ok) throw new Error('获取数据失败');
|
if (!response.ok) throw new Error('获取数据失败');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
@@ -195,7 +299,6 @@ const EventDailyStats = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchStats();
|
fetchStats();
|
||||||
// 每5分钟刷新一次
|
|
||||||
const interval = setInterval(fetchStats, 5 * 60 * 1000);
|
const interval = setInterval(fetchStats, 5 * 60 * 1000);
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [fetchStats]);
|
}, [fetchStats]);
|
||||||
@@ -239,7 +342,7 @@ const EventDailyStats = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { summary, topPerformers = [] } = stats;
|
const { summary, topPerformers = [], topStocks = [] } = stats;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -264,38 +367,27 @@ const EventDailyStats = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 标题 */}
|
{/* 标题 */}
|
||||||
<HStack spacing={2} mb={3}>
|
<HStack spacing={2} mb={2}>
|
||||||
<Box
|
<Box
|
||||||
w="3px"
|
w="3px"
|
||||||
h="16px"
|
h="16px"
|
||||||
bg="linear-gradient(180deg, #FFD700, #FFA500)"
|
bg="linear-gradient(180deg, #FFD700, #FFA500)"
|
||||||
borderRadius="full"
|
borderRadius="full"
|
||||||
/>
|
/>
|
||||||
<Text
|
<Text fontSize="sm" fontWeight="bold" color="white" letterSpacing="wide">
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="bold"
|
|
||||||
color="white"
|
|
||||||
letterSpacing="wide"
|
|
||||||
>
|
|
||||||
今日统计
|
今日统计
|
||||||
</Text>
|
</Text>
|
||||||
<Badge
|
<Badge colorScheme="yellow" variant="subtle" fontSize="2xs" px={1.5}>
|
||||||
colorScheme="yellow"
|
|
||||||
variant="subtle"
|
|
||||||
fontSize="2xs"
|
|
||||||
px={1.5}
|
|
||||||
>
|
|
||||||
实时
|
实时
|
||||||
</Badge>
|
</Badge>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|
||||||
<VStack spacing={3} align="stretch">
|
<VStack spacing={2} align="stretch">
|
||||||
|
{/* 胜率仪表盘 */}
|
||||||
|
<WinRateGauge rate={summary?.positiveRate || 0} />
|
||||||
|
|
||||||
{/* 核心指标 - 2x2 网格 */}
|
{/* 核心指标 - 2x2 网格 */}
|
||||||
<Box
|
<Box display="grid" gridTemplateColumns="repeat(2, 1fr)" gap={2}>
|
||||||
display="grid"
|
|
||||||
gridTemplateColumns="repeat(2, 1fr)"
|
|
||||||
gap={2}
|
|
||||||
>
|
|
||||||
<CompactStatCard
|
<CompactStatCard
|
||||||
label="事件数"
|
label="事件数"
|
||||||
value={summary?.totalEvents || 0}
|
value={summary?.totalEvents || 0}
|
||||||
@@ -304,11 +396,11 @@ const EventDailyStats = () => {
|
|||||||
subText="今日追踪"
|
subText="今日追踪"
|
||||||
/>
|
/>
|
||||||
<CompactStatCard
|
<CompactStatCard
|
||||||
label="胜率"
|
label="关联股票"
|
||||||
value={`${(summary?.positiveRate || 0).toFixed(0)}%`}
|
value={summary?.totalStocks || 0}
|
||||||
icon={<CheckCircleOutlined />}
|
icon={<StockOutlined />}
|
||||||
color={getWinRateColor(summary?.positiveRate || 0)}
|
color="#1890FF"
|
||||||
progress={summary?.positiveRate || 0}
|
subText="去重统计"
|
||||||
/>
|
/>
|
||||||
<CompactStatCard
|
<CompactStatCard
|
||||||
label="平均超额"
|
label="平均超额"
|
||||||
@@ -326,63 +418,59 @@ const EventDailyStats = () => {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* 评分指标 */}
|
|
||||||
<Box
|
|
||||||
display="grid"
|
|
||||||
gridTemplateColumns="repeat(2, 1fr)"
|
|
||||||
gap={2}
|
|
||||||
>
|
|
||||||
<CompactStatCard
|
|
||||||
label="投资价值"
|
|
||||||
value={(summary?.avgInvestScore || 0).toFixed(0)}
|
|
||||||
icon={<StarOutlined />}
|
|
||||||
color="#F59E0B"
|
|
||||||
progress={summary?.avgInvestScore || 0}
|
|
||||||
/>
|
|
||||||
<CompactStatCard
|
|
||||||
label="超预期分"
|
|
||||||
value={(summary?.avgSurpriseScore || 0).toFixed(0)}
|
|
||||||
icon={<TrophyOutlined />}
|
|
||||||
color="#8B5CF6"
|
|
||||||
progress={summary?.avgSurpriseScore || 0}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* 分割线 */}
|
{/* 分割线 */}
|
||||||
<Box h="1px" bg="rgba(255,215,0,0.1)" />
|
<Box h="1px" bg="rgba(255,215,0,0.1)" />
|
||||||
|
|
||||||
{/* TOP表现事件 - 显示 TOP10 */}
|
{/* TOP 表现 - Tab 切换 */}
|
||||||
<Box flex="1" overflow="hidden">
|
<Box flex="1" overflow="hidden">
|
||||||
<HStack spacing={1.5} mb={2}>
|
<Tabs
|
||||||
<TrophyOutlined style={{ color: '#FFD700', fontSize: '12px' }} />
|
variant="soft-rounded"
|
||||||
<Text fontSize="xs" fontWeight="bold" color="gray.400">
|
colorScheme="yellow"
|
||||||
今日 TOP 表现
|
size="sm"
|
||||||
</Text>
|
index={activeTab}
|
||||||
<Text fontSize="2xs" color="gray.600">
|
onChange={setActiveTab}
|
||||||
(点击查看详情)
|
>
|
||||||
</Text>
|
<TabList mb={2}>
|
||||||
|
<Tab
|
||||||
|
fontSize="xs"
|
||||||
|
py={1}
|
||||||
|
px={3}
|
||||||
|
_selected={{ bg: 'rgba(255,215,0,0.2)', color: '#FFD700' }}
|
||||||
|
color="gray.500"
|
||||||
|
>
|
||||||
|
<HStack spacing={1}>
|
||||||
|
<TrophyOutlined style={{ fontSize: '11px' }} />
|
||||||
|
<Text>事件TOP10</Text>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
</Tab>
|
||||||
|
<Tab
|
||||||
|
fontSize="xs"
|
||||||
|
py={1}
|
||||||
|
px={3}
|
||||||
|
_selected={{ bg: 'rgba(255,215,0,0.2)', color: '#FFD700' }}
|
||||||
|
color="gray.500"
|
||||||
|
>
|
||||||
|
<HStack spacing={1}>
|
||||||
|
<StockOutlined style={{ fontSize: '11px' }} />
|
||||||
|
<Text>股票TOP10</Text>
|
||||||
|
</HStack>
|
||||||
|
</Tab>
|
||||||
|
</TabList>
|
||||||
|
|
||||||
|
<TabPanels>
|
||||||
|
{/* 事件 TOP10 */}
|
||||||
|
<TabPanel p={0}>
|
||||||
<VStack
|
<VStack
|
||||||
spacing={1}
|
spacing={1}
|
||||||
align="stretch"
|
align="stretch"
|
||||||
maxH="220px"
|
maxH="195px"
|
||||||
overflowY="auto"
|
overflowY="auto"
|
||||||
pr={1}
|
pr={1}
|
||||||
css={{
|
css={{
|
||||||
'&::-webkit-scrollbar': {
|
'&::-webkit-scrollbar': { width: '4px' },
|
||||||
width: '4px',
|
'&::-webkit-scrollbar-track': { background: 'rgba(255,255,255,0.05)', borderRadius: '2px' },
|
||||||
},
|
'&::-webkit-scrollbar-thumb': { background: 'rgba(255,215,0,0.3)', borderRadius: '2px' },
|
||||||
'&::-webkit-scrollbar-track': {
|
'&::-webkit-scrollbar-thumb:hover': { background: 'rgba(255,215,0,0.5)' },
|
||||||
background: 'rgba(255,255,255,0.05)',
|
|
||||||
borderRadius: '2px',
|
|
||||||
},
|
|
||||||
'&::-webkit-scrollbar-thumb': {
|
|
||||||
background: 'rgba(255,215,0,0.3)',
|
|
||||||
borderRadius: '2px',
|
|
||||||
},
|
|
||||||
'&::-webkit-scrollbar-thumb:hover': {
|
|
||||||
background: 'rgba(255,215,0,0.5)',
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{topPerformers.slice(0, 10).map((event, idx) => (
|
{topPerformers.slice(0, 10).map((event, idx) => (
|
||||||
@@ -394,6 +482,35 @@ const EventDailyStats = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</VStack>
|
</VStack>
|
||||||
|
</TabPanel>
|
||||||
|
|
||||||
|
{/* 股票 TOP10 */}
|
||||||
|
<TabPanel p={0}>
|
||||||
|
<VStack
|
||||||
|
spacing={1}
|
||||||
|
align="stretch"
|
||||||
|
maxH="195px"
|
||||||
|
overflowY="auto"
|
||||||
|
pr={1}
|
||||||
|
css={{
|
||||||
|
'&::-webkit-scrollbar': { width: '4px' },
|
||||||
|
'&::-webkit-scrollbar-track': { background: 'rgba(255,255,255,0.05)', borderRadius: '2px' },
|
||||||
|
'&::-webkit-scrollbar-thumb': { background: 'rgba(255,215,0,0.3)', borderRadius: '2px' },
|
||||||
|
'&::-webkit-scrollbar-thumb:hover': { background: 'rgba(255,215,0,0.5)' },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{topStocks.slice(0, 10).map((stock, idx) => (
|
||||||
|
<TopStockItem key={stock.stockCode || idx} stock={stock} rank={idx + 1} />
|
||||||
|
))}
|
||||||
|
{topStocks.length === 0 && (
|
||||||
|
<Text fontSize="xs" color="gray.600" textAlign="center" py={2}>
|
||||||
|
暂无数据
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</VStack>
|
||||||
|
</TabPanel>
|
||||||
|
</TabPanels>
|
||||||
|
</Tabs>
|
||||||
</Box>
|
</Box>
|
||||||
</VStack>
|
</VStack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
Reference in New Issue
Block a user