feat: 添加滚动组件

This commit is contained in:
zdl
2025-11-02 16:41:21 +08:00
parent e7e2b3bb11
commit 0110dc2fdc

View File

@@ -8,7 +8,6 @@ import {
Badge,
Button,
Collapse,
useDisclosure,
Skeleton,
Alert,
AlertIcon,
@@ -19,13 +18,6 @@ import {
Icon,
useColorModeValue,
Tooltip,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalBody,
ModalFooter,
Spinner,
Table,
Thead,
@@ -43,7 +35,9 @@ import {
FaChartLine,
FaEye,
FaTimes,
FaInfoCircle
FaInfoCircle,
FaChevronDown,
FaChevronUp
} from 'react-icons/fa';
import { stockService } from '../../../services/eventService';
import { logger } from '../../../utils/logger';
@@ -57,11 +51,9 @@ const HistoricalEvents = ({
// 所有 useState/useEffect/useContext/useRef/useCallback/useMemo 必须在组件顶层、顺序一致
// 不要在 if/循环/回调中调用 Hook
const [expandedEvents, setExpandedEvents] = useState(new Set());
const [selectedEvent, setSelectedEvent] = useState(null);
const [expandedStocks, setExpandedStocks] = useState(new Set()); // 追踪哪些事件的股票列表被展开
const [eventStocks, setEventStocks] = useState({});
const [loadingStocks, setLoadingStocks] = useState(false);
const { isOpen, onOpen, onClose } = useDisclosure();
const [loadingStocks, setLoadingStocks] = useState({});
// 颜色主题
const timelineBg = useColorModeValue('#D4AF37', '#B8860B');
@@ -80,36 +72,48 @@ const HistoricalEvents = ({
setExpandedEvents(newExpanded);
};
// 显示事件相关股票
const showEventStocks = async (event) => {
setSelectedEvent(event);
setLoadingStocks(true);
onOpen();
// 切换股票列表展开状态
const toggleStocksExpansion = async (event) => {
const eventId = event.id;
const newExpanded = new Set(expandedStocks);
// 如果正在收起,直接更新状态
if (newExpanded.has(eventId)) {
newExpanded.delete(eventId);
setExpandedStocks(newExpanded);
return;
}
// 如果正在展开,先展开再加载数据
newExpanded.add(eventId);
setExpandedStocks(newExpanded);
// 如果已经加载过该事件的股票数据,不再重复加载
if (eventStocks[eventId]) {
return;
}
// 标记为加载中
setLoadingStocks(prev => ({ ...prev, [eventId]: true }));
try {
// 如果已经加载过该事件的股票数据,直接使用缓存
if (eventStocks[event.id]) {
setLoadingStocks(false);
return;
}
// 调用API获取历史事件相关股票
const response = await stockService.getHistoricalEventStocks(event.id);
const response = await stockService.getHistoricalEventStocks(eventId);
setEventStocks(prev => ({
...prev,
[event.id]: response.data || []
[eventId]: response.data || []
}));
} catch (err) {
logger.error('HistoricalEvents', 'showEventStocks', err, {
eventId: event.id,
logger.error('HistoricalEvents', 'toggleStocksExpansion', err, {
eventId: eventId,
eventTitle: event.title
});
setEventStocks(prev => ({
...prev,
[event.id]: []
[eventId]: []
}));
} finally {
setLoadingStocks(false);
setLoadingStocks(prev => ({ ...prev, [eventId]: false }));
}
};
@@ -377,7 +381,8 @@ const HistoricalEvents = ({
<Button
size="sm"
leftIcon={<Icon as={FaChartLine} />}
onClick={() => showEventStocks(event)}
rightIcon={<Icon as={expandedStocks.has(event.id) ? FaChevronUp : FaChevronDown} />}
onClick={() => toggleStocksExpansion(event)}
colorScheme="blue"
variant="outline"
>
@@ -416,6 +421,31 @@ const HistoricalEvents = ({
</VStack>
</Box>
</Collapse>
{/* 相关股票列表 Collapse */}
<Collapse in={expandedStocks.has(event.id)} animateOpacity>
<Box
mt={3}
pt={3}
borderTop="1px solid"
borderTopColor={borderColor}
bg={useColorModeValue('gray.50', 'gray.750')}
p={3}
borderRadius="md"
>
{loadingStocks[event.id] ? (
<VStack spacing={4} py={8}>
<Spinner size="lg" color="blue.500" />
<Text color={textSecondary}>加载相关股票数据...</Text>
</VStack>
) : (
<StocksList
stocks={eventStocks[event.id] || []}
eventTradingDate={event.event_date}
/>
)}
</Box>
</Collapse>
</VStack>
</CardBody>
</Card>
@@ -426,40 +456,6 @@ const HistoricalEvents = ({
</VStack>
</Box>
</VStack>
{/* 事件相关股票模态框 */}
<Modal isOpen={isOpen} onClose={onClose} size="4xl">
<ModalOverlay />
<ModalContent maxW="80vw" maxH="85vh">
<ModalHeader>
<VStack align="flex-start" spacing={1}>
<Text>{selectedEvent?.title || '历史事件'}</Text>
<Text fontSize="sm" color={textSecondary} fontWeight="normal">
相关股票信息
</Text>
</VStack>
</ModalHeader>
<ModalCloseButton />
<ModalBody overflowY="auto" maxH="calc(85vh - 180px)">
{loadingStocks ? (
<VStack spacing={4} py={8}>
<Spinner size="lg" color="blue.500" />
<Text color={textSecondary}>加载相关股票数据...</Text>
</VStack>
) : (
<StocksList
stocks={selectedEvent ? eventStocks[selectedEvent.id] || [] : []}
eventTradingDate={selectedEvent ? selectedEvent.event_date : null}
/>
)}
</ModalBody>
<ModalFooter>
<Button onClick={onClose}>关闭</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};