feat: 实现 Redux 全局状态管理事件关注功能

本次提交实现了滚动列表和事件详情的关注按钮状态同步:

 Redux 状态管理
- communityDataSlice.js: 添加 eventFollowStatus state
- 新增 toggleEventFollow AsyncThunk(复用 EventList.js 逻辑)
- 新增 setEventFollowStatus reducer 和 selectEventFollowStatus selector

 组件集成
- DynamicNewsCard.js: 从 Redux 读取关注状态并传递给子组件
- EventScrollList.js: 接收并传递关注状态给事件卡片
- DynamicNewsDetailPanel.js: 移除本地 state,使用 Redux 状态

 Mock API 支持
- event.js: 添加 POST /api/events/:eventId/follow 处理器
- 返回 { is_following, follower_count } 模拟数据

 Bug 修复
- EventDetail/index.js: 添加 useRef 导入
- concept.js: 导出 generatePopularConcepts 函数
- event.js: 添加 /api/events/:eventId/concepts 处理器

功能:
- 点击滚动列表的关注按钮,详情面板的关注状态自动同步
- 点击详情面板的关注按钮,滚动列表的关注状态自动同步
- 关注人数实时更新
- 状态在整个应用中保持一致

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-11-03 17:40:09 +08:00
parent 6a0a8e8e2b
commit f17a8fbd87
6 changed files with 308 additions and 88 deletions

View File

@@ -2,6 +2,7 @@
// 动态新闻详情面板主组件(组装所有子组件)
import React, { useState, useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
Card,
CardBody,
@@ -15,6 +16,7 @@ import {
import { getImportanceConfig } from '../../../../constants/importanceLevels';
import { eventService } from '../../../../services/eventService';
import { useEventStocks } from '../StockDetailPanel/hooks/useEventStocks';
import { toggleEventFollow, selectEventFollowStatus } from '../../../../store/slices/communityDataSlice';
import EventHeaderInfo from './EventHeaderInfo';
import EventDescriptionSection from './EventDescriptionSection';
import RelatedConceptsSection from './RelatedConceptsSection';
@@ -29,11 +31,17 @@ import TransmissionChainAnalysis from '../../../EventDetail/components/Transmiss
* @param {Object} props.event - 事件对象(包含详情数据)
*/
const DynamicNewsDetailPanel = ({ event }) => {
const dispatch = useDispatch();
const cardBg = useColorModeValue('white', 'gray.800');
const borderColor = useColorModeValue('gray.200', 'gray.700');
const textColor = useColorModeValue('gray.600', 'gray.400');
const toast = useToast();
// 从 Redux 读取关注状态
const eventFollowStatus = useSelector(selectEventFollowStatus);
const isFollowing = event?.id ? (eventFollowStatus[event.id]?.isFollowing || false) : false;
const followerCount = event?.id ? (eventFollowStatus[event.id]?.followerCount || event.follower_count || 0) : 0;
// 使用 Hook 获取实时数据
const {
stocks,
@@ -49,10 +57,6 @@ const DynamicNewsDetailPanel = ({ event }) => {
const [isHistoricalOpen, setIsHistoricalOpen] = useState(true);
const [isTransmissionOpen, setIsTransmissionOpen] = useState(false);
// 关注状态管理
const [isFollowing, setIsFollowing] = useState(false);
const [followerCount, setFollowerCount] = useState(0);
// 自选股管理(使用 localStorage
const [watchlistSet, setWatchlistSet] = useState(() => {
try {
@@ -64,23 +68,10 @@ const DynamicNewsDetailPanel = ({ event }) => {
});
// 切换关注状态
const handleToggleFollow = async () => {
try {
if (isFollowing) {
// 取消关注
await eventService.unfollowEvent(event.id);
setIsFollowing(false);
setFollowerCount(prev => Math.max(0, prev - 1));
} else {
// 添加关注
await eventService.followEvent(event.id);
setIsFollowing(true);
setFollowerCount(prev => prev + 1);
}
} catch (error) {
console.error('切换关注状态失败:', error);
}
};
const handleToggleFollow = useCallback(async () => {
if (!event?.id) return;
dispatch(toggleEventFollow(event.id));
}, [dispatch, event?.id]);
// 切换自选股
const handleWatchlistToggle = useCallback(async (stockCode, isInWatchlist) => {