heropanel修改
This commit is contained in:
6
app.py
6
app.py
@@ -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]
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user