community增加事件详情
This commit is contained in:
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user