From f9387ffbd99ba90d14bcf3f50217b74f6a2a16ba Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Wed, 5 Nov 2025 08:56:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=9B=86=E6=88=90=E5=9B=9B=E6=8E=92/?= =?UTF-8?q?=E7=BA=B5=E5=90=91=E6=A8=A1=E5=BC=8FUI=E5=92=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E4=BA=A4=E4=BA=92=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VirtualizedFourRowGrid: - 组件通用化,支持多列布局(通过 columnsPerRow 参数配置) - 支持自定义卡片组件(通过 CardComponent 参数传入) - 根据列数动态调整间距 EventScrollList: - 添加四排和纵向模式切换按钮 - 集成 VirtualizedFourRowGrid 组件(四排模式使用4列,纵向模式使用1列) - 添加纵向分栏布局(1:2 比例,左侧列表+右侧详情) - 启用纵向滚动和统一滚动条样式 - 接收新 props: displayEvents, isAccumulateMode, loadNextPage, onFourRowEventClick DynamicNewsCard: - 添加 Modal 弹窗显示四排模式详情 - 优化自动选中逻辑: · 首次加载时自动选中第一个事件 · 翻页时,单排/双排/纵向模式自动选中当前页第一个事件(保持详情显示) · 翻页时,四排模式清空选中状态(通过弹窗显示详情) - 传递新 props 到 EventScrollList - 添加调试日志 --- .../Community/components/DynamicNewsCard.js | 87 ++++++++++-- .../DynamicNewsCard/EventScrollList.js | 130 ++++++++++++++++-- .../components/DynamicNewsCard/constants.js | 2 +- 3 files changed, 198 insertions(+), 21 deletions(-) diff --git a/src/views/Community/components/DynamicNewsCard.js b/src/views/Community/components/DynamicNewsCard.js index b9bc38e5..e7cc4d0b 100644 --- a/src/views/Community/components/DynamicNewsCard.js +++ b/src/views/Community/components/DynamicNewsCard.js @@ -16,8 +16,15 @@ import { Badge, Center, Spinner, + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalBody, + ModalCloseButton, useColorModeValue, - useToast + useToast, + useDisclosure } from '@chakra-ui/react'; import { TimeIcon } from '@chakra-ui/icons'; import EventScrollList from './DynamicNewsCard/EventScrollList'; @@ -27,6 +34,9 @@ import { fetchDynamicNews, toggleEventFollow, selectEventFollowStatus } from '.. import { usePagination } from './DynamicNewsCard/hooks/usePagination'; import { PAGINATION_CONFIG } from './DynamicNewsCard/constants'; +// 🔍 调试:渲染计数器 +let dynamicNewsCardRenderCount = 0; + /** * 实时要闻·动态追踪 - 事件展示卡片组件 * @param {Array} allCachedEvents - 完整缓存事件列表(从 Redux 传入) @@ -56,6 +66,9 @@ const DynamicNewsCard = forwardRef(({ onViewDetail, ...rest }, ref) => { + // 🔍 调试:记录每次渲染 + dynamicNewsCardRenderCount++; + console.log(`%c🔍 [DynamicNewsCard] 渲染 #${dynamicNewsCardRenderCount} - allCachedEvents.length=${allCachedEvents.length}, total=${total}`, 'color: #FF9800; font-weight: bold; font-size: 14px;'); const dispatch = useDispatch(); const toast = useToast(); const cardBg = useColorModeValue('white', 'gray.800'); @@ -72,8 +85,14 @@ const DynamicNewsCard = forwardRef(({ // 本地状态 const [selectedEvent, setSelectedEvent] = useState(null); + // 弹窗状态(用于四排模式) + const { isOpen: isModalOpen, onOpen: onModalOpen, onClose: onModalClose } = useDisclosure(); + const [modalEvent, setModalEvent] = useState(null); + // 初始化标记 - 确保初始加载只执行一次 const hasInitialized = useRef(false); + // 追踪是否已自动选中过首个事件 + const hasAutoSelectedFirstEvent = useRef(false); // 使用分页 Hook const { @@ -84,8 +103,11 @@ const DynamicNewsCard = forwardRef(({ totalPages, hasMore, currentPageEvents, + displayEvents, // 新增:累积显示的事件列表 + isAccumulateMode, // 新增:是否累积模式 handlePageChange, - handleModeToggle + handleModeToggle, + loadNextPage // 新增:加载下一页 } = usePagination({ allCachedEvents, total, @@ -94,6 +116,13 @@ const DynamicNewsCard = forwardRef(({ toast }); + // 四排模式的事件点击处理(打开弹窗) + const handleFourRowEventClick = useCallback((event) => { + console.log('%c🔲 [四排模式] 点击事件,打开详情弹窗', 'color: #8B5CF6; font-weight: bold;', { eventId: event.id, title: event.title }); + setModalEvent(event); + onModalOpen(); + }, [onModalOpen]); + // 初始加载 - 只在组件首次挂载且未初始化时执行 useEffect(() => { if (!hasInitialized.current && allCachedEvents.length === 0) { @@ -107,20 +136,37 @@ const DynamicNewsCard = forwardRef(({ } }, [dispatch, allCachedEvents.length]); - // 默认选中第一个事件 - 只在当前选中的事件不在当前页时重置 + // 自动选中逻辑 - 只在首次加载时自动选中第一个事件,翻页时不自动选中 useEffect(() => { if (currentPageEvents.length > 0) { - // 检查当前选中的事件是否在当前页中 + // 情况1: 首次加载 - 自动选中第一个事件并触发详情加载 + if (!hasAutoSelectedFirstEvent.current && !selectedEvent) { + console.log('%c🎯 [首次加载] 自动选中第一个事件', 'color: #10B981; font-weight: bold;'); + hasAutoSelectedFirstEvent.current = true; + setSelectedEvent(currentPageEvents[0]); + return; + } + + // 情况2: 翻页 - 如果选中的事件不在当前页,根据模式决定处理方式 const selectedEventInCurrentPage = currentPageEvents.find( e => e.id === selectedEvent?.id ); - // 如果选中的事件不在当前页,则选中当前页第一个事件 - if (!selectedEventInCurrentPage) { - setSelectedEvent(currentPageEvents[0]); + if (selectedEvent && !selectedEventInCurrentPage) { + console.log('%c📄 [翻页] 选中的事件不在当前页', 'color: #F59E0B; font-weight: bold;'); + + // 单排/双排/纵向模式:自动选中当前页第一个事件(保持详情显示) + // 四排模式:清空选中状态(不需要底部详情) + if (mode === 'carousel' || mode === 'grid' || mode === 'vertical') { + console.log('%c📄 [翻页] 自动选中当前页第一个事件', 'color: #10B981; font-weight: bold;'); + setSelectedEvent(currentPageEvents[0]); + } else { + console.log('%c📄 [翻页] 清空选中状态(四排模式)', 'color: #F59E0B; font-weight: bold;'); + setSelectedEvent(null); + } } } - }, [currentPageEvents, selectedEvent?.id]); + }, [currentPageEvents, selectedEvent?.id, mode]); // 组件卸载时清理选中状态 useEffect(() => { @@ -169,6 +215,10 @@ const DynamicNewsCard = forwardRef(({ {currentPageEvents && currentPageEvents.length > 0 ? ( )} - {/* 详情面板 - 始终显示(如果有选中事件) */} - {currentPageEvents && currentPageEvents.length > 0 && selectedEvent && ( + {/* 详情面板 - 仅在单排/双排模式下显示(四排模式不显示,纵向模式已在右侧显示) */} + {currentPageEvents && currentPageEvents.length > 0 && selectedEvent && + (mode === 'carousel' || mode === 'grid') && ( )} + + {/* 四排模式详情弹窗 - 未打开时不渲染 */} + {isModalOpen && ( + + + + + {modalEvent?.title || '事件详情'} + + + + {modalEvent && } + + + + )} ); }); diff --git a/src/views/Community/components/DynamicNewsCard/EventScrollList.js b/src/views/Community/components/DynamicNewsCard/EventScrollList.js index 969efcf2..acc48b65 100644 --- a/src/views/Community/components/DynamicNewsCard/EventScrollList.js +++ b/src/views/Community/components/DynamicNewsCard/EventScrollList.js @@ -6,6 +6,7 @@ import { Box, Flex, Grid, + GridItem, IconButton, Button, ButtonGroup, @@ -18,7 +19,11 @@ import { } from '@chakra-ui/react'; import { ChevronLeftIcon, ChevronRightIcon } from '@chakra-ui/icons'; import DynamicNewsEventCard from '../EventCard/DynamicNewsEventCard'; +import CompactEventCard from '../EventCard/CompactEventCard'; +import HorizontalDynamicNewsEventCard from '../EventCard/HorizontalDynamicNewsEventCard'; +import DynamicNewsDetailPanel from '../DynamicNewsDetail'; import PaginationControl from './PaginationControl'; +import VirtualizedFourRowGrid from './VirtualizedFourRowGrid'; /** * 事件列表组件 - 支持两种展示模式 @@ -39,6 +44,10 @@ import PaginationControl from './PaginationControl'; */ const EventScrollList = ({ events, + displayEvents, // 累积显示的事件列表(四排模式用) + isAccumulateMode, // 是否累积模式 + loadNextPage, // 加载下一页(无限滚动) + onFourRowEventClick, // 四排模式事件点击回调(打开弹窗) selectedEvent, onEventSelect, borderColor, @@ -88,7 +97,7 @@ const EventScrollList = ({ {/* 模式切换按钮 */} - */} + + @@ -126,7 +149,10 @@ const EventScrollList = ({ top="50%" transform="translateY(-50%)" zIndex={2} - onClick={() => onPageChange(currentPage - 1)} + onClick={() => { + console.log(`%c🔵 [翻页] 点击上一页: 当前页${currentPage} → 目标页${currentPage - 1} (共${totalPages}页)`, 'color: #3B82F6; font-weight: bold;'); + onPageChange(currentPage - 1); + }} variant="ghost" size="md" w="40px" @@ -146,7 +172,7 @@ const EventScrollList = ({ )} {/* 右侧翻页按钮 - 下一页 */} - {currentPage < totalPages && hasMore && ( + {currentPage < totalPages && ( } position="absolute" @@ -154,7 +180,10 @@ const EventScrollList = ({ top="50%" transform="translateY(-50%)" zIndex={2} - onClick={() => onPageChange(currentPage + 1)} + onClick={() => { + console.log(`%c🔵 [翻页] 点击下一页: 当前页${currentPage} → 目标页${currentPage + 1} (共${totalPages}页)`, 'color: #3B82F6; font-weight: bold;'); + onPageChange(currentPage + 1); + }} variant="ghost" size="md" w="40px" @@ -168,7 +197,7 @@ const EventScrollList = ({ boxShadow: '0 4px 12px rgba(0, 0, 0, 0.2)', transform: 'translateY(-50%) scale(1.05)' }} - isDisabled={currentPage >= totalPages && !hasMore} + isDisabled={currentPage >= totalPages} aria-label="下一页" title="下一页" /> @@ -178,13 +207,16 @@ const EventScrollList = ({ - {/* 加载遮罩 */} - {loading && ( + {/* 加载遮罩 - 四排和纵向模式不显示(使用底部 loading 指示器) */} + {loading && mode !== 'four-row' && mode !== 'vertical' && (
)} + + {/* 模式3: 四排网格模式 - 使用虚拟滚动 + 无限滚动 */} + {mode === 'four-row' && ( + + )} + + {/* 模式4: 纵向分栏模式 - 横向布局(时间在左,卡片在右) */} + {mode === 'vertical' && ( + + {/* 左侧:事件列表 (33.3%) - 使用虚拟滚动 + 无限滚动 */} + + + + + {/* 右侧:事件详情 (66.7%) */} + + + {selectedEvent ? ( + + ) : ( +
+ + + 请选择左侧事件查看详情 + + +
+ )} +
+
+
+ )} diff --git a/src/views/Community/components/DynamicNewsCard/constants.js b/src/views/Community/components/DynamicNewsCard/constants.js index af511623..5aad3ab3 100644 --- a/src/views/Community/components/DynamicNewsCard/constants.js +++ b/src/views/Community/components/DynamicNewsCard/constants.js @@ -19,7 +19,7 @@ export const DISPLAY_MODES = { VERTICAL: 'vertical', // 纵向分栏模式 }; -export const DEFAULT_MODE = DISPLAY_MODES.CAROUSEL; +export const DEFAULT_MODE = DISPLAY_MODES.VERTICAL; // ========== Toast 提示配置 ========== export const TOAST_CONFIG = {