pref: UI优化

This commit is contained in:
zdl
2025-11-06 11:35:10 +08:00
parent 116594d9b1
commit 5ff8db8899
4 changed files with 31 additions and 38 deletions

View File

@@ -198,11 +198,14 @@ const DynamicNewsCard = forwardRef(({
// 初始加载 - 只在组件首次挂载且对应模式数据为空时执行 // 初始加载 - 只在组件首次挂载且对应模式数据为空时执行
useEffect(() => { useEffect(() => {
// 添加防抖:如果已经初始化,不再执行
if (hasInitialized.current) return;
const isDataEmpty = currentMode === 'vertical' const isDataEmpty = currentMode === 'vertical'
? Object.keys(allCachedEventsByPage || {}).length === 0 ? Object.keys(allCachedEventsByPage || {}).length === 0
: (allCachedEvents?.length || 0) === 0; : (allCachedEvents?.length || 0) === 0;
if (!hasInitialized.current && isDataEmpty) { if (isDataEmpty) {
hasInitialized.current = true; hasInitialized.current = true;
dispatch(fetchDynamicNews({ dispatch(fetchDynamicNews({
mode: mode, // 传递当前模式 mode: mode, // 传递当前模式
@@ -213,7 +216,7 @@ const DynamicNewsCard = forwardRef(({
page: PAGINATION_CONFIG.INITIAL_PAGE, // 然后覆盖 page 参数 page: PAGINATION_CONFIG.INITIAL_PAGE, // 然后覆盖 page 参数
})); }));
} }
}, [dispatch, allCachedEventsByPage, allCachedEvents, currentMode, mode, pageSize]); // 移除 filters 依赖,避免重复触发 }, [dispatch, currentMode, mode, pageSize]); // 移除 allCachedEventsByPage, allCachedEvents 依赖,避免数据更新触发重复请求
// 监听筛选条件变化 - 清空缓存并重新请求数据 // 监听筛选条件变化 - 清空缓存并重新请求数据
useEffect(() => { useEffect(() => {
@@ -383,10 +386,8 @@ const DynamicNewsCard = forwardRef(({
totalPages={totalPages} totalPages={totalPages}
onPageChange={handlePageChange} onPageChange={handlePageChange}
loading={loadingPage !== null} loading={loadingPage !== null}
loadingPage={loadingPage}
error={error} error={error}
mode={mode} mode={mode}
onModeChange={handleModeToggle}
eventFollowStatus={eventFollowStatus} eventFollowStatus={eventFollowStatus}
onToggleFollow={handleToggleFollow} onToggleFollow={handleToggleFollow}
hasMore={hasMore} hasMore={hasMore}

View File

@@ -12,6 +12,10 @@ import VerticalModeLayout from './VerticalModeLayout';
/** /**
* 事件列表组件 - 支持纵向和平铺两种展示模式 * 事件列表组件 - 支持纵向和平铺两种展示模式
* @param {Array} events - 当前页的事件列表(服务端已分页) * @param {Array} events - 当前页的事件列表(服务端已分页)
* @param {Array} displayEvents - 累积显示的事件列表(平铺模式用)
* @param {Function} loadNextPage - 加载下一页(无限滚动)
* @param {Function} loadPrevPage - 加载上一页(双向无限滚动)
* @param {Function} onFourRowEventClick - 平铺模式事件点击回调(打开弹窗)
* @param {Object} selectedEvent - 当前选中的事件 * @param {Object} selectedEvent - 当前选中的事件
* @param {Function} onEventSelect - 事件选择回调 * @param {Function} onEventSelect - 事件选择回调
* @param {string} borderColor - 边框颜色 * @param {string} borderColor - 边框颜色
@@ -19,19 +23,18 @@ import VerticalModeLayout from './VerticalModeLayout';
* @param {number} totalPages - 总页数(由服务端返回) * @param {number} totalPages - 总页数(由服务端返回)
* @param {Function} onPageChange - 页码改变回调 * @param {Function} onPageChange - 页码改变回调
* @param {boolean} loading - 全局加载状态 * @param {boolean} loading - 全局加载状态
* @param {number|null} loadingPage - 正在加载的目标页码(用于显示"正在加载第X页..." * @param {Object} error - 错误状态
* @param {string} mode - 展示模式:'vertical'(纵向分栏)| 'four-row'(平铺网格) * @param {string} mode - 展示模式:'vertical'(纵向分栏)| 'four-row'(平铺网格)
* @param {Function} onModeChange - 模式切换回调
* @param {boolean} hasMore - 是否还有更多数据 * @param {boolean} hasMore - 是否还有更多数据
* @param {Object} eventFollowStatus - 事件关注状态 { [eventId]: { isFollowing, followerCount } } * @param {Object} eventFollowStatus - 事件关注状态 { [eventId]: { isFollowing, followerCount } }
* @param {Function} onToggleFollow - 关注按钮回调 * @param {Function} onToggleFollow - 关注按钮回调
*/ */
const EventScrollList = ({ const EventScrollList = ({
events, events,
displayEvents, // 累积显示的事件列表(四排模式用) displayEvents,
loadNextPage, // 加载下一页(无限滚动) loadNextPage,
loadPrevPage, // 加载上一页(双向无限滚动) loadPrevPage,
onFourRowEventClick, // 四排模式事件点击回调(打开弹窗) onFourRowEventClick,
selectedEvent, selectedEvent,
onEventSelect, onEventSelect,
borderColor, borderColor,
@@ -39,9 +42,8 @@ const EventScrollList = ({
totalPages, totalPages,
onPageChange, onPageChange,
loading = false, loading = false,
error, // 错误状态 error,
mode = 'vertical', mode = 'vertical',
onModeChange,
hasMore = true, hasMore = true,
eventFollowStatus = {}, eventFollowStatus = {},
onToggleFollow onToggleFollow
@@ -126,7 +128,7 @@ const EventScrollList = ({
{/* 纵向分栏模式 */} {/* 纵向分栏模式 */}
<VerticalModeLayout <VerticalModeLayout
display={mode === 'vertical' ? 'block' : 'none'} display={mode === 'vertical' ? 'flex' : 'none'}
events={events} events={events}
selectedEvent={selectedEvent} selectedEvent={selectedEvent}
onEventSelect={onEventSelect} onEventSelect={onEventSelect}
@@ -134,9 +136,6 @@ const EventScrollList = ({
onToggleFollow={onToggleFollow} onToggleFollow={onToggleFollow}
getTimelineBoxStyle={getTimelineBoxStyle} getTimelineBoxStyle={getTimelineBoxStyle}
borderColor={borderColor} borderColor={borderColor}
currentPage={currentPage}
totalPages={totalPages}
onPageChange={onPageChange}
/> />
</Box> </Box>
); );

View File

@@ -6,7 +6,6 @@ import { Box, IconButton, Tooltip, VStack, Flex } from '@chakra-ui/react';
import { ViewIcon, ViewOffIcon } from '@chakra-ui/icons'; import { ViewIcon, ViewOffIcon } from '@chakra-ui/icons';
import HorizontalDynamicNewsEventCard from '../EventCard/HorizontalDynamicNewsEventCard'; import HorizontalDynamicNewsEventCard from '../EventCard/HorizontalDynamicNewsEventCard';
import EventDetailScrollPanel from './EventDetailScrollPanel'; import EventDetailScrollPanel from './EventDetailScrollPanel';
import PaginationControl from './PaginationControl';
/** /**
* 纵向分栏模式布局 * 纵向分栏模式布局
@@ -14,8 +13,7 @@ import PaginationControl from './PaginationControl';
* - detail默认左侧事件列表 1fr | 右侧详情 2fr * - detail默认左侧事件列表 1fr | 右侧详情 2fr
* - list左侧事件列表 7fr | 右侧详情 300px * - list左侧事件列表 7fr | 右侧详情 300px
* *
* 左侧使用分页模式,高度根据内容自适应,分页控制器在左侧列表底部 * @param {string} display - CSS display 属性(用于显示/隐藏组件)
*
* @param {Array} events - 当前页的事件列表(分页数据) * @param {Array} events - 当前页的事件列表(分页数据)
* @param {Object} selectedEvent - 当前选中的事件 * @param {Object} selectedEvent - 当前选中的事件
* @param {Function} onEventSelect - 事件选择回调 * @param {Function} onEventSelect - 事件选择回调
@@ -23,11 +21,9 @@ import PaginationControl from './PaginationControl';
* @param {Function} onToggleFollow - 关注按钮回调 * @param {Function} onToggleFollow - 关注按钮回调
* @param {Function} getTimelineBoxStyle - 时间线样式获取函数 * @param {Function} getTimelineBoxStyle - 时间线样式获取函数
* @param {string} borderColor - 边框颜色 * @param {string} borderColor - 边框颜色
* @param {number} currentPage - 当前页码
* @param {number} totalPages - 总页数
* @param {Function} onPageChange - 页码改变回调
*/ */
const VerticalModeLayout = ({ const VerticalModeLayout = ({
display = 'flex',
events, events,
selectedEvent, selectedEvent,
onEventSelect, onEventSelect,
@@ -35,9 +31,6 @@ const VerticalModeLayout = ({
onToggleFollow, onToggleFollow,
getTimelineBoxStyle, getTimelineBoxStyle,
borderColor, borderColor,
currentPage,
totalPages,
onPageChange,
}) => { }) => {
// 布局模式状态:'detail' = 聚焦详情(默认),'list' = 聚焦列表 // 布局模式状态:'detail' = 聚焦详情(默认),'list' = 聚焦列表
const [layoutMode, setLayoutMode] = useState('list'); const [layoutMode, setLayoutMode] = useState('list');
@@ -53,6 +46,7 @@ const VerticalModeLayout = ({
return ( return (
<Flex <Flex
display={display}
gap={6} gap={6}
position="relative" position="relative"
transition="all 0.3s ease-in-out" transition="all 0.3s ease-in-out"
@@ -80,17 +74,6 @@ const VerticalModeLayout = ({
/> />
))} ))}
</VStack> </VStack>
{/* 分页控制器 */}
{totalPages > 1 && (
<Flex justify="center" py={3} px={2}>
<PaginationControl
currentPage={currentPage}
totalPages={totalPages}
onPageChange={onPageChange}
/>
</Flex>
)}
</Box> </Box>
{/* 右侧:事件详情 */} {/* 右侧:事件详情 */}

View File

@@ -11,6 +11,7 @@ import DynamicNewsEventCard from '../EventCard/DynamicNewsEventCard';
/** /**
* 虚拟化网格组件(支持多列布局 + 无限滚动) * 虚拟化网格组件(支持多列布局 + 无限滚动)
* @param {Object} props * @param {Object} props
* @param {string} props.display - CSS display 属性(用于显示/隐藏组件)
* @param {Array} props.events - 事件列表(累积显示) * @param {Array} props.events - 事件列表(累积显示)
* @param {number} props.columnsPerRow - 每行列数(默认 4单列模式传 1 * @param {number} props.columnsPerRow - 每行列数(默认 4单列模式传 1
* @param {React.Component} props.CardComponent - 卡片组件(默认 DynamicNewsEventCard * @param {React.Component} props.CardComponent - 卡片组件(默认 DynamicNewsEventCard
@@ -25,6 +26,7 @@ import DynamicNewsEventCard from '../EventCard/DynamicNewsEventCard';
* @param {boolean} props.loading - 加载状态 * @param {boolean} props.loading - 加载状态
*/ */
const VirtualizedFourRowGrid = ({ const VirtualizedFourRowGrid = ({
display = 'block',
events, events,
columnsPerRow = 4, columnsPerRow = 4,
CardComponent = DynamicNewsEventCard, CardComponent = DynamicNewsEventCard,
@@ -92,6 +94,9 @@ const VirtualizedFourRowGrid = ({
* - 虚拟滚动缓存(渲染层):@tanstack/react-virtual 只渲染可见行,复用 DOM 节点 * - 虚拟滚动缓存(渲染层):@tanstack/react-virtual 只渲染可见行,复用 DOM 节点
*/ */
useEffect(() => { useEffect(() => {
// 如果组件被隐藏,不执行滚动监听
if (display === 'none') return;
const scrollElement = parentRef.current; const scrollElement = parentRef.current;
if (!scrollElement) return; if (!scrollElement) return;
@@ -136,7 +141,7 @@ const VirtualizedFourRowGrid = ({
scrollElement.addEventListener('scroll', handleScroll); scrollElement.addEventListener('scroll', handleScroll);
return () => scrollElement.removeEventListener('scroll', handleScroll); return () => scrollElement.removeEventListener('scroll', handleScroll);
}, [loadNextPage, onRefreshFirstPage, hasMore, loading]); }, [display, loadNextPage, onRefreshFirstPage, hasMore, loading]);
/** /**
* 【核心逻辑2】主动检测内容高度 - 确保内容始终填满容器 * 【核心逻辑2】主动检测内容高度 - 确保内容始终填满容器
@@ -155,6 +160,9 @@ const VirtualizedFourRowGrid = ({
* - 监听 events.length 变化:新数据加载后重新检查 * - 监听 events.length 变化:新数据加载后重新检查
*/ */
useEffect(() => { useEffect(() => {
// 如果组件被隐藏,不执行高度检测
if (display === 'none') return;
const scrollElement = parentRef.current; const scrollElement = parentRef.current;
if (!scrollElement || !loadNextPage) return; if (!scrollElement || !loadNextPage) return;
@@ -180,7 +188,7 @@ const VirtualizedFourRowGrid = ({
}, 500); }, 500);
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, [events.length, hasMore, loading, loadNextPage]); }, [display, events.length, hasMore, loading, loadNextPage]);
// 错误指示器(同行显示) // 错误指示器(同行显示)
const renderErrorIndicator = () => { const renderErrorIndicator = () => {
@@ -244,8 +252,10 @@ const VirtualizedFourRowGrid = ({
return ( return (
<Box <Box
ref={parentRef} ref={parentRef}
display={display}
overflowY="auto" overflowY="auto"
overflowX="hidden" overflowX="hidden"
minH="800px"
maxH="800px" maxH="800px"
w="100%" w="100%"
position="relative" position="relative"