community增加事件详情

This commit is contained in:
2026-01-06 18:25:48 +08:00
parent 19952ef2d9
commit 4a1f2d676c
3 changed files with 119 additions and 5 deletions

View File

@@ -7,11 +7,15 @@ import {
Badge,
Text,
Icon,
IconButton,
Tooltip,
useColorModeValue,
} from '@chakra-ui/react';
import { useNavigate } from 'react-router-dom';
import { EventFollowButton } from '@views/Community/components/EventCard/atoms';
import { Eye } from 'lucide-react';
import { Eye, ExternalLink } from 'lucide-react';
import ShareButton from '@components/ShareButton';
import { getEventDetailUrl } from '@utils/idEncoder';
/**
* 精简信息栏组件
@@ -26,9 +30,17 @@ import ShareButton from '@components/ShareButton';
* @param {Function} props.onToggleFollow - 切换关注回调
*/
const CompactMetaBar = ({ event, importance, isFollowing, followerCount, onToggleFollow }) => {
const navigate = useNavigate();
const viewCountBg = useColorModeValue('white', 'gray.700');
const viewCountTextColor = useColorModeValue('gray.600', 'gray.300');
// 跳转到事件详情页
const handleViewDetail = () => {
if (event?.id) {
navigate(getEventDetailUrl(event.id));
}
};
// 获取重要性文本
const getImportanceText = () => {
const levelMap = {
@@ -104,6 +116,22 @@ const CompactMetaBar = ({ event, importance, isFollowing, followerCount, onToggl
variant="icon"
size="sm"
/>
{/* 查看详情按钮 */}
<Tooltip label="查看事件详情页" hasArrow>
<IconButton
icon={<Icon as={ExternalLink} boxSize={4} />}
aria-label="查看事件详情"
size="sm"
variant="ghost"
colorScheme="blue"
onClick={handleViewDetail}
_hover={{
bg: 'blue.500',
color: 'white',
}}
/>
</Tooltip>
</HStack>
);
};

View File

@@ -42,6 +42,7 @@ import { PAGINATION_CONFIG, DISPLAY_MODES, REFRESH_DEBOUNCE_DELAY } from './cons
import { PROFESSIONAL_COLORS } from '@constants/professionalTheme';
import { debounce } from '@utils/debounce';
import { useDevice } from '@hooks/useDevice';
import { eventService } from '@services/eventService';
// 🔍 调试:渲染计数器
let dynamicNewsCardRenderCount = 0;
@@ -55,6 +56,7 @@ let dynamicNewsCardRenderCount = 0;
* @param {Function} onSearchFocus - 搜索框获得焦点回调
* @param {Function} onEventClick - 事件点击回调
* @param {Function} onViewDetail - 查看详情回调
* @param {number|null} initialEventId - 从 URL 获取的初始事件 ID用于分享链接定位
* @param {Object} trackingFunctions - PostHog 追踪函数集合
* @param {Object} ref - 用于滚动的ref
*/
@@ -66,6 +68,7 @@ const DynamicNewsCardComponent = forwardRef(({
onSearchFocus,
onEventClick,
onViewDetail,
initialEventId = null,
trackingFunctions = {},
...rest
}, ref) => {
@@ -183,6 +186,8 @@ const [currentMode, setCurrentMode] = useState('vertical');
const hasAutoSelectedFirstEvent = useRef(false);
// 追踪筛选条件 useEffect 是否是第一次渲染(避免初始加载时重复请求)
const isFirstRenderForFilters = useRef(true);
// 追踪是否已尝试从 API 获取 URL 指定的事件
const hasTriedFetchingInitialEvent = useRef(false);
// 使用分页 Hook
const {
@@ -395,6 +400,7 @@ const [currentMode, setCurrentMode] = useState('vertical');
hasInitialized.current = false;
isFirstRenderForFilters.current = true;
hasAutoSelectedFirstEvent.current = false;
hasTriedFetchingInitialEvent.current = false;
console.log('%c🧹 [卸载] 重置初始化标记', 'color: #F59E0B; font-weight: bold;');
};
}, [dispatch, mode, pageSize]); // 移除 currentMode 依赖,避免模式切换时重复请求
@@ -477,9 +483,81 @@ const [currentMode, setCurrentMode] = useState('vertical');
}
}, [mode, currentMode, allCachedEventsByPage, allCachedEvents, dispatch, filters]); // 添加 filters 依赖
// 自动选中逻辑 - 只在首次加载时自动选中第一个事件,翻页时不自动选中
// ⚡ URL 指定事件 ID 时,从 API 获取事件详情(用于分享链接直接定位)
useEffect(() => {
// 只有在有 initialEventId 且还没尝试获取过时才执行
if (!initialEventId || hasTriedFetchingInitialEvent.current) return;
// 等待数据加载完成
if (loading) return;
// 检查事件是否已在当前页数据中
const eventInCurrentPage = currentPageEvents.find(e => e.id === initialEventId);
if (eventInCurrentPage) {
// 事件在当前页,标记已尝试,让自动选中逻辑处理
hasTriedFetchingInitialEvent.current = true;
return;
}
// 事件不在当前页,从 API 获取
hasTriedFetchingInitialEvent.current = true;
console.log('%c🔍 [URL定位] 事件不在当前页,从 API 获取详情', 'color: #8B5CF6; font-weight: bold;', { initialEventId });
eventService.getEventDetail(initialEventId)
.then(response => {
if (response.success && response.data) {
console.log('%c✅ [URL定位] 成功获取事件详情', 'color: #10B981; font-weight: bold;', { event: response.data.title });
hasAutoSelectedFirstEvent.current = true;
setSelectedEvent(response.data);
// 🎯 追踪事件点击URL 定位 - API 获取)
if (trackingFunctions.trackNewsArticleClicked) {
trackingFunctions.trackNewsArticleClicked({
eventId: response.data.id,
eventTitle: response.data.title,
importance: response.data.importance,
source: 'url_param_api',
displayMode: mode,
timestamp: new Date().toISOString(),
});
}
} else {
console.log('%c⚠ [URL定位] 事件不存在或获取失败', 'color: #F59E0B;', { initialEventId });
}
})
.catch(error => {
console.error('%c❌ [URL定位] 获取事件详情失败', 'color: #EF4444;', error);
});
}, [initialEventId, loading, currentPageEvents, mode, trackingFunctions]);
// 自动选中逻辑 - 支持从 URL 参数定位到指定事件
useEffect(() => {
if (currentPageEvents.length > 0) {
// 情况0: URL 指定了初始事件 ID优先定位到该事件
if (initialEventId && !hasAutoSelectedFirstEvent.current) {
const targetEvent = currentPageEvents.find(e => e.id === initialEventId);
if (targetEvent) {
console.log('%c🎯 [URL定位] 自动选中 URL 指定的事件', 'color: #8B5CF6; font-weight: bold;', { eventId: initialEventId });
hasAutoSelectedFirstEvent.current = true;
setSelectedEvent(targetEvent);
// 🎯 追踪事件点击URL 定位)
if (trackingFunctions.trackNewsArticleClicked) {
trackingFunctions.trackNewsArticleClicked({
eventId: targetEvent.id,
eventTitle: targetEvent.title,
importance: targetEvent.importance,
source: 'url_param',
displayMode: mode,
timestamp: new Date().toISOString(),
});
}
return;
}
// 如果在当前页没找到,继续等待数据加载(不阻止首次自动选中)
console.log('%c⏳ [URL定位] 目标事件不在当前页,继续等待...', 'color: #F59E0B;', { initialEventId });
}
// 情况1: 首次加载 - 自动选中第一个事件并触发详情加载
if (!hasAutoSelectedFirstEvent.current && !selectedEvent) {
console.log('%c🎯 [首次加载] 自动选中第一个事件', 'color: #10B981; font-weight: bold;');
@@ -505,7 +583,7 @@ const [currentMode, setCurrentMode] = useState('vertical');
e => e.id === selectedEvent?.id
);
}
}, [currentPageEvents, selectedEvent?.id, mode, trackingFunctions]);
}, [currentPageEvents, selectedEvent?.id, mode, trackingFunctions, initialEventId]);
// 组件卸载时清理选中状态
useEffect(() => {

View File

@@ -1,6 +1,6 @@
// src/views/Community/index.js
import React, { useEffect, useRef, lazy, Suspense } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import React, { useEffect, useRef, lazy, Suspense, useMemo } from 'react';
import { useNavigate, useLocation, useSearchParams } from 'react-router-dom';
import { useSelector, useDispatch } from 'react-redux';
import {
fetchPopularKeywords,
@@ -32,8 +32,15 @@ import { flushPendingEventsBeforeUnload } from '@/utils/trackingHelpers';
const Community = () => {
const navigate = useNavigate();
const location = useLocation(); // ⚡ 获取当前路由信息(用于判断是否在 /community 页面)
const [searchParams] = useSearchParams();
const dispatch = useDispatch();
// 从 URL 获取初始事件 ID用于分享链接直接定位到指定事件
const initialEventId = useMemo(() => {
const eventId = searchParams.get('event');
return eventId ? parseInt(eventId, 10) : null;
}, [searchParams]);
// Redux状态
const { popularKeywords, hotEvents } = useSelector(state => state.communityData);
@@ -166,6 +173,7 @@ const Community = () => {
onSearch={updateFilters}
onEventClick={handleEventClick}
onViewDetail={handleViewDetail}
initialEventId={initialEventId} // ⚡ 从 URL 获取的初始事件 ID
trackingFunctions={{
trackNewsArticleClicked: communityEvents.trackNewsArticleClicked,
trackNewsDetailOpened: communityEvents.trackNewsDetailOpened,