import React, { useState, useEffect } from 'react'; import { Box, VStack, HStack, Heading, Text, Badge, useToast, Skeleton, IconButton, Flex, useColorModeValue, SimpleGrid, Tooltip, Card, CardBody, Alert, AlertIcon, } from '@chakra-ui/react'; import { RefreshCw, ChevronUp } from 'lucide-react'; // 导入拆分的组件 // 注意:在实际使用中,这些组件应该被拆分到独立的文件中 // 这里为了演示,我们假设它们已经被正确导出 // 使用静态数据服务(从 /data/zt/ 读取 JSON 文件) import ztStaticService from '../../services/ztStaticService'; // 导入的组件(实际使用时应该从独立文件导入) // 恢复使用本页自带的轻量日历 import EnhancedCalendar from './components/EnhancedCalendar'; import SectorDetails from './components/SectorDetails'; import { DataAnalysis } from './components/DataVisualizationComponents'; import { AdvancedSearch, SearchResultsModal } from './components/SearchComponents'; // 导航栏已由 MainLayout 提供,无需在此导入 // 导入高位股统计组件 import HighPositionStocks from './components/HighPositionStocks'; import { logger } from '../../utils/logger'; import { useLimitAnalyseEvents } from './hooks/useLimitAnalyseEvents'; // 主组件 export default function LimitAnalyse() { const [selectedDate, setSelectedDate] = useState(null); const [dateStr, setDateStr] = useState(''); const [loading, setLoading] = useState(false); const [dailyData, setDailyData] = useState(null); const [availableDates, setAvailableDates] = useState(null); // null 表示未加载,[] 表示加载完成但无数据 const [wordCloudData, setWordCloudData] = useState([]); const [searchResults, setSearchResults] = useState(null); const [isSearchOpen, setIsSearchOpen] = useState(false); const toast = useToast(); // 🎯 PostHog 事件追踪 const { trackDateSelected, trackDailyStatsViewed, trackSearchInitiated, } = useLimitAnalyseEvents(); const bgColor = useColorModeValue('gray.50', 'gray.900'); const cardBg = useColorModeValue('white', 'gray.800'); const accentColor = useColorModeValue('blue.500', 'blue.300'); // 获取可用日期 useEffect(() => { fetchAvailableDates(); }, []); // 初始进入展示骨架屏,直到选中日期的数据加载完成 useEffect(() => { setLoading(true); }, []); // 根据可用日期加载最近一个有数据的日期 useEffect(() => { // 等待日期列表加载完成(null 表示未加载) if (availableDates === null) { return; } if (availableDates.length > 0) { // 选择日期字符串最大的那一天(格式为 YYYYMMDD) const latest = availableDates.reduce((max, cur) => (!max || (cur.date && cur.date > max)) ? cur.date : max , null); if (latest) { setDateStr(latest); const year = parseInt(latest.slice(0, 4), 10); const month = parseInt(latest.slice(4, 6), 10) - 1; const day = parseInt(latest.slice(6, 8), 10); setSelectedDate(new Date(year, month, day)); fetchDailyAnalysis(latest); } } else { // 日期列表为空,显示提示但不请求数据 setLoading(false); logger.warn('LimitAnalyse', '暂无可用数据'); } }, [availableDates]); // 使用静态数据服务获取数据 const fetchAvailableDates = async () => { try { const data = await ztStaticService.fetchAvailableDates(); if (data.success) { setAvailableDates(data.events || []); logger.debug('LimitAnalyse', '可用日期加载成功(静态文件)', { count: data.events?.length || 0 }); } else { // 请求成功但返回失败,设置空数组 setAvailableDates([]); logger.warn('LimitAnalyse', '日期列表返回失败', data.error); } } catch (error) { // 请求失败,设置空数组避免一直 loading setAvailableDates([]); logger.error('LimitAnalyse', 'fetchAvailableDates', error); } }; const fetchDailyAnalysis = async (date) => { setLoading(true); try { const data = await ztStaticService.fetchDailyAnalysis(date); if (data.success) { setDailyData(data.data); // 🎯 追踪每日统计数据查看 trackDailyStatsViewed(data.data, date); // 词云数据已包含在分析数据中 setWordCloudData(data.data.word_freq_data || []); logger.debug('LimitAnalyse', '每日分析数据加载成功(静态文件)', { date, totalStocks: data.data?.total_stocks || 0, fromCache: data.from_cache }); } } catch (error) { logger.error('LimitAnalyse', 'fetchDailyAnalysis', error, { date }); } finally { setLoading(false); } }; // 格式化日期 const formatDateStr = (date) => { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}${month}${day}`; }; // 处理日期选择 const handleDateChange = (date) => { const previousDateStr = dateStr; setSelectedDate(date); const dateString = formatDateStr(date); setDateStr(dateString); // 🎯 追踪日期选择 trackDateSelected(dateString, previousDateStr); fetchDailyAnalysis(dateString); }; // 处理搜索(使用静态数据关键词搜索) const handleSearch = async (searchParams) => { // 🎯 追踪搜索开始 trackSearchInitiated( searchParams.query, searchParams.type || 'all', searchParams.mode || 'keyword' // 静态模式只支持关键词搜索 ); setLoading(true); try { const data = await ztStaticService.searchStocks(searchParams); if (data.success) { setSearchResults(data.data); setIsSearchOpen(true); logger.info('LimitAnalyse', '搜索完成(静态文件)', { resultCount: data.data?.total || 0, searchParams }); toast({ title: '搜索完成', description: `找到 ${data.data.total} 只相关股票`, status: 'success', duration: 3000, }); } else { toast({ title: '搜索失败', description: data.error || '请稍后重试', status: 'error', duration: 3000, }); } } catch (error) { logger.error('LimitAnalyse', 'handleSearch', error, { searchParams }); toast({ title: '搜索失败', description: '请稍后重试', status: 'error', duration: 3000, }); } finally { setLoading(false); } }; // 处理板块数据排序 const getSortedSectorData = () => { if (!dailyData?.sector_data) return []; const sectors = Object.entries(dailyData.sector_data); const announcement = sectors.find(([name]) => name === '公告'); const others = sectors.filter(([name]) => name !== '公告' && name !== '其他'); const other = sectors.find(([name]) => name === '其他'); // 按数量排序 others.sort((a, b) => b[1].count - a[1].count); // 组合:公告在最前,其他在最后 let result = []; if (announcement) result.push(announcement); result = result.concat(others); if (other) result.push(other); return result; }; const formatDisplayDate = (date) => { if (!date) return ''; const year = date.getFullYear(); const month = date.getMonth() + 1; const day = date.getDate(); return `${year}年${month}月${day}日`; }; const getSelectedDateCount = () => { if (!selectedDate || !availableDates?.length) return null; const date = formatDateStr(selectedDate); const found = availableDates.find(d => d.date === date); return found ? found.count : null; }; return ( {/* 导航栏已由 MainLayout 提供 */} {/* 顶部Header */} {/* 左侧:标题置顶,注释与图例贴底 */} AI驱动 实时更新 涨停板块分析平台 以大模型辅助整理海量信息,结合领域知识图谱与分析师复核,呈现涨停板块关键线索 {selectedDate ? `当前选择:${formatDisplayDate(selectedDate)}` : '当前选择:--'} {getSelectedDateCount() != null ? ` - ${getSelectedDateCount()}只涨停` : ''} 涨停数量图例 少量 (≤50只) 中等 (51-80只) 大量 (>80只) {/* 右侧:半屏日历 */} {/* 主内容区 - padding 由 MainLayout 统一设置 */} {/* 搜索框 */} {/* 数据分析(含涨停统计) */} {loading ? ( ) : ( )} {/* 板块详情 - 核心内容 */} {loading ? ( ) : ( )} {/* 高位股统计 */} {/* 弹窗 */} setIsSearchOpen(false)} searchResults={searchResults} onStockClick={() => {}} /> {/* 浮动按钮 */} } colorScheme="blue" size="lg" borderRadius="full" boxShadow="2xl" onClick={() => fetchDailyAnalysis(dateStr)} isLoading={loading} /> } colorScheme="gray" size="lg" borderRadius="full" boxShadow="2xl" onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })} /> ); }