- 补充精简/详细模式切换功能文档 - 添加无头部模式(showHeader)使用说明 - 更新 CollapsibleSection 和 DynamicNewsDetailPanel 的 API 参考 - 添加相关组件的使用示例 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1198 lines
36 KiB
Markdown
1198 lines
36 KiB
Markdown
# Community 页面 - 技术文档
|
||
|
||
> **版本**: v1.0.0
|
||
> **更新日期**: 2025-01-07
|
||
> **文档类型**: 页面架构与开发指南
|
||
|
||
---
|
||
|
||
## 📑 目录
|
||
|
||
1. [页面概述](#-页面概述)
|
||
2. [页面架构](#-页面架构)
|
||
3. [核心功能模块](#-核心功能模块)
|
||
4. [数据流与状态管理](#-数据流与状态管理)
|
||
5. [自定义 Hooks 详解](#-自定义-hooks-详解)
|
||
6. [组件结构说明](#-组件结构说明)
|
||
7. [通知与权限引导](#-通知与权限引导)
|
||
8. [开发指南](#-开发指南)
|
||
9. [代码变更历史](#-代码变更历史)
|
||
|
||
---
|
||
|
||
## 📖 页面概述
|
||
|
||
### 功能定位
|
||
|
||
Community 页面(社区页面)是应用的核心功能模块之一,提供实时金融事件、动态新闻、热点事件的展示和追踪功能。
|
||
|
||
### 核心价值
|
||
|
||
- ✅ **实时性**: 展示最新的金融事件和市场动态
|
||
- ✅ **可追踪**: 用户可以关注感兴趣的事件和股票
|
||
- ✅ **可筛选**: 支持多维度筛选(重要性、行业、时间范围等)
|
||
- ✅ **数据分析**: 集成 PostHog 埋点,追踪用户行为
|
||
- ✅ **通知引导**: 首次访问时引导用户开启通知权限
|
||
|
||
### 页面入口
|
||
|
||
- **路由路径**: `/community`
|
||
- **导航位置**: 主导航栏
|
||
- **权限要求**: MODAL(未登录显示登录弹窗)
|
||
|
||
---
|
||
|
||
## 🏗️ 页面架构
|
||
|
||
### 组件树结构
|
||
|
||
```
|
||
Community (index.js)
|
||
├── HotEventsSection # 热点事件区域
|
||
│ └── HotEvents # 热点事件卡片列表
|
||
│
|
||
└── DynamicNewsCard # 实时动态新闻区域
|
||
├── UnifiedSearchBox # 统一搜索框(关键词+筛选器)
|
||
│ ├── PopularKeywords # 热门关键词
|
||
│ ├── TradingTimeFilter # 交易时间筛选
|
||
│ └── IndustryCascader # 行业级联选择器
|
||
│
|
||
├── ModeToggleButtons # 横向/纵向布局切换
|
||
│
|
||
├── EventScrollList (横向模式) # 横向滚动事件列表
|
||
│ └── EventCard # 事件卡片
|
||
│ ├── HorizontalDynamicNewsEventCard # 横向卡片
|
||
│ └── DynamicNewsEventCard # 纵向卡片
|
||
│
|
||
├── VerticalModeLayout (纵向模式) # 纵向网格布局
|
||
│ └── VirtualizedFourRowGrid # 虚拟化四行网格
|
||
│ └── EventCard # 事件卡片
|
||
│
|
||
├── EventDetailScrollPanel # 右侧详情面板
|
||
│ └── DynamicNewsDetail # 事件详情
|
||
│ ├── EventHeaderInfo # 事件头部信息
|
||
│ ├── EventDescriptionSection # 事件描述
|
||
│ ├── RelatedStocksSection # 相关股票
|
||
│ └── RelatedConceptsSection # 相关概念
|
||
│ ├── SimpleConceptCard # 简单概念卡片
|
||
│ └── DetailedConceptCard # 详细概念卡片
|
||
│
|
||
└── PaginationControl # 分页控制
|
||
```
|
||
|
||
### 文件组织方式
|
||
|
||
```
|
||
src/views/Community/
|
||
├── index.js # 页面主入口
|
||
├── index.css # 页面样式
|
||
│
|
||
├── components/ # 页面组件
|
||
│ ├── HotEventsSection.js # 热点事件区域容器
|
||
│ ├── HotEvents.js # 热点事件卡片列表
|
||
│ ├── HotEvents.css
|
||
│ │
|
||
│ ├── DynamicNewsCard.js # 实时动态新闻主组件
|
||
│ │ ├── constants.js # 常量定义
|
||
│ │ ├── hooks/ # 专属 Hooks
|
||
│ │ │ ├── usePagination.js # 分页逻辑
|
||
│ │ │ └── useInfiniteScroll.js # 无限滚动
|
||
│ │ ├── EventScrollList.js # 横向滚动列表
|
||
│ │ ├── VerticalModeLayout.js # 纵向布局
|
||
│ │ ├── VirtualizedFourRowGrid.js # 虚拟化网格
|
||
│ │ ├── ModeToggleButtons.js # 布局切换按钮
|
||
│ │ ├── PaginationControl.js # 分页控制
|
||
│ │ ├── PageNavigationButton.js # 页码导航按钮
|
||
│ │ └── EventDetailScrollPanel.js # 详情面板容器
|
||
│ │
|
||
│ ├── EventCard/ # 事件卡片组件族
|
||
│ │ ├── index.js # 智能路由(紧凑 vs 详细)
|
||
│ │ ├── CompactEventCard.js # 紧凑卡片
|
||
│ │ ├── DetailedEventCard.js # 详细卡片
|
||
│ │ ├── HorizontalDynamicNewsEventCard.js # 横向动态卡片
|
||
│ │ ├── DynamicNewsEventCard.js # 纵向动态卡片
|
||
│ │ ├── EventHeader.js # 事件头部
|
||
│ │ ├── EventDescription.js # 事件描述
|
||
│ │ ├── EventTimeline.js # 事件时间线
|
||
│ │ ├── EventImportanceBadge.js # 重要性徽章
|
||
│ │ ├── ImportanceBadge.js # 重要性标识
|
||
│ │ ├── ImportanceStamp.js # 重要性印章
|
||
│ │ ├── EventPriceDisplay.js # 价格显示
|
||
│ │ ├── EventStats.js # 事件统计
|
||
│ │ └── EventFollowButton.js # 关注按钮
|
||
│ │
|
||
│ ├── DynamicNewsDetail/ # 事件详情面板
|
||
│ │ ├── index.js # 主入口
|
||
│ │ ├── DynamicNewsDetailPanel.js # 详情面板容器
|
||
│ │ ├── EventHeaderInfo.js # 头部信息
|
||
│ │ ├── EventDescriptionSection.js # 描述区域
|
||
│ │ ├── CollapsibleSection.js # 可折叠区域
|
||
│ │ ├── CollapsibleHeader.js # 可折叠头部
|
||
│ │ ├── RelatedStocksSection.js # 相关股票区域
|
||
│ │ ├── StockListItem.js # 股票列表项
|
||
│ │ ├── CompactStockItem.js # 紧凑股票项
|
||
│ │ ├── MiniLineChart.js # 迷你折线图
|
||
│ │ ├── MiniKLineChart.js # 迷你K线图
|
||
│ │ └── RelatedConceptsSection/ # 相关概念区域
|
||
│ │ ├── index.js
|
||
│ │ ├── SimpleConceptCard.js # 简单概念卡片
|
||
│ │ ├── DetailedConceptCard.js # 详细概念卡片
|
||
│ │ ├── ConceptStockItem.js # 概念股票项
|
||
│ │ └── TradingDateInfo.js # 交易日期信息
|
||
│ │
|
||
│ ├── UnifiedSearchBox.js # 统一搜索框
|
||
│ ├── PopularKeywords.js # 热门关键词
|
||
│ ├── SearchBox.js # 基础搜索框
|
||
│ ├── TradingTimeFilter.js # 交易时间筛选器
|
||
│ ├── IndustryCascader.js # 行业级联选择器
|
||
│ ├── ImportanceLegend.js # 重要性图例
|
||
│ │
|
||
│ ├── StockDetailPanel.js # 股票详情面板(独立模块)
|
||
│ ├── StockDetailPanel.css
|
||
│ ├── StockDetailPanel/
|
||
│ │ ├── components/
|
||
│ │ │ ├── index.js
|
||
│ │ │ ├── StockSearchBar.js # 股票搜索栏
|
||
│ │ │ ├── StockTable.js # 股票表格
|
||
│ │ │ ├── RelatedStocksTab.js # 相关股票标签页
|
||
│ │ │ ├── MiniTimelineChart.js # 迷你时间线图
|
||
│ │ │ └── LockedContent.js # 锁定内容
|
||
│ │ ├── hooks/
|
||
│ │ │ ├── useEventStocks.js # 事件股票钩子
|
||
│ │ │ ├── useWatchlist.js # 监控列表钩子
|
||
│ │ │ └── useStockMonitoring.js # 股票监控钩子
|
||
│ │ └── utils/
|
||
│ │ └── klineDataCache.js # K线数据缓存
|
||
│ │
|
||
│ ├── EventList.js # 事件列表(旧版,已备份)
|
||
│ ├── EventList.js.bak
|
||
│ ├── EventList.css
|
||
│ ├── EventListSection.js # 事件列表区域
|
||
│ ├── EventTimelineHeader.js # 事件时间线头部
|
||
│ ├── EventDetailModal.js # 事件详情弹窗
|
||
│ ├── EventDiscussionModal.js # 事件讨论弹窗
|
||
│ │
|
||
│ ├── MarketReviewCard.js # 市场回顾卡片
|
||
│ ├── InvestmentCalendar.js # 投资日历
|
||
│ ├── InvestmentCalendar.css
|
||
│ ├── MidjourneyHeroSection.js # Hero 区域
|
||
│ │
|
||
│ └── UnifiedSearchBox修复说明.md # 修复说明文档
|
||
│
|
||
└── hooks/ # 页面专属 Hooks
|
||
├── useEventData.js # 事件数据获取
|
||
├── useEventFilters.js # 事件筛选逻辑
|
||
└── useCommunityEvents.js # PostHog 埋点追踪
|
||
```
|
||
|
||
---
|
||
|
||
## 🎯 核心功能模块
|
||
|
||
### 1. 热点事件展示 (HotEventsSection)
|
||
|
||
**功能**:展示当前最热门的事件,吸引用户关注。
|
||
|
||
**位置**: `src/views/Community/components/HotEventsSection.js`
|
||
|
||
**特性**:
|
||
- 自动获取热点事件数据
|
||
- 卡片式展示,支持点击查看详情
|
||
- 集成 PostHog 埋点追踪点击行为
|
||
|
||
**使用示例**:
|
||
```jsx
|
||
<HotEventsSection
|
||
events={hotEvents}
|
||
onEventClick={communityEvents.trackNewsArticleClicked}
|
||
/>
|
||
```
|
||
|
||
---
|
||
|
||
### 2. 实时动态新闻 (DynamicNewsCard)
|
||
|
||
**功能**:展示实时金融事件动态,支持筛选、搜索、分页、布局切换。
|
||
|
||
**位置**: `src/views/Community/components/DynamicNewsCard.js`
|
||
|
||
**核心特性**:
|
||
|
||
#### 2.1 搜索与筛选
|
||
- ✅ 关键词搜索(支持热门关键词快速选择)
|
||
- ✅ 交易时间筛选(盘前、盘中、盘后、全部)
|
||
- ✅ 重要性筛选(特级、A级、B级、C级、全部)
|
||
- ✅ 行业筛选(行业级联选择器)
|
||
|
||
#### 2.2 布局模式
|
||
- **横向滚动模式**:
|
||
- 适合快速浏览大量事件
|
||
- 支持左右滚动查看更多
|
||
- 卡片紧凑布局
|
||
|
||
- **纵向网格模式**:
|
||
- 适合深度浏览事件详情
|
||
- 虚拟化四行网格提升性能
|
||
- 卡片详细布局
|
||
|
||
#### 2.3 详情面板
|
||
- 右侧滑出式详情面板
|
||
- 展示事件完整描述
|
||
- 展示相关股票和概念
|
||
- 支持点击股票跳转到详情页
|
||
|
||
#### 2.4 分页功能
|
||
- 支持上一页/下一页
|
||
- 支持页码快速跳转
|
||
- 显示当前页码和总页数
|
||
|
||
**使用示例**:
|
||
```jsx
|
||
<DynamicNewsCard
|
||
mt={6}
|
||
filters={filters}
|
||
popularKeywords={popularKeywords}
|
||
lastUpdateTime={lastUpdateTime}
|
||
onSearch={updateFilters}
|
||
onEventClick={handleEventClick}
|
||
onViewDetail={handleViewDetail}
|
||
trackingFunctions={{
|
||
trackNewsArticleClicked: communityEvents.trackNewsArticleClicked,
|
||
trackNewsDetailOpened: communityEvents.trackNewsDetailOpened,
|
||
trackNewsFilterApplied: communityEvents.trackNewsFilterApplied,
|
||
trackNewsSorted: communityEvents.trackNewsSorted,
|
||
trackNewsSearched: communityEvents.trackNewsSearched,
|
||
trackRelatedStockClicked: communityEvents.trackRelatedStockClicked,
|
||
}}
|
||
/>
|
||
```
|
||
|
||
---
|
||
|
||
### 3. 事件详情查看 (DynamicNewsDetail)
|
||
|
||
**功能**:展示单个事件的详细信息,包括描述、相关股票、相关概念等。
|
||
|
||
**位置**: `src/views/Community/components/DynamicNewsDetail/index.js`
|
||
|
||
**内容结构**:
|
||
```
|
||
DynamicNewsDetail
|
||
├── EventHeaderInfo # 事件头部(标题、时间、重要性)
|
||
├── EventDescriptionSection # 事件描述
|
||
├── RelatedStocksSection # 相关股票
|
||
│ ├── StockListItem # 股票列表项
|
||
│ ├── CompactStockItem # 紧凑股票项
|
||
│ ├── MiniLineChart # 迷你折线图
|
||
│ └── MiniKLineChart # 迷你K线图
|
||
└── RelatedConceptsSection # 相关概念
|
||
├── SimpleConceptCard # 简单概念卡片
|
||
├── DetailedConceptCard # 详细概念卡片
|
||
└── ConceptStockItem # 概念股票项
|
||
```
|
||
|
||
**特性**:
|
||
- ✅ 可折叠区域(节省空间)
|
||
- ✅ 相关股票实时行情展示
|
||
- ✅ 相关概念板块分析
|
||
- ✅ 支持股票点击跳转
|
||
|
||
---
|
||
|
||
### 4. 股票详情面板 (StockDetailPanel)
|
||
|
||
**功能**:独立的股票详情查看模块,支持搜索、监控、K线图查看。
|
||
|
||
**位置**: `src/views/Community/components/StockDetailPanel.js`
|
||
|
||
**核心功能**:
|
||
- ✅ 股票搜索
|
||
- ✅ 股票监控(加入自选)
|
||
- ✅ K线图展示
|
||
- ✅ 相关股票推荐
|
||
- ✅ 事件关联股票
|
||
|
||
**自定义 Hooks**:
|
||
- `useEventStocks` - 获取事件关联股票
|
||
- `useWatchlist` - 管理自选股列表
|
||
- `useStockMonitoring` - 股票监控逻辑
|
||
|
||
**数据缓存**:
|
||
- `klineDataCache.js` - K线数据缓存,提升性能
|
||
|
||
---
|
||
|
||
## 🔄 数据流与状态管理
|
||
|
||
### Redux State (communityDataSlice)
|
||
|
||
**位置**: `src/store/slices/communityDataSlice.js`
|
||
|
||
**State 结构**:
|
||
```javascript
|
||
{
|
||
popularKeywords: [], // 热门关键词
|
||
hotEvents: [], // 热点事件
|
||
loading: false, // 加载状态
|
||
error: null // 错误信息
|
||
}
|
||
```
|
||
|
||
**Actions**:
|
||
```javascript
|
||
// 同步 Actions
|
||
dispatch(fetchPopularKeywords()); // 获取热门关键词
|
||
dispatch(fetchHotEvents()); // 获取热点事件
|
||
|
||
// Thunk Actions (异步)
|
||
dispatch(fetchPopularKeywordsAsync());
|
||
dispatch(fetchHotEventsAsync());
|
||
```
|
||
|
||
**使用示例**:
|
||
```jsx
|
||
import { useSelector, useDispatch } from 'react-redux';
|
||
import { fetchPopularKeywords, fetchHotEvents } from '@store/slices/communityDataSlice';
|
||
|
||
const Community = () => {
|
||
const dispatch = useDispatch();
|
||
const { popularKeywords, hotEvents } = useSelector(state => state.communityData);
|
||
|
||
useEffect(() => {
|
||
dispatch(fetchPopularKeywords());
|
||
dispatch(fetchHotEvents());
|
||
}, [dispatch]);
|
||
|
||
return (/* ... */);
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
### API 调用流程
|
||
|
||
**API 服务**: `src/services/communityService.js`
|
||
|
||
**主要 API 端点**:
|
||
```javascript
|
||
// 获取事件列表
|
||
GET /api/community/events
|
||
?page=1
|
||
&pageSize=20
|
||
&importance=all
|
||
&industry_code=all
|
||
&date_range=today
|
||
&trading_time=all
|
||
&search=关键词
|
||
|
||
// 获取热点事件
|
||
GET /api/community/hot-events
|
||
|
||
// 获取热门关键词
|
||
GET /api/community/popular-keywords
|
||
|
||
// 获取事件详情
|
||
GET /api/community/events/:id
|
||
|
||
// 获取相关股票
|
||
GET /api/community/events/:id/related-stocks
|
||
|
||
// 获取相关概念
|
||
GET /api/community/events/:id/related-concepts
|
||
```
|
||
|
||
**数据流向**:
|
||
```
|
||
用户交互
|
||
↓
|
||
Component 触发 Action
|
||
↓
|
||
自定义 Hook 处理逻辑 (useEventData, useEventFilters)
|
||
↓
|
||
API Service 调用后端
|
||
↓
|
||
后端返回数据
|
||
↓
|
||
Hook 更新本地 State
|
||
↓
|
||
Component 重新渲染
|
||
```
|
||
|
||
---
|
||
|
||
## 🪝 自定义 Hooks 详解
|
||
|
||
### 1. useEventData
|
||
|
||
**位置**: `src/views/Community/hooks/useEventData.js`
|
||
|
||
**功能**: 根据筛选条件获取事件数据。
|
||
|
||
**输入参数**:
|
||
```javascript
|
||
const filters = {
|
||
page: 1, // 页码
|
||
pageSize: 20, // 每页数量
|
||
importance: 'all', // 重要性筛选
|
||
industry_code: 'all', // 行业代码
|
||
date_range: 'today', // 日期范围
|
||
trading_time: 'all', // 交易时间
|
||
search: '' // 搜索关键词
|
||
};
|
||
```
|
||
|
||
**返回值**:
|
||
```javascript
|
||
{
|
||
events: [], // 事件列表
|
||
pagination: { // 分页信息
|
||
current: 1,
|
||
pageSize: 20,
|
||
total: 100
|
||
},
|
||
loading: false, // 加载状态
|
||
lastUpdateTime: Date // 最后更新时间
|
||
}
|
||
```
|
||
|
||
**使用示例**:
|
||
```jsx
|
||
const { events, pagination, loading, lastUpdateTime } = useEventData(filters);
|
||
```
|
||
|
||
---
|
||
|
||
### 2. useEventFilters
|
||
|
||
**位置**: `src/views/Community/hooks/useEventFilters.js`
|
||
|
||
**功能**: 管理事件筛选逻辑和导航。
|
||
|
||
**返回值**:
|
||
```javascript
|
||
{
|
||
filters: {}, // 当前筛选条件
|
||
updateFilters: (newFilters) => {}, // 更新筛选条件
|
||
handlePageChange: (page) => {}, // 分页变更
|
||
handleEventClick: (event) => {}, // 事件点击
|
||
handleViewDetail: (event) => {} // 查看详情
|
||
}
|
||
```
|
||
|
||
**使用示例**:
|
||
```jsx
|
||
const {
|
||
filters,
|
||
updateFilters,
|
||
handlePageChange,
|
||
handleEventClick,
|
||
handleViewDetail
|
||
} = useEventFilters({ navigate });
|
||
```
|
||
|
||
---
|
||
|
||
### 3. useCommunityEvents
|
||
|
||
**位置**: `src/views/Community/hooks/useCommunityEvents.js`
|
||
|
||
**功能**: 提供 PostHog 埋点追踪函数集合。
|
||
|
||
**返回的追踪函数**:
|
||
```javascript
|
||
{
|
||
trackNewsListViewed, // 新闻列表查看
|
||
trackNewsArticleClicked, // 新闻文章点击
|
||
trackNewsDetailOpened, // 新闻详情打开
|
||
trackNewsFilterApplied, // 筛选器应用
|
||
trackNewsSorted, // 排序
|
||
trackNewsSearched, // 搜索
|
||
trackRelatedStockClicked, // 相关股票点击
|
||
trackNewsShareClicked, // 分享点击
|
||
trackNewsCommentPosted, // 评论发布
|
||
trackNewsLiked // 点赞
|
||
}
|
||
```
|
||
|
||
**使用示例**:
|
||
```jsx
|
||
const communityEvents = useCommunityEvents({ navigate });
|
||
|
||
// 追踪事件点击
|
||
communityEvents.trackNewsArticleClicked({
|
||
eventId: event.id,
|
||
title: event.title,
|
||
importance: event.importance
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## 🧩 组件结构说明
|
||
|
||
### 原子组件 (Atomic Components)
|
||
|
||
#### EventCard 系列
|
||
|
||
**设计模式**: 原子设计 + 智能路由
|
||
|
||
**组件清单**:
|
||
|
||
| 组件文件 | 功能 | 使用场景 |
|
||
|---------|------|---------|
|
||
| `index.js` | 智能路由器 | 根据 `mode` 参数路由到紧凑或详细卡片 |
|
||
| `CompactEventCard.js` | 紧凑卡片 | 列表视图、横向滚动 |
|
||
| `DetailedEventCard.js` | 详细卡片 | 详情视图、纵向网格 |
|
||
| `HorizontalDynamicNewsEventCard.js` | 横向动态卡片 | 横向滚动布局 |
|
||
| `DynamicNewsEventCard.js` | 纵向动态卡片 | 纵向网格布局 |
|
||
| `EventHeader.js` | 事件头部 | 标题、时间、重要性 |
|
||
| `EventDescription.js` | 事件描述 | 文本内容 |
|
||
| `EventTimeline.js` | 事件时间线 | 时间轴展示 |
|
||
| `EventImportanceBadge.js` | 重要性徽章 | 重要性标识 |
|
||
| `ImportanceBadge.js` | 重要性标识 | 简单版徽章 |
|
||
| `ImportanceStamp.js` | 重要性印章 | 印章样式 |
|
||
| `EventPriceDisplay.js` | 价格显示 | 股票价格和涨跌幅 |
|
||
| `EventStats.js` | 事件统计 | 浏览量、点赞数等 |
|
||
| `EventFollowButton.js` | 关注按钮 | 关注/取消关注 |
|
||
|
||
**智能路由示例**:
|
||
```jsx
|
||
import EventCard from './components/EventCard';
|
||
|
||
// 紧凑模式
|
||
<EventCard mode="compact" event={event} />
|
||
|
||
// 详细模式
|
||
<EventCard mode="detailed" event={event} />
|
||
|
||
// 横向动态模式
|
||
<EventCard mode="horizontal-dynamic" event={event} />
|
||
|
||
// 纵向动态模式
|
||
<EventCard mode="vertical-dynamic" event={event} />
|
||
```
|
||
|
||
---
|
||
|
||
### 复合组件 (Composite Components)
|
||
|
||
#### DynamicNewsCard
|
||
|
||
**设计模式**: 容器 + 展示分离
|
||
|
||
**核心子组件**:
|
||
- `UnifiedSearchBox` - 搜索和筛选
|
||
- `ModeToggleButtons` - 布局切换
|
||
- `EventScrollList` - 横向滚动列表
|
||
- `VerticalModeLayout` - 纵向网格布局
|
||
- `VirtualizedFourRowGrid` - 虚拟化网格(性能优化)
|
||
- `EventDetailScrollPanel` - 详情面板
|
||
- `PaginationControl` - 分页控制
|
||
|
||
**状态管理**:
|
||
```javascript
|
||
// 内部管理的状态
|
||
const [viewMode, setViewMode] = useState('horizontal'); // 布局模式
|
||
const [selectedEvent, setSelectedEvent] = useState(null); // 选中事件
|
||
const [currentPage, setCurrentPage] = useState(1); // 当前页码
|
||
const [detailVisible, setDetailVisible] = useState(false); // 详情面板可见性
|
||
```
|
||
|
||
**自定义 Hooks**:
|
||
- `usePagination` - 分页逻辑
|
||
- `useInfiniteScroll` - 无限滚动(可选)
|
||
|
||
---
|
||
|
||
#### HotEventsSection
|
||
|
||
**设计模式**: 简单容器
|
||
|
||
**功能**: 包装 HotEvents 组件,提供布局和样式。
|
||
|
||
**使用示例**:
|
||
```jsx
|
||
<HotEventsSection
|
||
events={hotEvents}
|
||
onEventClick={handleEventClick}
|
||
/>
|
||
```
|
||
|
||
---
|
||
|
||
### 面板组件 (Panel Components)
|
||
|
||
#### DynamicNewsDetail
|
||
|
||
**位置**: `src/views/Community/components/DynamicNewsDetail/index.js`
|
||
|
||
**设计模式**: 组合模式
|
||
|
||
**子组件结构**:
|
||
```jsx
|
||
<DynamicNewsDetailPanel>
|
||
<EventHeaderInfo />
|
||
<EventDescriptionSection />
|
||
<CollapsibleSection title="相关股票">
|
||
<RelatedStocksSection>
|
||
<StockListItem />
|
||
<CompactStockItem />
|
||
</RelatedStocksSection>
|
||
</CollapsibleSection>
|
||
<CollapsibleSection title="相关概念">
|
||
<RelatedConceptsSection>
|
||
<SimpleConceptCard />
|
||
<DetailedConceptCard />
|
||
</RelatedConceptsSection>
|
||
</CollapsibleSection>
|
||
</DynamicNewsDetailPanel>
|
||
```
|
||
|
||
**特性**:
|
||
- ✅ 可折叠区域
|
||
- ✅ 懒加载(折叠时不渲染内容)
|
||
- ✅ 滚动优化
|
||
|
||
---
|
||
|
||
#### StockDetailPanel
|
||
|
||
**位置**: `src/views/Community/components/StockDetailPanel.js`
|
||
|
||
**设计模式**: 独立模块
|
||
|
||
**内部模块**:
|
||
- `StockSearchBar` - 股票搜索
|
||
- `StockTable` - 股票表格
|
||
- `RelatedStocksTab` - 相关股票标签页
|
||
- `MiniTimelineChart` - 迷你时间线图
|
||
- `LockedContent` - 锁定内容(需要权限)
|
||
|
||
**自定义 Hooks**:
|
||
- `useEventStocks` - 事件股票获取
|
||
- `useWatchlist` - 自选股管理
|
||
- `useStockMonitoring` - 股票监控
|
||
|
||
**缓存机制**:
|
||
```javascript
|
||
// klineDataCache.js
|
||
export const klineCache = {
|
||
get: (stockCode) => { /* ... */ },
|
||
set: (stockCode, data) => { /* ... */ },
|
||
clear: () => { /* ... */ }
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## 🔔 通知与权限引导
|
||
|
||
### 首次访问引导
|
||
|
||
**触发时机**: 用户首次访问 Community 页面
|
||
|
||
**延迟时间**: 5 秒(让用户先浏览页面内容)
|
||
|
||
**实现代码**:
|
||
```jsx
|
||
// src/views/Community/index.js
|
||
const { showCommunityGuide } = useNotification();
|
||
|
||
useEffect(() => {
|
||
if (showCommunityGuide) {
|
||
const timer = setTimeout(() => {
|
||
logger.info('Community', '显示社区权限引导');
|
||
showCommunityGuide();
|
||
}, 5000);
|
||
|
||
return () => clearTimeout(timer);
|
||
}
|
||
}, [showCommunityGuide]);
|
||
```
|
||
|
||
### 权限请求时机
|
||
|
||
**通知权限**:
|
||
- 首次收到新事件通知时
|
||
- 用户主动点击"开启通知"按钮时
|
||
|
||
**引导策略**:
|
||
- 权限状态为 `default`(未决定)→ 自动请求权限
|
||
- 权限状态为 `denied`(已拒绝)→ 显示设置指引
|
||
|
||
**相关文档**: [NOTIFICATION_SYSTEM.md](./NOTIFICATION_SYSTEM.md)
|
||
|
||
---
|
||
|
||
## 📚 开发指南
|
||
|
||
### 添加新的筛选条件
|
||
|
||
**步骤**:
|
||
|
||
1. **更新 filters 状态** (`useEventFilters.js`):
|
||
```javascript
|
||
const [filters, setFilters] = useState({
|
||
// ... 现有筛选条件
|
||
newFilter: 'default' // 新增筛选条件
|
||
});
|
||
```
|
||
|
||
2. **添加筛选器 UI** (`UnifiedSearchBox.js` 或新组件):
|
||
```jsx
|
||
<Select
|
||
value={filters.newFilter}
|
||
onChange={(value) => updateFilters({ newFilter: value })}
|
||
>
|
||
<option value="default">默认</option>
|
||
<option value="option1">选项1</option>
|
||
</Select>
|
||
```
|
||
|
||
3. **更新 API 调用** (`useEventData.js`):
|
||
```javascript
|
||
const fetchEvents = async (filters) => {
|
||
const response = await api.get('/api/community/events', {
|
||
params: {
|
||
// ... 现有参数
|
||
new_filter: filters.newFilter
|
||
}
|
||
});
|
||
return response.data;
|
||
};
|
||
```
|
||
|
||
4. **添加 PostHog 埋点** (`useCommunityEvents.js`):
|
||
```javascript
|
||
const trackNewFilterApplied = (filterValue) => {
|
||
posthog.capture('news_filter_applied', {
|
||
filter_type: 'new_filter',
|
||
filter_value: filterValue
|
||
});
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
### 添加新的事件卡片样式
|
||
|
||
**步骤**:
|
||
|
||
1. **创建新组件** (`src/views/Community/components/EventCard/NewStyleCard.js`):
|
||
```jsx
|
||
import React from 'react';
|
||
import { Box, Text } from '@chakra-ui/react';
|
||
|
||
const NewStyleCard = ({ event, onClick }) => {
|
||
return (
|
||
<Box
|
||
onClick={() => onClick(event)}
|
||
cursor="pointer"
|
||
// ... 样式
|
||
>
|
||
<Text>{event.title}</Text>
|
||
{/* 自定义内容 */}
|
||
</Box>
|
||
);
|
||
};
|
||
|
||
export default NewStyleCard;
|
||
```
|
||
|
||
2. **更新智能路由** (`src/views/Community/components/EventCard/index.js`):
|
||
```jsx
|
||
import NewStyleCard from './NewStyleCard';
|
||
|
||
const EventCard = ({ mode = 'compact', event, ...props }) => {
|
||
if (mode === 'new-style') {
|
||
return <NewStyleCard event={event} {...props} />;
|
||
}
|
||
|
||
// ... 现有逻辑
|
||
};
|
||
```
|
||
|
||
3. **使用新样式**:
|
||
```jsx
|
||
<EventCard mode="new-style" event={event} />
|
||
```
|
||
|
||
---
|
||
|
||
### 性能优化建议
|
||
|
||
#### 1. 虚拟化长列表
|
||
|
||
**已实现**: `VirtualizedFourRowGrid.js`
|
||
|
||
**适用场景**: 事件数量超过 100 条时
|
||
|
||
**库**: `react-window`
|
||
|
||
**示例**:
|
||
```jsx
|
||
import { FixedSizeGrid } from 'react-window';
|
||
|
||
<FixedSizeGrid
|
||
columnCount={4}
|
||
rowCount={Math.ceil(events.length / 4)}
|
||
columnWidth={300}
|
||
rowHeight={200}
|
||
height={800}
|
||
width={1200}
|
||
>
|
||
{({ columnIndex, rowIndex, style }) => (
|
||
<div style={style}>
|
||
<EventCard event={events[rowIndex * 4 + columnIndex]} />
|
||
</div>
|
||
)}
|
||
</FixedSizeGrid>
|
||
```
|
||
|
||
#### 2. 图片懒加载
|
||
|
||
**实现**: 使用 `loading="lazy"` 属性
|
||
|
||
```jsx
|
||
<img src={event.imageUrl} loading="lazy" alt={event.title} />
|
||
```
|
||
|
||
#### 3. 防抖搜索
|
||
|
||
**实现**: 在搜索框使用 `debounce`
|
||
|
||
```javascript
|
||
import { debounce } from 'lodash';
|
||
|
||
const debouncedSearch = debounce((keyword) => {
|
||
updateFilters({ search: keyword });
|
||
}, 300);
|
||
```
|
||
|
||
#### 4. 缓存 API 响应
|
||
|
||
**实现**: 使用 `klineDataCache.js`
|
||
|
||
```javascript
|
||
// 缓存 K线数据
|
||
const getCachedKlineData = (stockCode) => {
|
||
const cached = klineCache.get(stockCode);
|
||
if (cached && Date.now() - cached.timestamp < 60000) {
|
||
return cached.data;
|
||
}
|
||
|
||
const data = await fetchKlineData(stockCode);
|
||
klineCache.set(stockCode, data);
|
||
return data;
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
### 测试指南
|
||
|
||
#### 单元测试
|
||
|
||
**工具**: Jest + React Testing Library
|
||
|
||
**测试用例示例**:
|
||
```javascript
|
||
// useEventData.test.js
|
||
import { renderHook } from '@testing-library/react-hooks';
|
||
import { useEventData } from '../hooks/useEventData';
|
||
|
||
describe('useEventData', () => {
|
||
it('应该返回事件列表', async () => {
|
||
const { result, waitForNextUpdate } = renderHook(() =>
|
||
useEventData({ page: 1, pageSize: 20 })
|
||
);
|
||
|
||
await waitForNextUpdate();
|
||
|
||
expect(result.current.events).toHaveLength(20);
|
||
expect(result.current.loading).toBe(false);
|
||
});
|
||
});
|
||
```
|
||
|
||
#### 集成测试
|
||
|
||
**测试场景**:
|
||
- 搜索功能是否正常工作
|
||
- 筛选器是否正确应用
|
||
- 分页是否正常切换
|
||
- 事件点击是否正确跳转
|
||
|
||
**测试示例**:
|
||
```javascript
|
||
// Community.test.js
|
||
import { render, screen, fireEvent } from '@testing-library/react';
|
||
import Community from '../index';
|
||
|
||
describe('Community Page', () => {
|
||
it('应该显示搜索框', () => {
|
||
render(<Community />);
|
||
expect(screen.getByPlaceholderText('搜索事件...')).toBeInTheDocument();
|
||
});
|
||
|
||
it('应该响应搜索', async () => {
|
||
render(<Community />);
|
||
const searchInput = screen.getByPlaceholderText('搜索事件...');
|
||
|
||
fireEvent.change(searchInput, { target: { value: '特斯拉' } });
|
||
|
||
// 等待搜索结果
|
||
await screen.findByText(/特斯拉/i);
|
||
});
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## 📜 代码变更历史
|
||
|
||
### 2025-01-07: Community 页面 PostHog 追踪完善
|
||
|
||
**变更类型**: 用户行为追踪完善
|
||
|
||
**背景**:
|
||
Community 页面已实现 `useCommunityEvents` hook,提供了 9 个追踪函数,但大部分追踪函数未被实际使用。页面缺少对用户关键交互行为的追踪,无法有效分析用户使用习惯。
|
||
|
||
**变更内容**:
|
||
|
||
**新增追踪点**:
|
||
1. **热点事件交互追踪** (`HotEvents.js`)
|
||
- ✅ 热点事件卡片点击 → `trackNewsArticleClicked`
|
||
- 追踪数据:事件 ID、标题、重要性、来源
|
||
|
||
2. **动态新闻交互追踪** (`DynamicNewsCard.js`)
|
||
- ✅ 四排模式事件点击(弹窗打开)→ `trackNewsDetailOpened`
|
||
- ✅ 首次自动选中事件 → `trackNewsArticleClicked`
|
||
- 追踪数据:事件 ID、标题、重要性、显示模式、来源
|
||
|
||
3. **搜索和筛选追踪** (`UnifiedSearchBox.js`)
|
||
- ✅ 主搜索提交 → `trackNewsSearched`
|
||
- ✅ 股票选择(下拉框)→ `trackRelatedStockClicked`
|
||
- ✅ 重要性筛选 → `trackNewsFilterApplied`
|
||
- ✅ 排序更改 → `trackNewsSorted`
|
||
- ✅ 行业筛选 → `trackNewsFilterApplied`
|
||
- ✅ 热门关键词点击 → `trackNewsSearched`
|
||
- ✅ 交易时段筛选 → `trackNewsFilterApplied`
|
||
- ✅ 筛选重置 → `trackNewsFilterApplied`
|
||
- 追踪数据:搜索关键词、筛选类型、筛选值、时间戳
|
||
|
||
**实现策略**:
|
||
- 通过 props 传递追踪函数到子组件(`Community/index.js` → 子组件)
|
||
- 所有追踪函数从 `useCommunityEvents` hook 获取,无需额外创建
|
||
- 追踪调用为非阻塞操作,不影响用户体验
|
||
|
||
**优化效果**:
|
||
- ✅ 实现了 9 个 PostHog 追踪函数中的 6 个
|
||
- ✅ 覆盖了所有关键用户交互:浏览、点击、搜索、筛选
|
||
- ✅ 追踪数据结构化,便于后续分析
|
||
- ✅ 为产品优化和用户行为分析提供数据支持
|
||
|
||
**影响范围**:
|
||
- `src/views/Community/index.js` - 传递追踪函数到子组件
|
||
- `src/views/Community/components/HotEventsSection.js` - 接收并传递追踪函数
|
||
- `src/views/Community/components/HotEvents.js` - 热点事件点击追踪
|
||
- `src/views/Community/components/DynamicNewsCard.js` - 事件交互追踪
|
||
- `src/views/Community/components/UnifiedSearchBox.js` - 搜索和筛选追踪
|
||
|
||
**后续改进空间**:
|
||
- 可添加模式切换追踪(纵向 ↔ 四排)
|
||
- 可添加翻页行为追踪
|
||
- 可添加热点事件轮播翻页追踪
|
||
- 可添加相关概念点击追踪(`trackRelatedConceptClicked`)
|
||
|
||
---
|
||
|
||
### 2025-01-07: Community 页面代码清理
|
||
|
||
**变更类型**: 代码清理与优化
|
||
|
||
**背景**:
|
||
Community 页面存在大量已注释但未删除的代码,影响可维护性和代码可读性。
|
||
|
||
**变更内容**:
|
||
|
||
#### 删除的组件
|
||
- ❌ `EventTimelineCard.js` - 实时事件纵向列表组件(已被 DynamicNewsCard 替代)
|
||
- ❌ `EventModals.js` - 事件弹窗组件(功能已整合到 DynamicNewsCard)
|
||
|
||
#### 删除的代码
|
||
- ❌ 5 个不再使用的导入语句(EventTimelineCard, EventModals, usePostHogTrack, RETENTION_EVENTS, fetchDynamicNews)
|
||
- ❌ 4 个不再使用的状态和 Ref(selectedEvent, selectedEventForStock, eventTimelineRef, hasScrolledRef)
|
||
- ❌ 1 个不再使用的函数(scrollToTimeline)
|
||
- ❌ 3 处已注释的代码块(PostHog 追踪、EventTimelineCard 渲染、EventModals 渲染)
|
||
- ❌ 1 个重复的 useEffect(动态新闻每 5 分钟刷新,DynamicNewsCard 内部已管理)
|
||
|
||
#### 优化效果
|
||
- 📉 代码行数:207 行 → 138 行(减少 69 行,33% 代码量)
|
||
- ✅ 移除冗余代码,提高可维护性
|
||
- ✅ 减少导入依赖,加快编译速度
|
||
- ✅ 删除未使用的状态,减少内存占用
|
||
- ✅ 代码更清晰,更易理解
|
||
|
||
#### 影响范围
|
||
- `src/views/Community/index.js` - 主页面组件
|
||
- `src/views/Community/components/EventTimelineCard.js` - 已删除
|
||
- `src/views/Community/components/EventModals.js` - 已删除
|
||
|
||
#### 迁移说明
|
||
所有事件展示功能已统一由 `DynamicNewsCard` 组件实现,采用横向滚动布局,用户体验更佳。
|
||
|
||
**提交信息**:
|
||
```
|
||
feat: 删除不需要的组件
|
||
|
||
- 删除 EventTimelineCard 和 EventModals
|
||
- 清理未使用的导入和状态
|
||
- 移除已注释的代码块
|
||
- 优化页面结构,减少 33% 代码量
|
||
```
|
||
|
||
---
|
||
|
||
### 2025-01-XX: 纵向分栏模式滚动行为优化
|
||
|
||
**变更类型**: 用户体验优化
|
||
|
||
**优化内容**: 改进纵向分栏模式(VerticalModeLayout)的滚动交互体验,解决右侧详情面板滚动触发页面滚动以及固定模式切换逻辑的问题。
|
||
|
||
**改进点**:
|
||
|
||
1. **右侧详情面板滚动隔离** - `EventDetailScrollPanel.js`
|
||
- 添加 `overscrollBehavior: 'contain'` 属性,防止滚动传播到页面
|
||
- 添加 `data-detail-panel-container` 标识,与左侧列表区分
|
||
|
||
2. **左侧事件列表明确标识** - `VerticalModeLayout.js`
|
||
- 将 `data-scroll-container` 改为 `data-event-list-container`
|
||
- 明确标识左侧事件列表容器,便于固定模式逻辑识别
|
||
|
||
3. **固定模式进入/退出机制优化** - `DynamicNewsCard.js`
|
||
- **进入参数**: `TRIGGER_OFFSET = 150px`(原 100px)- 更平滑的视觉过渡
|
||
- **退出参数**:
|
||
- `EXIT_OFFSET = 200px` - 退出比进入更容易(非对称设计)
|
||
- `EXIT_THRESHOLD = 30px` - 接近顶部 30px 内即可退出(原需精确 0px)
|
||
- **退出逻辑**: 双重检测机制
|
||
- 条件 1: CardHeader 位置超过退出点 → 退出固定模式
|
||
- 条件 2: 左侧事件列表滚动到顶部附近 → 退出固定模式
|
||
- 任一条件满足即退出,提升用户体验
|
||
- **性能优化**: 在 `handleWheel` 中添加 `requestAnimationFrame`,提高滚动性能
|
||
|
||
**用户体验提升**:
|
||
- ✅ 右侧详情面板滚动不再触发整个页面滚动
|
||
- ✅ 右侧详情面板滚动不再影响固定模式切换
|
||
- ✅ 只有左侧事件列表滚动才会触发固定模式退出
|
||
- ✅ 固定模式进入/退出更流畅(150px/200px 非对称设计)
|
||
- ✅ 退出固定模式更容易(接近顶部即可,无需精确到 0px)
|
||
|
||
**技术细节**:
|
||
- 使用 CSS `overscrollBehavior` 隔离滚动链
|
||
- 使用 `data-*` 属性区分不同滚动容器类型
|
||
- 非对称触发距离设计(进入 150px,退出 200px)
|
||
- 双重检测退出条件(位置检测 OR 滚动检测)
|
||
|
||
**影响范围**:
|
||
- `src/views/Community/components/DynamicNewsCard/EventDetailScrollPanel.js`
|
||
- `src/views/Community/components/DynamicNewsCard/VerticalModeLayout.js`
|
||
- `src/views/Community/components/DynamicNewsCard.js`
|
||
|
||
---
|
||
|
||
### 2025-01-XX: CollapsibleSection 支持精简/详细模式切换
|
||
|
||
**变更类型**: 功能增强
|
||
|
||
**优化内容**: 为事件详情面板的 CollapsibleSection 组件添加精简/详细双模式切换功能,提升用户浏览体验。
|
||
|
||
**新增功能**:
|
||
|
||
1. **CollapsibleHeader.js** - 支持模式切换按钮
|
||
- 新增 `showModeToggle` prop:是否显示模式切换按钮
|
||
- 新增 `currentMode` prop:当前模式('detailed' | 'simple')
|
||
- 新增 `onModeToggle` prop:模式切换回调
|
||
- 按钮样式:
|
||
- 详细模式展开:`详细模式 ▲`
|
||
- 详细模式收起:`详细模式 ▼`
|
||
- 精简模式:`精简模式`
|
||
- 向后兼容:不提供 `showModeToggle` 时,保持原有的 IconButton 样式
|
||
|
||
2. **CollapsibleSection.js** - 支持精简/详细模式
|
||
- 新增 `simpleContent` prop:精简模式的内容
|
||
- 新增 `showModeToggle` prop:是否显示模式切换按钮
|
||
- 新增 `defaultMode` prop:默认模式(默认 'detailed')
|
||
- 模式切换逻辑:
|
||
- 详细模式 → 精简模式:显示 simpleContent
|
||
- 精简模式 → 详细模式:自动展开 children
|
||
- 权限控制:锁定状态下点击模式切换,触发付费弹窗
|
||
|
||
3. **SimpleStocksList.js** - 新增精简股票列表组件
|
||
- 横向展示股票名称和涨跌幅
|
||
- 自动根据涨跌幅显示颜色(红涨绿跌)
|
||
- 支持加载中状态
|
||
- 响应式设计,自动折行
|
||
|
||
4. **DynamicNewsDetailPanel.js** - 相关股票模块启用双模式
|
||
- 添加 `showModeToggle={true}`
|
||
- 添加 `simpleContent`:使用 SimpleStocksList 显示精简股票列表
|
||
- `children`:保持原有的 RelatedStocksSection(完整信息)
|
||
- 默认详细模式展开(PRO 会员)
|
||
|
||
**用户体验提升**:
|
||
- ✅ 支持精简/详细两种浏览模式,满足不同场景需求
|
||
- ✅ 精简模式:快速浏览股票名称和涨跌幅
|
||
- ✅ 详细模式:查看完整股票信息(价格、按钮等)
|
||
- ✅ 一键切换,无需重新加载数据
|
||
- ✅ PRO 会员默认展开详细模式,优化会员体验
|
||
|
||
**技术细节**:
|
||
- 使用 React useState 管理模式状态
|
||
- 模式切换不触发数据重新加载
|
||
- 向后兼容:不使用 `showModeToggle` 的 CollapsibleSection 保持原有行为
|
||
- 权限控制:锁定状态下切换模式触发付费引导
|
||
|
||
**影响范围**:
|
||
- `src/views/Community/components/DynamicNewsDetail/CollapsibleHeader.js`
|
||
- `src/views/Community/components/DynamicNewsDetail/CollapsibleSection.js`
|
||
- `src/views/Community/components/DynamicNewsDetail/SimpleStocksList.js`(新增)
|
||
- `src/views/Community/components/DynamicNewsDetail/DynamicNewsDetailPanel.js`
|
||
|
||
**扩展性**:
|
||
- 其他模块(历史事件对比、传导链分析)可以复用此功能
|
||
- 只需提供对应的 `simpleContent` 即可启用双模式
|
||
|
||
---
|
||
|
||
## 🔗 相关文档
|
||
|
||
- [项目总览 - CLAUDE.md](../CLAUDE.md)
|
||
- [通知系统 - NOTIFICATION_SYSTEM.md](./NOTIFICATION_SYSTEM.md)
|
||
- [路由系统 - src/routes/README.md](../src/routes/README.md)
|
||
- [Redux 状态管理 - src/store/README.md](../src/store/README.md)
|
||
- [PostHog 埋点追踪 - POSTHOG_TRACKING_GUIDE.md](./POSTHOG_TRACKING_GUIDE.md)
|
||
|
||
---
|
||
|
||
## 📝 文档维护
|
||
|
||
**更新时机**:
|
||
- 添加新功能模块时
|
||
- 重构组件结构时
|
||
- 修复重大 bug 时
|
||
- 性能优化时
|
||
|
||
**更新规范**:
|
||
- 保持目录结构清晰
|
||
- 添加代码示例
|
||
- 更新相关链接
|
||
- 记录变更历史
|
||
|
||
**贡献者**:
|
||
- 所有参与 Community 页面开发的工程师
|
||
|
||
---
|
||
|
||
**文档结束** 🎉
|