Files
vf_react/src/views/Community/components/DynamicNewsCard/EventScrollList.js
zdl 52e88e5005 refactor: 提取 ModeToggleButtons 为独立子组件
问题:
- EventScrollList 组件中模式切换按钮代码内联(17行)
- 降低代码可读性和可维护性
- 按钮组无法在其他地方复用

修改:
1. 新建 ModeToggleButtons.js 独立组件
   - 接收 mode 和 onModeChange 两个 props
   - 包含完整的 JSDoc 注释
   - 支持 vertical(纵向)和 four-row(平铺)两种模式

2. 重构 EventScrollList.js
   - 删除未使用的 import(Button, ButtonGroup)
   - 导入 ModeToggleButtons 组件
   - 替换 17 行内联代码为 1 行组件调用
   - 代码净减少 14 行

效果:
-  职责分离:模式切换逻辑独立封装
-  可复用性:其他页面可直接导入使用
-  易维护性:修改按钮样式只需改一个文件
-  易测试性:可单独编写单元测试
-  代码简洁:EventScrollList 更简洁易读

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-05 13:44:34 +08:00

239 lines
8.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// src/views/Community/components/DynamicNewsCard/EventScrollList.js
// 横向滚动事件列表组件
import React, { useRef } from 'react';
import {
Box,
Flex,
Grid,
GridItem,
Center,
VStack,
Text,
useColorModeValue
} from '@chakra-ui/react';
import HorizontalDynamicNewsEventCard from '../EventCard/HorizontalDynamicNewsEventCard';
import DynamicNewsDetailPanel from '../DynamicNewsDetail';
import PaginationControl from './PaginationControl';
import VirtualizedFourRowGrid from './VirtualizedFourRowGrid';
import PageNavigationButton from './PageNavigationButton';
import ModeToggleButtons from './ModeToggleButtons';
/**
* 事件列表组件 - 支持纵向和平铺两种展示模式
* @param {Array} events - 当前页的事件列表(服务端已分页)
* @param {Object} selectedEvent - 当前选中的事件
* @param {Function} onEventSelect - 事件选择回调
* @param {string} borderColor - 边框颜色
* @param {number} currentPage - 当前页码
* @param {number} totalPages - 总页数(由服务端返回)
* @param {Function} onPageChange - 页码改变回调
* @param {boolean} loading - 全局加载状态
* @param {number|null} loadingPage - 正在加载的目标页码(用于显示"正在加载第X页..."
* @param {string} mode - 展示模式:'vertical'(纵向分栏)| 'four-row'(平铺网格)
* @param {Function} onModeChange - 模式切换回调
* @param {boolean} hasMore - 是否还有更多数据
* @param {Object} eventFollowStatus - 事件关注状态 { [eventId]: { isFollowing, followerCount } }
* @param {Function} onToggleFollow - 关注按钮回调
*/
const EventScrollList = ({
events,
displayEvents, // 累积显示的事件列表(四排模式用)
isAccumulateMode, // 是否累积模式
loadNextPage, // 加载下一页(无限滚动)
loadPrevPage, // 加载上一页(双向无限滚动)
onFourRowEventClick, // 四排模式事件点击回调(打开弹窗)
selectedEvent,
onEventSelect,
borderColor,
currentPage,
totalPages,
onPageChange,
loading = false,
mode = 'vertical',
onModeChange,
hasMore = true,
eventFollowStatus = {},
onToggleFollow
}) => {
const scrollContainerRef = useRef(null);
// 所有 useColorModeValue 必须在组件顶层调用(不能在条件渲染中)
const timelineBg = useColorModeValue('gray.50', 'gray.700');
const timelineBorderColor = useColorModeValue('gray.400', 'gray.500');
const timelineTextColor = useColorModeValue('blue.600', 'blue.400');
// 滚动条颜色
const scrollbarTrackBg = useColorModeValue('#f1f1f1', '#2D3748');
const scrollbarThumbBg = useColorModeValue('#888', '#4A5568');
const scrollbarThumbHoverBg = useColorModeValue('#555', '#718096');
const getTimelineBoxStyle = () => {
return {
bg: timelineBg,
borderColor: timelineBorderColor,
borderWidth: '2px',
textColor: timelineTextColor,
boxShadow: 'sm',
};
};
return (
<Box>
{/* 顶部控制栏:模式切换按钮(左)+ 分页控制器(右) */}
<Flex justify="space-between" align="center" mb={2}>
{/* 模式切换按钮 */}
<ModeToggleButtons mode={mode} onModeChange={onModeChange} />
{/* 分页控制器(平铺模式不显示,使用无限滚动) */}
{totalPages > 1 && mode !== 'four-row' && (
<PaginationControl
currentPage={currentPage}
totalPages={totalPages}
onPageChange={onPageChange}
/>
)}
</Flex>
{/* 横向滚动区域 */}
<Box position="relative">
{/* 翻页导航按钮(平铺模式不显示,使用无限滚动) */}
{mode !== 'four-row' && (
<>
<PageNavigationButton
direction="prev"
currentPage={currentPage}
totalPages={totalPages}
onPageChange={onPageChange}
mode={mode}
/>
<PageNavigationButton
direction="next"
currentPage={currentPage}
totalPages={totalPages}
onPageChange={onPageChange}
mode={mode}
/>
</>
)}
{/* 事件卡片容器 */}
<Box
ref={scrollContainerRef}
overflowX="hidden"
overflowY="hidden"
maxH={mode === 'vertical' ? '820px' : 'none'}
pt={0}
pb={4}
px={2}
position="relative"
css={{
// 统一滚动条样式(支持横向和纵向)
'&::-webkit-scrollbar': {
width: '1px',
height: '1px',
},
'&::-webkit-scrollbar-track': {
background: scrollbarTrackBg,
borderRadius: '10px',
},
'&::-webkit-scrollbar-thumb': {
background: scrollbarThumbBg,
borderRadius: '10px',
},
'&::-webkit-scrollbar-thumb:hover': {
background: scrollbarThumbHoverBg,
},
scrollBehavior: 'smooth',
WebkitOverflowScrolling: 'touch',
}}
>
{/* 平铺网格模式 - 使用虚拟滚动 + 双向无限滚动 */}
{mode === 'four-row' && (
<VirtualizedFourRowGrid
events={displayEvents || events} // 使用累积列表(如果有)
selectedEvent={selectedEvent}
onEventSelect={onFourRowEventClick} // 四排模式点击打开弹窗
eventFollowStatus={eventFollowStatus}
onToggleFollow={onToggleFollow}
getTimelineBoxStyle={getTimelineBoxStyle}
borderColor={borderColor}
loadNextPage={loadNextPage} // 加载下一页
loadPrevPage={loadPrevPage} // 加载上一页(双向滚动)
hasMore={hasMore} // 是否还有更多数据
loading={loading} // 加载状态
/>
)}
{/* 模式4: 纵向分栏模式 - 横向布局(时间在左,卡片在右) */}
{mode === 'vertical' && (
<Grid templateColumns="1fr 2fr" gap={6} h="800px">
{/* 左侧:事件列表 (33.3%) - 使用虚拟滚动 + 双向无限滚动 */}
<GridItem>
<VirtualizedFourRowGrid
events={displayEvents || events} // 使用累积列表
columnsPerRow={1} // 单列布局
CardComponent={HorizontalDynamicNewsEventCard} // 使用横向卡片
selectedEvent={selectedEvent}
onEventSelect={onEventSelect}
eventFollowStatus={eventFollowStatus}
onToggleFollow={onToggleFollow}
getTimelineBoxStyle={getTimelineBoxStyle}
borderColor={borderColor}
loadNextPage={loadNextPage} // 支持向下无限滚动
loadPrevPage={loadPrevPage} // 支持向上无限滚动(双向滚动)
hasMore={hasMore}
loading={loading}
/>
</GridItem>
{/* 右侧:事件详情 (66.7%) */}
<GridItem h="100%">
<Box
pl={2}
position="relative"
sx={{
height: '100% !important',
maxHeight: '800px !important',
overflowY: 'scroll !important',
overflowX: 'hidden !important',
'&::-webkit-scrollbar': {
width: '3px',
},
'&::-webkit-scrollbar-track': {
background: scrollbarTrackBg,
borderRadius: '10px',
},
'&::-webkit-scrollbar-thumb': {
background: scrollbarThumbBg,
borderRadius: '10px',
},
'&::-webkit-scrollbar-thumb:hover': {
background: scrollbarThumbHoverBg,
},
}}
>
{selectedEvent ? (
<DynamicNewsDetailPanel event={selectedEvent} />
) : (
<Center h="100%" minH="400px">
<VStack spacing={4}>
<Text fontSize="lg" color="gray.500">
请选择左侧事件查看详情
</Text>
</VStack>
</Center>
)}
</Box>
</GridItem>
</Grid>
)}
</Box>
</Box>
</Box>
);
};
export default EventScrollList;