heropanel修改

This commit is contained in:
2026-01-09 10:10:47 +08:00
parent 40dbef63ee
commit b2160347db
3 changed files with 102 additions and 57 deletions

6
app.py
View File

@@ -11418,10 +11418,10 @@ def get_events_effectiveness_stats():
'topEvents': top_events 'topEvents': top_events
}) })
# 找出表现最好的事件(全局) # 找出表现最好的事件(全局,按平均超额排序
top_performers = sorted( top_performers = sorted(
[e for e in events_query if e.related_max_chg is not None], [e for e in events_query if e.related_avg_chg is not None],
key=lambda x: x.related_max_chg, key=lambda x: x.related_avg_chg,
reverse=True reverse=True
)[:10] )[:10]

View File

@@ -27,6 +27,7 @@ import {
TrophyOutlined, TrophyOutlined,
StockOutlined, StockOutlined,
CalendarOutlined, CalendarOutlined,
ReloadOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { getApiBase } from '@utils/apiConfig'; import { getApiBase } from '@utils/apiConfig';
@@ -346,8 +347,8 @@ const TopEventItem = ({ event, rank }) => {
{event.title} {event.title}
</Text> </Text>
</Tooltip> </Tooltip>
<Text fontSize="xs" fontWeight="bold" color={getChgColor(event.maxChg)}> <Text fontSize="xs" fontWeight="bold" color={getChgColor(event.avgChg)}>
{formatChg(event.maxChg)} {formatChg(event.avgChg)}
</Text> </Text>
</HStack> </HStack>
); );
@@ -392,13 +393,18 @@ const TopStockItem = ({ stock, rank }) => {
const EventDailyStats = () => { const EventDailyStats = () => {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
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 [activeTab, setActiveTab] = useState(0);
const [selectedDate, setSelectedDate] = useState(''); const [selectedDate, setSelectedDate] = useState('');
const fetchStats = useCallback(async (dateStr = '') => { const fetchStats = useCallback(async (dateStr = '', isRefresh = false) => {
setLoading(true); if (isRefresh) {
setRefreshing(true);
} else {
setLoading(true);
}
setError(null); setError(null);
try { try {
const apiBase = getApiBase(); const apiBase = getApiBase();
@@ -416,6 +422,7 @@ const EventDailyStats = () => {
setError(err.message); setError(err.message);
} finally { } finally {
setLoading(false); setLoading(false);
setRefreshing(false);
} }
}, []); }, []);
@@ -423,10 +430,10 @@ const EventDailyStats = () => {
fetchStats(selectedDate); fetchStats(selectedDate);
}, [fetchStats, selectedDate]); }, [fetchStats, selectedDate]);
// 自动刷新(仅当选择今天时) // 自动刷新(仅当选择今天时每60秒刷新一次
useEffect(() => { useEffect(() => {
if (!selectedDate) { if (!selectedDate) {
const interval = setInterval(() => fetchStats(''), 5 * 60 * 1000); const interval = setInterval(() => fetchStats('', true), 60 * 1000);
return () => clearInterval(interval); return () => clearInterval(interval);
} }
}, [selectedDate, fetchStats]); }, [selectedDate, fetchStats]);
@@ -435,6 +442,13 @@ const EventDailyStats = () => {
setSelectedDate(e.target.value); setSelectedDate(e.target.value);
}; };
// 手动刷新
const handleRefresh = () => {
if (!refreshing) {
fetchStats(selectedDate, true);
}
};
const isToday = !selectedDate; const isToday = !selectedDate;
if (loading) { if (loading) {
@@ -549,6 +563,27 @@ const EventDailyStats = () => {
)} )}
</HStack> </HStack>
<HStack spacing={2}> <HStack spacing={2}>
{/* 刷新按钮 */}
<Tooltip label="刷新数据" placement="bottom" hasArrow>
<Box
p={1.5}
bg="rgba(255,255,255,0.03)"
border="1px solid rgba(255,255,255,0.08)"
borderRadius="lg"
cursor="pointer"
_hover={{ bg: 'rgba(6, 182, 212, 0.15)', borderColor: 'rgba(6, 182, 212, 0.3)', transform: 'scale(1.05)' }}
transition="all 0.2s"
onClick={handleRefresh}
>
<ReloadOutlined
style={{
color: 'rgba(6, 182, 212, 0.8)',
fontSize: '14px',
}}
spin={refreshing}
/>
</Box>
</Tooltip>
{/* 今天按钮 - 仅在查看历史时显示 */} {/* 今天按钮 - 仅在查看历史时显示 */}
{!isToday && ( {!isToday && (
<Box <Box
@@ -607,50 +642,59 @@ const EventDailyStats = () => {
</HStack> </HStack>
</Flex> </Flex>
{/* 内容区域 - 使用 flex: 1 填充剩余空间 */} {/* 内容区域 - 固定高度滚动 */}
<VStack spacing={4} align="stretch" flex="1"> <Box
{/* 胜率对比仪表盘 */} flex="1"
<WinRateGauge overflowY="auto"
eventRate={summary?.positiveRate || 0} pr={1}
marketRate={marketStats?.risingRate || 0} css={{
marketStats={marketStats} '&::-webkit-scrollbar': { width: '4px' },
/> '&::-webkit-scrollbar-track': { background: 'rgba(255,255,255,0.02)', borderRadius: '2px' },
'&::-webkit-scrollbar-thumb': { background: 'rgba(124, 58, 237, 0.3)', borderRadius: '2px' },
'&::-webkit-scrollbar-thumb:hover': { background: 'rgba(124, 58, 237, 0.5)' },
}}
>
<VStack spacing={4} align="stretch">
{/* 胜率对比仪表盘 */}
<WinRateGauge
eventRate={summary?.positiveRate || 0}
marketRate={marketStats?.risingRate || 0}
marketStats={marketStats}
/>
{/* 核心指标 - 2x2 网格 */} {/* 核心指标 - 2x2 网格 */}
<Box display="grid" gridTemplateColumns="repeat(2, 1fr)" gap={2}> <Box display="grid" gridTemplateColumns="repeat(2, 1fr)" gap={3}>
<CompactStatCard <CompactStatCard
label="事件数" label="事件数"
value={summary?.totalEvents || 0} value={summary?.totalEvents || 0}
icon={<FireOutlined />} icon={<FireOutlined />}
color="#FFD700" color="#F59E0B"
subText="追踪中" />
/> <CompactStatCard
<CompactStatCard label="关联股票"
label="关联股票" value={summary?.totalStocks || 0}
value={summary?.totalStocks || 0} icon={<StockOutlined />}
icon={<StockOutlined />} color="#06B6D4"
color="#1890FF" />
subText="去重" <CompactStatCard
/> label="平均超额"
<CompactStatCard value={formatChg(summary?.avgChg)}
label="平均超额" icon={<RiseOutlined />}
value={formatChg(summary?.avgChg)} color={summary?.avgChg >= 0 ? '#F31260' : '#17C964'}
icon={<RiseOutlined />} />
color={getChgColor(summary?.avgChg)} <CompactStatCard
/> label="最大超额"
<CompactStatCard value={formatChg(summary?.maxChg)}
label="最大超额" icon={<ThunderboltOutlined />}
value={formatChg(summary?.maxChg)} color="#F31260"
icon={<ThunderboltOutlined />} />
color="#FF4D4F" </Box>
/>
</Box>
{/* 分割线 */} {/* 分割线 */}
<Box h="1px" bg="rgba(255,215,0,0.1)" flexShrink={0} /> <Box h="1px" bg="rgba(255,255,255,0.06)" />
{/* TOP 表现 - Tab 切换flex: 1 填充剩余空间 */} {/* TOP 表现 - Tab 切换 */}
<Box flex="1" display="flex" flexDirection="column" minH={0}> <Box>
<Tabs <Tabs
variant="soft-rounded" variant="soft-rounded"
colorScheme="yellow" colorScheme="yellow"
@@ -742,8 +786,9 @@ const EventDailyStats = () => {
</TabPanel> </TabPanel>
</TabPanels> </TabPanels>
</Tabs> </Tabs>
</Box> </Box>
</VStack> </VStack>
</Box>
</Box> </Box>
); );
}; };

View File

@@ -2617,8 +2617,8 @@ const RightPanelTabs = () => {
</Box> </Box>
</HStack> </HStack>
{/* Tab 内容区域 - 固定高度确保一致 */} {/* Tab 内容区域 */}
<Box flex="1" minH="650px" p={3} overflow="hidden"> <Box flex="1" p={3} overflow="hidden">
{activeTab === 'comet' ? ( {activeTab === 'comet' ? (
<Box h="100%"> <Box h="100%">
<ThemeCometChart /> <ThemeCometChart />
@@ -2808,14 +2808,14 @@ const HeroPanel = () => {
</Flex> </Flex>
{/* AI舆情时空决策驾驶舱 - 左侧今日统计(2/5)右侧Tab切换(3/5) */} {/* AI舆情时空决策驾驶舱 - 左侧今日统计(2/5)右侧Tab切换(3/5) */}
<Flex gap={5}> <Flex gap={5} align="stretch" h="720px">
{/* 左侧:今日事件统计 */} {/* 左侧:今日事件统计 */}
<Box flex="2" minW="0"> <Box flex="2" minW="0" h="100%">
<EventDailyStats /> <EventDailyStats />
</Box> </Box>
{/* 右侧:连板情绪 / 日历 Tab 切换 */} {/* 右侧:连板情绪 / 日历 Tab 切换 */}
<Box flex="3" minW="0"> <Box flex="3" minW="0" h="100%">
<RightPanelTabs /> <RightPanelTabs />
</Box> </Box>
</Flex> </Flex>