From 357b8bbdd7fceca19df426f83c3ce9e7ec672f74 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Tue, 28 Oct 2025 21:52:27 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20Company=20-=205=E4=B8=AA=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=EF=BC=88=E9=A1=B5=E9=9D=A2=E6=B5=8F=E8=A7=88=E3=80=81?= =?UTF-8?q?=E8=82=A1=E7=A5=A8=E6=90=9C=E7=B4=A2=E3=80=81Tab=20=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E3=80=81=E8=87=AA=E9=80=89=E8=82=A1=E7=AE=A1=E7=90=86?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/Company/hooks/useCompanyEvents.js | 103 ++++++++++++++++++++ src/views/Company/index.js | 39 +++++++- 2 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 src/views/Company/hooks/useCompanyEvents.js diff --git a/src/views/Company/hooks/useCompanyEvents.js b/src/views/Company/hooks/useCompanyEvents.js new file mode 100644 index 00000000..5b5ed769 --- /dev/null +++ b/src/views/Company/hooks/useCompanyEvents.js @@ -0,0 +1,103 @@ +// src/views/Company/hooks/useCompanyEvents.js +// 公司详情页面事件追踪 Hook + +import { useCallback, useEffect } from 'react'; +import { usePostHogTrack } from '../../../hooks/usePostHogRedux'; +import { RETENTION_EVENTS } from '../../../lib/constants'; +import { logger } from '../../../utils/logger'; + +/** + * 公司详情页面事件追踪 Hook + * @param {Object} options - 配置选项 + * @param {string} options.stockCode - 当前股票代码 + * @returns {Object} 事件追踪处理函数集合 + */ +export const useCompanyEvents = ({ stockCode } = {}) => { + const { track } = usePostHogTrack(); + + // 🎯 页面浏览事件 - 页面加载时触发 + useEffect(() => { + track(RETENTION_EVENTS.COMPANY_PAGE_VIEWED, { + timestamp: new Date().toISOString(), + stock_code: stockCode || null, + }); + logger.debug('useCompanyEvents', '📊 Company Page Viewed', { stockCode }); + }, [track, stockCode]); + + /** + * 追踪股票搜索/切换 + * @param {string} newStockCode - 新的股票代码 + * @param {string} previousStockCode - 之前的股票代码 + */ + const trackStockSearched = useCallback((newStockCode, previousStockCode = null) => { + if (!newStockCode) return; + + track(RETENTION_EVENTS.STOCK_SEARCHED, { + query: newStockCode, + stock_code: newStockCode, + previous_stock_code: previousStockCode, + context: 'company_page', + }); + + logger.debug('useCompanyEvents', '🔍 Stock Searched', { + newStockCode, + previousStockCode, + }); + }, [track]); + + /** + * 追踪 Tab 切换 + * @param {number} tabIndex - Tab 索引 (0: 公司概览, 1: 股票行情, 2: 财务全景, 3: 盈利预测) + * @param {string} tabName - Tab 名称 + * @param {number} previousTabIndex - 之前的 Tab 索引 + */ + const trackTabChanged = useCallback((tabIndex, tabName, previousTabIndex = null) => { + track(RETENTION_EVENTS.TAB_CHANGED, { + tab_index: tabIndex, + tab_name: tabName, + previous_tab_index: previousTabIndex, + stock_code: stockCode, + context: 'company_page', + }); + + logger.debug('useCompanyEvents', '🔄 Tab Changed', { + tabIndex, + tabName, + previousTabIndex, + stockCode, + }); + }, [track, stockCode]); + + /** + * 追踪加入自选股 + * @param {string} stock_code - 股票代码 + */ + const trackWatchlistAdded = useCallback((stock_code) => { + track(RETENTION_EVENTS.WATCHLIST_ADDED, { + stock_code, + source: 'company_page', + }); + + logger.debug('useCompanyEvents', '⭐ Watchlist Added', { stock_code }); + }, [track]); + + /** + * 追踪移除自选股 + * @param {string} stock_code - 股票代码 + */ + const trackWatchlistRemoved = useCallback((stock_code) => { + track(RETENTION_EVENTS.WATCHLIST_REMOVED, { + stock_code, + source: 'company_page', + }); + + logger.debug('useCompanyEvents', '❌ Watchlist Removed', { stock_code }); + }, [track]); + + return { + trackStockSearched, + trackTabChanged, + trackWatchlistAdded, + trackWatchlistRemoved, + }; +}; diff --git a/src/views/Company/index.js b/src/views/Company/index.js index 4a9cd4be..bdcc8253 100644 --- a/src/views/Company/index.js +++ b/src/views/Company/index.js @@ -34,6 +34,8 @@ import FinancialPanorama from './FinancialPanorama'; import ForecastReport from './ForecastReport'; import MarketDataView from './MarketDataView'; import CompanyOverview from './CompanyOverview'; +// 导入 PostHog 追踪 Hook +import { useCompanyEvents } from './hooks/useCompanyEvents'; const CompanyIndex = () => { const [searchParams, setSearchParams] = useSearchParams(); @@ -42,7 +44,18 @@ const CompanyIndex = () => { const { colorMode, toggleColorMode } = useColorMode(); const toast = useToast(); const { isAuthenticated } = useAuth(); - + + // 🎯 PostHog 事件追踪 + const { + trackStockSearched, + trackTabChanged, + trackWatchlistAdded, + trackWatchlistRemoved, + } = useCompanyEvents({ stockCode }); + + // Tab 索引状态(用于追踪 Tab 切换) + const [currentTabIndex, setCurrentTabIndex] = useState(0); + const bgColor = useColorModeValue('white', 'gray.800'); const tabBg = useColorModeValue('gray.50', 'gray.700'); const activeBg = useColorModeValue('blue.500', 'blue.400'); @@ -86,6 +99,9 @@ const CompanyIndex = () => { const handleSearch = () => { if (inputCode && inputCode !== stockCode) { + // 🎯 追踪股票搜索 + trackStockSearched(inputCode, stockCode); + setStockCode(inputCode); setSearchParams({ scode: inputCode }); } @@ -123,6 +139,10 @@ const CompanyIndex = () => { logger.api.response('DELETE', url, resp.status); if (!resp.ok) throw new Error('删除失败'); + + // 🎯 追踪移除自选 + trackWatchlistRemoved(stockCode); + setIsInWatchlist(false); toast({ title: '已从自选移除', status: 'info', duration: 1500 }); } else { @@ -140,6 +160,10 @@ const CompanyIndex = () => { logger.api.response('POST', url, resp.status); if (!resp.ok) throw new Error('添加失败'); + + // 🎯 追踪加入自选 + trackWatchlistAdded(stockCode); + setIsInWatchlist(true); toast({ title: '已加入自选', status: 'success', duration: 1500 }); } @@ -226,7 +250,18 @@ const CompanyIndex = () => { {/* 数据展示区域 */} - + { + const tabNames = ['公司概览', '股票行情', '财务全景', '盈利预测']; + // 🎯 追踪 Tab 切换 + trackTabChanged(index, tabNames[index], currentTabIndex); + setCurrentTabIndex(index); + }} + >