community增加事件详情
This commit is contained in:
@@ -7,11 +7,15 @@ import {
|
|||||||
Badge,
|
Badge,
|
||||||
Text,
|
Text,
|
||||||
Icon,
|
Icon,
|
||||||
|
IconButton,
|
||||||
|
Tooltip,
|
||||||
useColorModeValue,
|
useColorModeValue,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { EventFollowButton } from '@views/Community/components/EventCard/atoms';
|
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 ShareButton from '@components/ShareButton';
|
||||||
|
import { getEventDetailUrl } from '@utils/idEncoder';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 精简信息栏组件
|
* 精简信息栏组件
|
||||||
@@ -26,9 +30,17 @@ import ShareButton from '@components/ShareButton';
|
|||||||
* @param {Function} props.onToggleFollow - 切换关注回调
|
* @param {Function} props.onToggleFollow - 切换关注回调
|
||||||
*/
|
*/
|
||||||
const CompactMetaBar = ({ event, importance, isFollowing, followerCount, onToggleFollow }) => {
|
const CompactMetaBar = ({ event, importance, isFollowing, followerCount, onToggleFollow }) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
const viewCountBg = useColorModeValue('white', 'gray.700');
|
const viewCountBg = useColorModeValue('white', 'gray.700');
|
||||||
const viewCountTextColor = useColorModeValue('gray.600', 'gray.300');
|
const viewCountTextColor = useColorModeValue('gray.600', 'gray.300');
|
||||||
|
|
||||||
|
// 跳转到事件详情页
|
||||||
|
const handleViewDetail = () => {
|
||||||
|
if (event?.id) {
|
||||||
|
navigate(getEventDetailUrl(event.id));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 获取重要性文本
|
// 获取重要性文本
|
||||||
const getImportanceText = () => {
|
const getImportanceText = () => {
|
||||||
const levelMap = {
|
const levelMap = {
|
||||||
@@ -104,6 +116,22 @@ const CompactMetaBar = ({ event, importance, isFollowing, followerCount, onToggl
|
|||||||
variant="icon"
|
variant="icon"
|
||||||
size="sm"
|
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>
|
</HStack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import { PAGINATION_CONFIG, DISPLAY_MODES, REFRESH_DEBOUNCE_DELAY } from './cons
|
|||||||
import { PROFESSIONAL_COLORS } from '@constants/professionalTheme';
|
import { PROFESSIONAL_COLORS } from '@constants/professionalTheme';
|
||||||
import { debounce } from '@utils/debounce';
|
import { debounce } from '@utils/debounce';
|
||||||
import { useDevice } from '@hooks/useDevice';
|
import { useDevice } from '@hooks/useDevice';
|
||||||
|
import { eventService } from '@services/eventService';
|
||||||
|
|
||||||
// 🔍 调试:渲染计数器
|
// 🔍 调试:渲染计数器
|
||||||
let dynamicNewsCardRenderCount = 0;
|
let dynamicNewsCardRenderCount = 0;
|
||||||
@@ -55,6 +56,7 @@ let dynamicNewsCardRenderCount = 0;
|
|||||||
* @param {Function} onSearchFocus - 搜索框获得焦点回调
|
* @param {Function} onSearchFocus - 搜索框获得焦点回调
|
||||||
* @param {Function} onEventClick - 事件点击回调
|
* @param {Function} onEventClick - 事件点击回调
|
||||||
* @param {Function} onViewDetail - 查看详情回调
|
* @param {Function} onViewDetail - 查看详情回调
|
||||||
|
* @param {number|null} initialEventId - 从 URL 获取的初始事件 ID(用于分享链接定位)
|
||||||
* @param {Object} trackingFunctions - PostHog 追踪函数集合
|
* @param {Object} trackingFunctions - PostHog 追踪函数集合
|
||||||
* @param {Object} ref - 用于滚动的ref
|
* @param {Object} ref - 用于滚动的ref
|
||||||
*/
|
*/
|
||||||
@@ -66,6 +68,7 @@ const DynamicNewsCardComponent = forwardRef(({
|
|||||||
onSearchFocus,
|
onSearchFocus,
|
||||||
onEventClick,
|
onEventClick,
|
||||||
onViewDetail,
|
onViewDetail,
|
||||||
|
initialEventId = null,
|
||||||
trackingFunctions = {},
|
trackingFunctions = {},
|
||||||
...rest
|
...rest
|
||||||
}, ref) => {
|
}, ref) => {
|
||||||
@@ -183,6 +186,8 @@ const [currentMode, setCurrentMode] = useState('vertical');
|
|||||||
const hasAutoSelectedFirstEvent = useRef(false);
|
const hasAutoSelectedFirstEvent = useRef(false);
|
||||||
// 追踪筛选条件 useEffect 是否是第一次渲染(避免初始加载时重复请求)
|
// 追踪筛选条件 useEffect 是否是第一次渲染(避免初始加载时重复请求)
|
||||||
const isFirstRenderForFilters = useRef(true);
|
const isFirstRenderForFilters = useRef(true);
|
||||||
|
// 追踪是否已尝试从 API 获取 URL 指定的事件
|
||||||
|
const hasTriedFetchingInitialEvent = useRef(false);
|
||||||
|
|
||||||
// 使用分页 Hook
|
// 使用分页 Hook
|
||||||
const {
|
const {
|
||||||
@@ -395,6 +400,7 @@ const [currentMode, setCurrentMode] = useState('vertical');
|
|||||||
hasInitialized.current = false;
|
hasInitialized.current = false;
|
||||||
isFirstRenderForFilters.current = true;
|
isFirstRenderForFilters.current = true;
|
||||||
hasAutoSelectedFirstEvent.current = false;
|
hasAutoSelectedFirstEvent.current = false;
|
||||||
|
hasTriedFetchingInitialEvent.current = false;
|
||||||
console.log('%c🧹 [卸载] 重置初始化标记', 'color: #F59E0B; font-weight: bold;');
|
console.log('%c🧹 [卸载] 重置初始化标记', 'color: #F59E0B; font-weight: bold;');
|
||||||
};
|
};
|
||||||
}, [dispatch, mode, pageSize]); // 移除 currentMode 依赖,避免模式切换时重复请求
|
}, [dispatch, mode, pageSize]); // 移除 currentMode 依赖,避免模式切换时重复请求
|
||||||
@@ -477,9 +483,81 @@ const [currentMode, setCurrentMode] = useState('vertical');
|
|||||||
}
|
}
|
||||||
}, [mode, currentMode, allCachedEventsByPage, allCachedEvents, dispatch, filters]); // 添加 filters 依赖
|
}, [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(() => {
|
useEffect(() => {
|
||||||
if (currentPageEvents.length > 0) {
|
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: 首次加载 - 自动选中第一个事件并触发详情加载
|
// 情况1: 首次加载 - 自动选中第一个事件并触发详情加载
|
||||||
if (!hasAutoSelectedFirstEvent.current && !selectedEvent) {
|
if (!hasAutoSelectedFirstEvent.current && !selectedEvent) {
|
||||||
console.log('%c🎯 [首次加载] 自动选中第一个事件', 'color: #10B981; font-weight: bold;');
|
console.log('%c🎯 [首次加载] 自动选中第一个事件', 'color: #10B981; font-weight: bold;');
|
||||||
@@ -505,7 +583,7 @@ const [currentMode, setCurrentMode] = useState('vertical');
|
|||||||
e => e.id === selectedEvent?.id
|
e => e.id === selectedEvent?.id
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [currentPageEvents, selectedEvent?.id, mode, trackingFunctions]);
|
}, [currentPageEvents, selectedEvent?.id, mode, trackingFunctions, initialEventId]);
|
||||||
|
|
||||||
// 组件卸载时清理选中状态
|
// 组件卸载时清理选中状态
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// src/views/Community/index.js
|
// src/views/Community/index.js
|
||||||
import React, { useEffect, useRef, lazy, Suspense } from 'react';
|
import React, { useEffect, useRef, lazy, Suspense, useMemo } from 'react';
|
||||||
import { useNavigate, useLocation } from 'react-router-dom';
|
import { useNavigate, useLocation, useSearchParams } from 'react-router-dom';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
fetchPopularKeywords,
|
fetchPopularKeywords,
|
||||||
@@ -32,8 +32,15 @@ import { flushPendingEventsBeforeUnload } from '@/utils/trackingHelpers';
|
|||||||
const Community = () => {
|
const Community = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation(); // ⚡ 获取当前路由信息(用于判断是否在 /community 页面)
|
const location = useLocation(); // ⚡ 获取当前路由信息(用于判断是否在 /community 页面)
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
// 从 URL 获取初始事件 ID(用于分享链接直接定位到指定事件)
|
||||||
|
const initialEventId = useMemo(() => {
|
||||||
|
const eventId = searchParams.get('event');
|
||||||
|
return eventId ? parseInt(eventId, 10) : null;
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
// Redux状态
|
// Redux状态
|
||||||
const { popularKeywords, hotEvents } = useSelector(state => state.communityData);
|
const { popularKeywords, hotEvents } = useSelector(state => state.communityData);
|
||||||
|
|
||||||
@@ -166,6 +173,7 @@ const Community = () => {
|
|||||||
onSearch={updateFilters}
|
onSearch={updateFilters}
|
||||||
onEventClick={handleEventClick}
|
onEventClick={handleEventClick}
|
||||||
onViewDetail={handleViewDetail}
|
onViewDetail={handleViewDetail}
|
||||||
|
initialEventId={initialEventId} // ⚡ 从 URL 获取的初始事件 ID
|
||||||
trackingFunctions={{
|
trackingFunctions={{
|
||||||
trackNewsArticleClicked: communityEvents.trackNewsArticleClicked,
|
trackNewsArticleClicked: communityEvents.trackNewsArticleClicked,
|
||||||
trackNewsDetailOpened: communityEvents.trackNewsDetailOpened,
|
trackNewsDetailOpened: communityEvents.trackNewsDetailOpened,
|
||||||
|
|||||||
Reference in New Issue
Block a user