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
})
# 找出表现最好的事件(全局)
# 找出表现最好的事件(全局,按平均超额排序
top_performers = sorted(
[e for e in events_query if e.related_max_chg is not None],
key=lambda x: x.related_max_chg,
[e for e in events_query if e.related_avg_chg is not None],
key=lambda x: x.related_avg_chg,
reverse=True
)[:10]

View File

@@ -27,6 +27,7 @@ import {
TrophyOutlined,
StockOutlined,
CalendarOutlined,
ReloadOutlined,
} from '@ant-design/icons';
import { getApiBase } from '@utils/apiConfig';
@@ -346,8 +347,8 @@ const TopEventItem = ({ event, rank }) => {
{event.title}
</Text>
</Tooltip>
<Text fontSize="xs" fontWeight="bold" color={getChgColor(event.maxChg)}>
{formatChg(event.maxChg)}
<Text fontSize="xs" fontWeight="bold" color={getChgColor(event.avgChg)}>
{formatChg(event.avgChg)}
</Text>
</HStack>
);
@@ -392,13 +393,18 @@ const TopStockItem = ({ stock, rank }) => {
const EventDailyStats = () => {
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [stats, setStats] = useState(null);
const [error, setError] = useState(null);
const [activeTab, setActiveTab] = useState(0);
const [selectedDate, setSelectedDate] = useState('');
const fetchStats = useCallback(async (dateStr = '') => {
setLoading(true);
const fetchStats = useCallback(async (dateStr = '', isRefresh = false) => {
if (isRefresh) {
setRefreshing(true);
} else {
setLoading(true);
}
setError(null);
try {
const apiBase = getApiBase();
@@ -416,6 +422,7 @@ const EventDailyStats = () => {
setError(err.message);
} finally {
setLoading(false);
setRefreshing(false);
}
}, []);
@@ -423,10 +430,10 @@ const EventDailyStats = () => {
fetchStats(selectedDate);
}, [fetchStats, selectedDate]);
// 自动刷新(仅当选择今天时)
// 自动刷新(仅当选择今天时每60秒刷新一次
useEffect(() => {
if (!selectedDate) {
const interval = setInterval(() => fetchStats(''), 5 * 60 * 1000);
const interval = setInterval(() => fetchStats('', true), 60 * 1000);
return () => clearInterval(interval);
}
}, [selectedDate, fetchStats]);
@@ -435,6 +442,13 @@ const EventDailyStats = () => {
setSelectedDate(e.target.value);
};
// 手动刷新
const handleRefresh = () => {
if (!refreshing) {
fetchStats(selectedDate, true);
}
};
const isToday = !selectedDate;
if (loading) {
@@ -549,6 +563,27 @@ const EventDailyStats = () => {
)}
</HStack>
<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 && (
<Box
@@ -607,50 +642,59 @@ const EventDailyStats = () => {
</HStack>
</Flex>
{/* 内容区域 - 使用 flex: 1 填充剩余空间 */}
<VStack spacing={4} align="stretch" flex="1">
{/* 胜率对比仪表盘 */}
<WinRateGauge
eventRate={summary?.positiveRate || 0}
marketRate={marketStats?.risingRate || 0}
marketStats={marketStats}
/>
{/* 内容区域 - 固定高度滚动 */}
<Box
flex="1"
overflowY="auto"
pr={1}
css={{
'&::-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 网格 */}
<Box display="grid" gridTemplateColumns="repeat(2, 1fr)" gap={2}>
<CompactStatCard
label="事件数"
value={summary?.totalEvents || 0}
icon={<FireOutlined />}
color="#FFD700"
subText="追踪中"
/>
<CompactStatCard
label="关联股票"
value={summary?.totalStocks || 0}
icon={<StockOutlined />}
color="#1890FF"
subText="去重"
/>
<CompactStatCard
label="平均超额"
value={formatChg(summary?.avgChg)}
icon={<RiseOutlined />}
color={getChgColor(summary?.avgChg)}
/>
<CompactStatCard
label="最大超额"
value={formatChg(summary?.maxChg)}
icon={<ThunderboltOutlined />}
color="#FF4D4F"
/>
</Box>
{/* 核心指标 - 2x2 网格 */}
<Box display="grid" gridTemplateColumns="repeat(2, 1fr)" gap={3}>
<CompactStatCard
label="事件数"
value={summary?.totalEvents || 0}
icon={<FireOutlined />}
color="#F59E0B"
/>
<CompactStatCard
label="关联股票"
value={summary?.totalStocks || 0}
icon={<StockOutlined />}
color="#06B6D4"
/>
<CompactStatCard
label="平均超额"
value={formatChg(summary?.avgChg)}
icon={<RiseOutlined />}
color={summary?.avgChg >= 0 ? '#F31260' : '#17C964'}
/>
<CompactStatCard
label="最大超额"
value={formatChg(summary?.maxChg)}
icon={<ThunderboltOutlined />}
color="#F31260"
/>
</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 填充剩余空间 */}
<Box flex="1" display="flex" flexDirection="column" minH={0}>
{/* TOP 表现 - Tab 切换 */}
<Box>
<Tabs
variant="soft-rounded"
colorScheme="yellow"
@@ -742,8 +786,9 @@ const EventDailyStats = () => {
</TabPanel>
</TabPanels>
</Tabs>
</Box>
</VStack>
</Box>
</VStack>
</Box>
</Box>
);
};

View File

@@ -2617,8 +2617,8 @@ const RightPanelTabs = () => {
</Box>
</HStack>
{/* Tab 内容区域 - 固定高度确保一致 */}
<Box flex="1" minH="650px" p={3} overflow="hidden">
{/* Tab 内容区域 */}
<Box flex="1" p={3} overflow="hidden">
{activeTab === 'comet' ? (
<Box h="100%">
<ThemeCometChart />
@@ -2808,14 +2808,14 @@ const HeroPanel = () => {
</Flex>
{/* 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 />
</Box>
{/* 右侧:连板情绪 / 日历 Tab 切换 */}
<Box flex="3" minW="0">
<Box flex="3" minW="0" h="100%">
<RightPanelTabs />
</Box>
</Flex>