pref: UI优化
This commit is contained in:
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
{/* 右侧:事件详情 */}
|
{/* 右侧:事件详情 */}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user