refactor: 重构 StockDetailPanel 目录结构,清理未使用代码
- 将 MiniTimelineChart 和 useEventStocks 迁移到 src/components/Charts/Stock/ - 更新 DynamicNewsDetailPanel 和 StockListItem 的导入路径 - 删除未使用的 SearchBox.js 和 useSearchEvents.js(约 300 行) - 建立统一的股票图表组件目录结构 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// src/views/Community/components/StockDetailPanel/components/MiniTimelineChart.js
|
||||
// src/components/Charts/Stock/MiniTimelineChart.js
|
||||
import React, { useState, useEffect, useMemo, useRef, useCallback } from 'react';
|
||||
import ReactECharts from 'echarts-for-react';
|
||||
import * as echarts from 'echarts';
|
||||
4
src/components/Charts/Stock/hooks/index.js
Normal file
4
src/components/Charts/Stock/hooks/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
// src/components/Charts/Stock/hooks/index.js
|
||||
// 股票图表 Hooks 统一导出
|
||||
|
||||
export { useEventStocks } from './useEventStocks';
|
||||
@@ -1,4 +1,4 @@
|
||||
// src/views/Community/components/StockDetailPanel/hooks/useEventStocks.js
|
||||
// src/components/Charts/Stock/hooks/useEventStocks.js
|
||||
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
|
||||
import { useEffect, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
@@ -8,8 +8,8 @@ import {
|
||||
fetchHistoricalEvents,
|
||||
fetchChainAnalysis,
|
||||
fetchExpectationScore
|
||||
} from '../../../../../store/slices/stockSlice';
|
||||
import { logger } from '../../../../../utils/logger';
|
||||
} from '@store/slices/stockSlice';
|
||||
import { logger } from '@utils/logger';
|
||||
|
||||
/**
|
||||
* 事件股票数据 Hook
|
||||
5
src/components/Charts/Stock/index.js
Normal file
5
src/components/Charts/Stock/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
// src/components/Charts/Stock/index.js
|
||||
// 股票图表组件统一导出
|
||||
|
||||
export { default as MiniTimelineChart } from './MiniTimelineChart';
|
||||
export { useEventStocks } from './hooks/useEventStocks';
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
} from '@chakra-ui/react';
|
||||
import { getImportanceConfig } from '@constants/importanceLevels';
|
||||
import { eventService } from '@services/eventService';
|
||||
import { useEventStocks } from '@views/Community/components/StockDetailPanel/hooks/useEventStocks';
|
||||
import { useEventStocks } from '@components/Charts/Stock';
|
||||
import { toggleEventFollow, selectEventFollowStatus } from '@store/slices/communityDataSlice';
|
||||
import { useAuth } from '@contexts/AuthContext';
|
||||
import EventHeaderInfo from './EventHeaderInfo';
|
||||
|
||||
@@ -20,7 +20,7 @@ import { StarIcon } from '@chakra-ui/icons';
|
||||
import { Tag } from 'antd';
|
||||
import { RobotOutlined } from '@ant-design/icons';
|
||||
import { selectIsMobile } from '@store/slices/deviceSlice';
|
||||
import MiniTimelineChart from '@views/Community/components/StockDetailPanel/components/MiniTimelineChart';
|
||||
import { MiniTimelineChart } from '@components/Charts/Stock';
|
||||
import MiniKLineChart from './MiniKLineChart';
|
||||
import TimelineChartModal from '@components/StockChart/TimelineChartModal';
|
||||
import KLineChartModal from '@components/StockChart/KLineChartModal';
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
// src/hooks/useSearchEvents.js
|
||||
// 全局搜索功能事件追踪 Hook
|
||||
|
||||
import { useCallback } from 'react';
|
||||
import { usePostHogTrack } from './usePostHogRedux';
|
||||
import { RETENTION_EVENTS } from '../lib/constants';
|
||||
import { logger } from '../utils/logger';
|
||||
|
||||
/**
|
||||
* 全局搜索事件追踪 Hook
|
||||
* @param {Object} options - 配置选项
|
||||
* @param {string} options.context - 搜索上下文 ('global' | 'stock' | 'news' | 'concept' | 'simulation')
|
||||
* @returns {Object} 事件追踪处理函数集合
|
||||
*/
|
||||
export const useSearchEvents = ({ context = 'global' } = {}) => {
|
||||
const { track } = usePostHogTrack();
|
||||
|
||||
/**
|
||||
* 追踪搜索开始(聚焦搜索框)
|
||||
* @param {string} placeholder - 搜索框提示文本
|
||||
*/
|
||||
const trackSearchInitiated = useCallback((placeholder = '') => {
|
||||
track(RETENTION_EVENTS.SEARCH_INITIATED, {
|
||||
context,
|
||||
placeholder,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
logger.debug('useSearchEvents', '🔍 Search Initiated', {
|
||||
context,
|
||||
placeholder,
|
||||
});
|
||||
}, [track, context]);
|
||||
|
||||
/**
|
||||
* 追踪搜索查询提交
|
||||
* @param {string} query - 搜索查询词
|
||||
* @param {number} resultCount - 搜索结果数量
|
||||
* @param {Object} filters - 应用的筛选条件
|
||||
*/
|
||||
const trackSearchQuerySubmitted = useCallback((query, resultCount = 0, filters = {}) => {
|
||||
if (!query) {
|
||||
logger.warn('useSearchEvents', 'trackSearchQuerySubmitted: query is required');
|
||||
return;
|
||||
}
|
||||
|
||||
track(RETENTION_EVENTS.SEARCH_QUERY_SUBMITTED, {
|
||||
query,
|
||||
query_length: query.length,
|
||||
result_count: resultCount,
|
||||
has_results: resultCount > 0,
|
||||
context,
|
||||
filters: filters,
|
||||
filter_count: Object.keys(filters).length,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
// 如果没有搜索结果,额外追踪
|
||||
if (resultCount === 0) {
|
||||
track(RETENTION_EVENTS.SEARCH_NO_RESULTS, {
|
||||
query,
|
||||
context,
|
||||
filters,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
logger.debug('useSearchEvents', '❌ Search No Results', {
|
||||
query,
|
||||
context,
|
||||
});
|
||||
} else {
|
||||
logger.debug('useSearchEvents', '✅ Search Query Submitted', {
|
||||
query,
|
||||
resultCount,
|
||||
context,
|
||||
});
|
||||
}
|
||||
}, [track, context]);
|
||||
|
||||
/**
|
||||
* 追踪搜索结果点击
|
||||
* @param {Object} result - 被点击的搜索结果
|
||||
* @param {string} result.type - 结果类型 ('stock' | 'news' | 'concept' | 'event')
|
||||
* @param {string} result.id - 结果ID
|
||||
* @param {string} result.title - 结果标题
|
||||
* @param {number} position - 在搜索结果中的位置
|
||||
* @param {string} query - 搜索查询词
|
||||
*/
|
||||
const trackSearchResultClicked = useCallback((result, position = 0, query = '') => {
|
||||
if (!result || !result.type) {
|
||||
logger.warn('useSearchEvents', 'trackSearchResultClicked: result object with type is required');
|
||||
return;
|
||||
}
|
||||
|
||||
track(RETENTION_EVENTS.SEARCH_RESULT_CLICKED, {
|
||||
result_type: result.type,
|
||||
result_id: result.id || result.code || '',
|
||||
result_title: result.title || result.name || '',
|
||||
position,
|
||||
query,
|
||||
context,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
logger.debug('useSearchEvents', '🎯 Search Result Clicked', {
|
||||
type: result.type,
|
||||
id: result.id || result.code,
|
||||
position,
|
||||
context,
|
||||
});
|
||||
}, [track, context]);
|
||||
|
||||
/**
|
||||
* 追踪搜索筛选应用
|
||||
* @param {Object} filters - 应用的筛选条件
|
||||
* @param {string} filterType - 筛选类型 ('sort' | 'category' | 'date_range' | 'price_range')
|
||||
* @param {any} filterValue - 筛选值
|
||||
*/
|
||||
const trackSearchFilterApplied = useCallback((filterType, filterValue, filters = {}) => {
|
||||
if (!filterType) {
|
||||
logger.warn('useSearchEvents', 'trackSearchFilterApplied: filterType is required');
|
||||
return;
|
||||
}
|
||||
|
||||
track(RETENTION_EVENTS.SEARCH_FILTER_APPLIED, {
|
||||
filter_type: filterType,
|
||||
filter_value: String(filterValue),
|
||||
all_filters: filters,
|
||||
context,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
logger.debug('useSearchEvents', '🔍 Search Filter Applied', {
|
||||
filterType,
|
||||
filterValue,
|
||||
context,
|
||||
});
|
||||
}, [track, context]);
|
||||
|
||||
/**
|
||||
* 追踪搜索建议点击(自动完成)
|
||||
* @param {string} suggestion - 被点击的搜索建议
|
||||
* @param {number} position - 在建议列表中的位置
|
||||
* @param {string} source - 建议来源 ('history' | 'popular' | 'related')
|
||||
*/
|
||||
const trackSearchSuggestionClicked = useCallback((suggestion, position = 0, source = 'popular') => {
|
||||
if (!suggestion) {
|
||||
logger.warn('useSearchEvents', 'trackSearchSuggestionClicked: suggestion is required');
|
||||
return;
|
||||
}
|
||||
|
||||
track('Search Suggestion Clicked', {
|
||||
suggestion,
|
||||
position,
|
||||
source,
|
||||
context,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
logger.debug('useSearchEvents', '💡 Search Suggestion Clicked', {
|
||||
suggestion,
|
||||
position,
|
||||
source,
|
||||
context,
|
||||
});
|
||||
}, [track, context]);
|
||||
|
||||
/**
|
||||
* 追踪搜索历史查看
|
||||
* @param {number} historyCount - 历史记录数量
|
||||
*/
|
||||
const trackSearchHistoryViewed = useCallback((historyCount = 0) => {
|
||||
track('Search History Viewed', {
|
||||
history_count: historyCount,
|
||||
has_history: historyCount > 0,
|
||||
context,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
logger.debug('useSearchEvents', '📜 Search History Viewed', {
|
||||
historyCount,
|
||||
context,
|
||||
});
|
||||
}, [track, context]);
|
||||
|
||||
/**
|
||||
* 追踪搜索历史清除
|
||||
*/
|
||||
const trackSearchHistoryCleared = useCallback(() => {
|
||||
track('Search History Cleared', {
|
||||
context,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
logger.debug('useSearchEvents', '🗑️ Search History Cleared', {
|
||||
context,
|
||||
});
|
||||
}, [track, context]);
|
||||
|
||||
/**
|
||||
* 追踪热门搜索词点击
|
||||
* @param {string} keyword - 被点击的热门关键词
|
||||
* @param {number} position - 在列表中的位置
|
||||
* @param {number} heatScore - 热度分数
|
||||
*/
|
||||
const trackPopularKeywordClicked = useCallback((keyword, position = 0, heatScore = 0) => {
|
||||
if (!keyword) {
|
||||
logger.warn('useSearchEvents', 'trackPopularKeywordClicked: keyword is required');
|
||||
return;
|
||||
}
|
||||
|
||||
track('Popular Keyword Clicked', {
|
||||
keyword,
|
||||
position,
|
||||
heat_score: heatScore,
|
||||
context,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
logger.debug('useSearchEvents', '🔥 Popular Keyword Clicked', {
|
||||
keyword,
|
||||
position,
|
||||
context,
|
||||
});
|
||||
}, [track, context]);
|
||||
|
||||
return {
|
||||
// 搜索流程事件
|
||||
trackSearchInitiated,
|
||||
trackSearchQuerySubmitted,
|
||||
trackSearchResultClicked,
|
||||
|
||||
// 筛选和建议
|
||||
trackSearchFilterApplied,
|
||||
trackSearchSuggestionClicked,
|
||||
|
||||
// 历史和热门
|
||||
trackSearchHistoryViewed,
|
||||
trackSearchHistoryCleared,
|
||||
trackPopularKeywordClicked,
|
||||
};
|
||||
};
|
||||
|
||||
export default useSearchEvents;
|
||||
@@ -1,59 +0,0 @@
|
||||
// src/views/Community/components/SearchBox.js
|
||||
import React from 'react';
|
||||
import { Card, Input, Radio, Form, Button } from 'antd';
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
import { useSearchEvents } from '../../../hooks/useSearchEvents';
|
||||
|
||||
const SearchBox = ({ onSearch }) => {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
// 🎯 初始化搜索埋点Hook
|
||||
const searchEvents = useSearchEvents({ context: 'community' });
|
||||
|
||||
const handleSubmit = (values) => {
|
||||
// 🎯 追踪搜索查询提交(在调用onSearch之前)
|
||||
if (values.q) {
|
||||
searchEvents.trackSearchQuerySubmitted(values.q, 0, {
|
||||
search_type: values.search_type || 'topic'
|
||||
});
|
||||
}
|
||||
onSearch(values);
|
||||
};
|
||||
|
||||
return (
|
||||
<Card title="搜索事件" className="search-box" style={{ marginBottom: 16 }}>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={handleSubmit}
|
||||
initialValues={{ search_type: 'topic' }}
|
||||
>
|
||||
<Form.Item name="q" style={{ marginBottom: 12 }}>
|
||||
<Input.Search
|
||||
placeholder="搜索关键词..."
|
||||
allowClear
|
||||
enterButton={<SearchOutlined />}
|
||||
onSearch={(value) => {
|
||||
form.setFieldsValue({ q: value });
|
||||
form.submit();
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="search_type" style={{ marginBottom: 12 }}>
|
||||
<Radio.Group>
|
||||
<Radio value="topic">搜索话题</Radio>
|
||||
<Radio value="stock">搜索股票</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item style={{ marginBottom: 0 }}>
|
||||
<Button type="primary" htmlType="submit" block>
|
||||
搜索
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchBox;
|
||||
@@ -1,2 +0,0 @@
|
||||
// src/views/Community/components/StockDetailPanel/components/index.js
|
||||
export { default as MiniTimelineChart } from './MiniTimelineChart';
|
||||
Reference in New Issue
Block a user