update ui

This commit is contained in:
2025-11-13 17:31:06 +08:00
parent 4bb37c6e6d
commit 0e32076e71

View File

@@ -80,18 +80,10 @@ const DynamicNewsCard = forwardRef(({
// 通知权限相关 // 通知权限相关
const { browserPermission, requestBrowserPermission } = useNotification(); const { browserPermission, requestBrowserPermission } = useNotification();
// 固定模式状态 // Refs
const [isFixedMode, setIsFixedMode] = useState(false);
const [headerHeight, setHeaderHeight] = useState(0);
const cardHeaderRef = useRef(null); const cardHeaderRef = useRef(null);
const cardBodyRef = useRef(null); const cardBodyRef = useRef(null);
// 导航栏和页脚固定高度
const NAVBAR_HEIGHT = 64; // 主导航高度
const SECONDARY_NAV_HEIGHT = 44; // 二级导航高度
const FOOTER_HEIGHT = 80; // 页脚高度(优化后)
const TOTAL_NAV_HEIGHT = NAVBAR_HEIGHT + SECONDARY_NAV_HEIGHT; // 总导航高度 128px
// 从 Redux 读取关注状态 // 从 Redux 读取关注状态
const eventFollowStatus = useSelector(selectEventFollowStatus); const eventFollowStatus = useSelector(selectEventFollowStatus);
@@ -387,118 +379,7 @@ const [currentMode, setCurrentMode] = useState('vertical');
}, [handlePageChange]); }, [handlePageChange]);
// 测量 CardHeader 高度 // 测量 CardHeader 高度
useEffect(() => { // 固定模式逻辑已移除,改用 sticky 定位
const cardHeaderElement = cardHeaderRef.current;
if (!cardHeaderElement) return;
// 测量并更新高度
const updateHeaderHeight = () => {
const height = cardHeaderElement.offsetHeight;
setHeaderHeight(height);
};
// 初始测量
updateHeaderHeight();
// 监听窗口大小变化(响应式调整)
window.addEventListener('resize', updateHeaderHeight);
return () => {
window.removeEventListener('resize', updateHeaderHeight);
};
}, []);
// 监听 CardHeader 是否到达触发点,动态切换固定模式
useEffect(() => {
const cardHeaderElement = cardHeaderRef.current;
const cardBodyElement = cardBodyRef.current;
if (!cardHeaderElement || !cardBodyElement) return;
let ticking = false;
const TRIGGER_OFFSET = 150; // 提前 150px 触发(进入固定模式)
const EXIT_OFFSET = 200; // 提前 200px 退出(退出比进入更容易)
const EXIT_THRESHOLD = 30; // 接近顶部 30px 内即可退出
// 外部滚动监听:触发固定模式
const handleExternalScroll = () => {
// 只在非固定模式下监听外部滚动
if (!isFixedMode && !ticking) {
window.requestAnimationFrame(() => {
// 获取 CardHeader 相对视口的位置
const rect = cardHeaderElement.getBoundingClientRect();
const elementTop = rect.top;
// 计算触发点:总导航高度 + 150px 偏移量
const triggerPoint = TOTAL_NAV_HEIGHT + TRIGGER_OFFSET;
// 向上滑动:元素顶部到达触发点 → 激活固定模式
if (elementTop <= triggerPoint) {
setIsFixedMode(true);
console.log('🔒 切换为固定全屏模式', {
elementTop,
triggerPoint,
offset: TRIGGER_OFFSET
});
}
ticking = false;
});
ticking = true;
}
};
// 内部滚动监听:退出固定模式
const handleWheel = (e) => {
// 只在固定模式下监听内部滚动
if (!isFixedMode) return;
// 检测向上滚动deltaY < 0
if (e.deltaY < 0) {
window.requestAnimationFrame(() => {
// 🎯 检查 1CardHeader 位置(主要条件)
const rect = cardHeaderElement.getBoundingClientRect();
const elementTop = rect.top;
const exitPoint = TOTAL_NAV_HEIGHT + EXIT_OFFSET;
// 🎯 检查 2左侧事件列表滚动位置辅助条件
const eventListContainers = cardBodyElement.querySelectorAll('[data-event-list-container]');
const allNearTop = eventListContainers.length === 0 ||
Array.from(eventListContainers).every(
container => container.scrollTop <= EXIT_THRESHOLD
);
// 🎯 退出条件CardHeader 超过退出点 OR 左侧列表接近顶部
if (elementTop > exitPoint || allNearTop) {
setIsFixedMode(false);
console.log('🔓 恢复正常文档流模式', {
elementTop,
exitPoint,
listNearTop: allNearTop,
exitThreshold: EXIT_THRESHOLD,
reason: elementTop > exitPoint ? 'CardHeader位置' : '左侧列表滚动'
});
}
});
}
};
// 监听外部滚动
window.addEventListener('scroll', handleExternalScroll, { passive: true });
// 监听内部滚轮事件(固定模式下)
if (isFixedMode) {
cardBodyElement.addEventListener('wheel', handleWheel, { passive: true });
}
// 初次检查位置
handleExternalScroll();
return () => {
window.removeEventListener('scroll', handleExternalScroll);
cardBodyElement.removeEventListener('wheel', handleWheel);
};
}, [isFixedMode]);
return ( return (
<Card <Card
@@ -507,19 +388,14 @@ const [currentMode, setCurrentMode] = useState('vertical');
bg={cardBg} bg={cardBg}
borderColor={borderColor} borderColor={borderColor}
mb={4} mb={4}
position="relative"
zIndex={1}
> >
{/* 标题部分 */} {/* 标题和搜索部分 - 可滚动区域 */}
<CardHeader <CardHeader
ref={cardHeaderRef} ref={cardHeaderRef}
position={isFixedMode ? 'fixed' : 'relative'} position="relative"
top={isFixedMode ? `${TOTAL_NAV_HEIGHT}px` : 'auto'} zIndex={1}
left={isFixedMode ? 0 : 'auto'}
right={isFixedMode ? 0 : 'auto'}
maxW={isFixedMode ? '1600px' : '100%'}
mx={isFixedMode ? 'auto' : 0}
px={isFixedMode ? { base: 3, md: 4 } : undefined}
zIndex={isFixedMode ? 999 : 1}
bg={cardBg}
> >
<Flex justify="space-between" align="center"> <Flex justify="space-between" align="center">
<VStack align="start" spacing={1}> <VStack align="start" spacing={1}>
@@ -613,36 +489,41 @@ const [currentMode, setCurrentMode] = useState('vertical');
{/* 主体内容 */} {/* 主体内容 */}
<CardBody <CardBody
ref={cardBodyRef} ref={cardBodyRef}
position={isFixedMode ? 'fixed' : 'relative'} position="relative"
top={isFixedMode ? `${TOTAL_NAV_HEIGHT + headerHeight}px` : 'auto'}
left={isFixedMode ? 0 : 'auto'}
right={isFixedMode ? 0 : 'auto'}
bottom={isFixedMode ? `${FOOTER_HEIGHT}px` : 'auto'}
maxW={isFixedMode ? '1600px' : '100%'}
mx={isFixedMode ? 'auto' : 0}
h={isFixedMode ? `calc(100vh - ${TOTAL_NAV_HEIGHT + headerHeight + FOOTER_HEIGHT}px)` : 'auto'}
px={isFixedMode ? { base: 3, md: 4 } : undefined}
pt={4} pt={4}
display="flex" display="flex"
flexDirection="column" flexDirection="column"
overflow="hidden" overflow="visible"
zIndex={isFixedMode ? 1000 : 1} zIndex={1}
bg={cardBg}
> >
{/* 顶部控制栏:模式切换按钮 + 筛选按钮 + 分页控制器(固定不滚动 */} {/* 顶部控制栏:模式切换按钮 + 分页控制器(滚动时固定在顶部 */}
<Flex justify="space-between" align="center" mb={2} flexShrink={0}> <Box
{/* 左侧:模式切换按钮 + 筛选按钮 */} position="sticky"
<ModeToggleButtons mode={mode} onModeChange={handleModeToggle} /> top="0"
zIndex={10}
bg={cardBg}
py={2}
mb={2}
borderBottom="1px solid"
borderColor={borderColor}
mx={-6}
px={6}
boxShadow="sm"
>
<Flex justify="space-between" align="center">
{/* 左侧:模式切换按钮 */}
<ModeToggleButtons mode={mode} onModeChange={handleModeToggle} />
{/* 右侧:分页控制器(仅在纵向模式显示) */} {/* 右侧:分页控制器(仅在纵向模式显示) */}
{mode === 'vertical' && totalPages > 1 && ( {mode === 'vertical' && totalPages > 1 && (
<PaginationControl <PaginationControl
currentPage={currentPage} currentPage={currentPage}
totalPages={totalPages} totalPages={totalPages}
onPageChange={handlePageChangeWithScroll} onPageChange={handlePageChangeWithScroll}
/> />
)} )}
</Flex> </Flex>
</Box>
{/* 内容区域 - 撑满剩余高度 */} {/* 内容区域 - 撑满剩余高度 */}
<Box flex="1" minH={0} position="relative"> <Box flex="1" minH={0} position="relative">