diff --git a/src/views/Company/components/StockQuoteCard/index.tsx b/src/views/Company/components/StockQuoteCard/index.tsx index a3560de8..a95b6452 100644 --- a/src/views/Company/components/StockQuoteCard/index.tsx +++ b/src/views/Company/components/StockQuoteCard/index.tsx @@ -3,6 +3,8 @@ * * 展示股票的实时行情、关键指标和主力动态 * 采用原子组件拆分,提高可维护性和复用性 + * + * 优化:数据获取已下沉到组件内部,Props 从 11 个精简为 4 个 */ import React from 'react'; @@ -26,42 +28,46 @@ import { StockCompareModal, STOCK_CARD_THEME, } from './components'; +import { useStockQuoteData, useStockCompare } from './hooks'; import type { StockQuoteCardProps } from './types'; const StockQuoteCard: React.FC = ({ - data, - isLoading = false, + stockCode, isInWatchlist = false, isWatchlistLoading = false, onWatchlistToggle, - onShare, - basicInfo, - // 对比相关 - currentStockInfo, - compareStockInfo, - isCompareLoading = false, - onCompare, - onCloseCompare, }) => { + // 内部获取行情数据和基本信息 + const { quoteData, basicInfo, isLoading } = useStockQuoteData(stockCode); + + // 内部管理股票对比逻辑 + const { + currentStockInfo, + compareStockInfo, + isCompareLoading, + handleCompare: triggerCompare, + clearCompare, + } = useStockCompare(stockCode); + // 对比弹窗控制 const { isOpen: isCompareModalOpen, onOpen: openCompareModal, onClose: closeCompareModal } = useDisclosure(); // 处理对比按钮点击 - const handleCompare = (stockCode: string) => { - onCompare?.(stockCode); + const handleCompare = (compareCode: string) => { + triggerCompare(compareCode); openCompareModal(); }; // 处理关闭对比弹窗 const handleCloseCompare = () => { closeCompareModal(); - onCloseCompare?.(); + clearCompare(); }; const { cardBg, borderColor } = STOCK_CARD_THEME; // 加载中或无数据时显示骨架屏 - if (isLoading || !data) { + if (isLoading || !quoteData) { return ( @@ -80,16 +86,15 @@ const StockQuoteCard: React.FC = ({ {/* 顶部:股票名称 + 关注/分享按钮 + 更新时间 */} @@ -98,7 +103,7 @@ const StockQuoteCard: React.FC = ({ = ({ {/* 左栏:价格信息 (flex=1) */} {/* 右栏:关键指标 + 主力动态 (flex=2) */} diff --git a/src/views/Company/components/StockQuoteCard/types.ts b/src/views/Company/components/StockQuoteCard/types.ts index fce90875..b1c38099 100644 --- a/src/views/Company/components/StockQuoteCard/types.ts +++ b/src/views/Company/components/StockQuoteCard/types.ts @@ -2,8 +2,8 @@ * StockQuoteCard 组件类型定义 */ -import type { BasicInfo } from '../CompanyOverview/types'; -import type { StockInfo } from '../FinancialPanorama/types'; +// 注:BasicInfo 和 StockInfo 类型由内部 hooks 使用,不再在 Props 中传递 +export type { StockInfo } from '../FinancialPanorama/types'; /** * 股票行情卡片数据 @@ -46,26 +46,18 @@ export interface StockQuoteCardData { } /** - * StockQuoteCard 组件 Props + * StockQuoteCard 组件 Props(优化后) + * + * 行情数据、基本信息、对比逻辑已下沉到组件内部 hooks 获取 + * Props 从 11 个精简为 4 个 */ export interface StockQuoteCardProps { - data?: StockQuoteCardData; - isLoading?: boolean; - // 自选股相关(与 WatchlistButton 接口保持一致) - isInWatchlist?: boolean; // 是否在自选股中 - isWatchlistLoading?: boolean; // 自选股操作加载中 - onWatchlistToggle?: () => void; // 自选股切换回调 - // 分享 - onShare?: () => void; // 分享回调 - // 公司基本信息 - basicInfo?: BasicInfo; - // 股票对比相关 - currentStockInfo?: StockInfo; // 当前股票财务信息(用于对比) - compareStockInfo?: StockInfo; // 对比股票财务信息 - isCompareLoading?: boolean; // 对比数据加载中 - onCompare?: (stockCode: string) => void; // 触发对比回调 - onCloseCompare?: () => void; // 关闭对比弹窗回调 + /** 股票代码 - 用于内部数据获取 */ + stockCode?: string; + /** 是否在自选股中(保留:涉及 Redux 和事件追踪回调) */ + isInWatchlist?: boolean; + /** 自选股操作加载中 */ + isWatchlistLoading?: boolean; + /** 自选股切换回调 */ + onWatchlistToggle?: () => void; } - -// 重新导出 StockInfo 类型以便外部使用 -export type { StockInfo }; diff --git a/src/views/Company/hooks/useStockQuote.js b/src/views/Company/hooks/useStockQuote.js deleted file mode 100644 index 84b5a806..00000000 --- a/src/views/Company/hooks/useStockQuote.js +++ /dev/null @@ -1,103 +0,0 @@ -// src/views/Company/hooks/useStockQuote.js -// 股票行情数据获取 Hook - -import { useState, useEffect } from 'react'; -import { stockService } from '@services/eventService'; -import { logger } from '@utils/logger'; - -/** - * 将 API 响应数据转换为 StockQuoteCard 所需格式 - */ -const transformQuoteData = (apiData, stockCode) => { - if (!apiData) return null; - - return { - // 基础信息 - name: apiData.name || apiData.stock_name || '未知', - code: apiData.code || apiData.stock_code || stockCode, - indexTags: apiData.index_tags || apiData.indexTags || [], - industry: apiData.industry || apiData.sw_industry_l2 || '', - industryL1: apiData.industry_l1 || apiData.sw_industry_l1 || '', - - // 价格信息 - currentPrice: apiData.current_price || apiData.currentPrice || apiData.close || 0, - changePercent: apiData.change_percent || apiData.changePercent || apiData.pct_chg || 0, - todayOpen: apiData.today_open || apiData.todayOpen || apiData.open || 0, - yesterdayClose: apiData.yesterday_close || apiData.yesterdayClose || apiData.pre_close || 0, - todayHigh: apiData.today_high || apiData.todayHigh || apiData.high || 0, - todayLow: apiData.today_low || apiData.todayLow || apiData.low || 0, - - // 关键指标 - pe: apiData.pe || apiData.pe_ttm || 0, - eps: apiData.eps || apiData.basic_eps || undefined, - pb: apiData.pb || apiData.pb_mrq || 0, - marketCap: apiData.market_cap || apiData.marketCap || apiData.circ_mv || '0', - week52Low: apiData.week52_low || apiData.week52Low || 0, - week52High: apiData.week52_high || apiData.week52High || 0, - - // 主力动态 - mainNetInflow: apiData.main_net_inflow || apiData.mainNetInflow || 0, - institutionHolding: apiData.institution_holding || apiData.institutionHolding || 0, - buyRatio: apiData.buy_ratio || apiData.buyRatio || 50, - sellRatio: apiData.sell_ratio || apiData.sellRatio || 50, - - // 更新时间 - updateTime: apiData.update_time || apiData.updateTime || new Date().toLocaleString(), - }; -}; - -/** - * 股票行情数据获取 Hook - * - * @param {string} stockCode - 股票代码 - * @returns {Object} { data, isLoading, error, refetch } - */ -export const useStockQuote = (stockCode) => { - const [data, setData] = useState(null); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(null); - - useEffect(() => { - if (!stockCode) { - setData(null); - return; - } - - const fetchQuote = async () => { - setIsLoading(true); - setError(null); - - try { - logger.debug('useStockQuote', '获取股票行情', { stockCode }); - const quotes = await stockService.getQuotes([stockCode]); - - // API 返回格式: { [stockCode]: quoteData } - const quoteData = quotes?.[stockCode] || quotes; - const transformedData = transformQuoteData(quoteData, stockCode); - - logger.debug('useStockQuote', '行情数据转换完成', { stockCode, hasData: !!transformedData }); - setData(transformedData); - } catch (err) { - logger.error('useStockQuote', '获取行情失败', err); - setError(err); - setData(null); - } finally { - setIsLoading(false); - } - }; - - fetchQuote(); - }, [stockCode]); - - // 手动刷新 - const refetch = () => { - if (stockCode) { - setData(null); - // 触发 useEffect 重新执行 - } - }; - - return { data, isLoading, error, refetch }; -}; - -export default useStockQuote; diff --git a/src/views/Company/index.js b/src/views/Company/index.js index c57f931e..0a18236e 100644 --- a/src/views/Company/index.js +++ b/src/views/Company/index.js @@ -1,19 +1,17 @@ // src/views/Company/index.js // 公司详情页面入口 - 纯组合层 +// +// 优化:行情数据、基本信息、对比逻辑已下沉到 StockQuoteCard 内部 -import React, { useEffect, useRef, useState, useCallback } from 'react'; -import { Container, VStack, useToast } from '@chakra-ui/react'; +import React, { useEffect, useRef } from 'react'; +import { Container, VStack } from '@chakra-ui/react'; import { useDispatch } from 'react-redux'; import { loadAllStocks } from '@store/slices/stockSlice'; -import { financialService } from '@services/financialService'; -import { logger } from '@utils/logger'; // 自定义 Hooks import { useCompanyStock } from './hooks/useCompanyStock'; import { useCompanyWatchlist } from './hooks/useCompanyWatchlist'; import { useCompanyEvents } from './hooks/useCompanyEvents'; -import { useStockQuote } from './hooks/useStockQuote'; -import { useBasicInfo } from './components/CompanyOverview/hooks/useBasicInfo'; // 页面组件 import CompanyHeader from './components/CompanyHeader'; @@ -31,9 +29,8 @@ import CompanyTabs from './components/CompanyTabs'; */ const CompanyIndex = () => { const dispatch = useDispatch(); - const toast = useToast(); - // 1. 先获取股票代码(不带追踪回调) + // 1. 获取股票代码(不带追踪回调) const { stockCode, inputCode, @@ -47,64 +44,7 @@ const CompanyIndex = () => { dispatch(loadAllStocks()); }, [dispatch]); - // 2. 获取股票行情数据 - const { data: quoteData, isLoading: isQuoteLoading } = useStockQuote(stockCode); - - // 2.1 获取公司基本信息 - const { basicInfo } = useBasicInfo(stockCode); - - // 5. 股票对比状态管理 - const [currentStockInfo, setCurrentStockInfo] = useState(null); - const [compareStockInfo, setCompareStockInfo] = useState(null); - const [isCompareLoading, setIsCompareLoading] = useState(false); - - // 加载当前股票财务信息(用于对比) - useEffect(() => { - const loadCurrentStockInfo = async () => { - if (!stockCode) return; - try { - const res = await financialService.getStockInfo(stockCode); - setCurrentStockInfo(res.data); - } catch (error) { - logger.error('CompanyIndex', 'loadCurrentStockInfo', error, { stockCode }); - } - }; - loadCurrentStockInfo(); - // 清除对比数据 - setCompareStockInfo(null); - }, [stockCode]); - - // 处理股票对比 - const handleCompare = useCallback(async (compareCode) => { - if (!compareCode) return; - - logger.debug('CompanyIndex', '开始加载对比数据', { stockCode, compareCode }); - setIsCompareLoading(true); - - try { - const res = await financialService.getStockInfo(compareCode); - setCompareStockInfo(res.data); - logger.info('CompanyIndex', '对比数据加载成功', { stockCode, compareCode }); - } catch (error) { - logger.error('CompanyIndex', 'handleCompare', error, { stockCode, compareCode }); - toast({ - title: '加载对比数据失败', - description: '请检查股票代码是否正确', - status: 'error', - duration: 3000, - }); - } finally { - setIsCompareLoading(false); - } - }, [stockCode, toast]); - - // 关闭对比弹窗 - const handleCloseCompare = useCallback(() => { - // 可选:清除对比数据 - // setCompareStockInfo(null); - }, []); - - // 3. 再初始化事件追踪(传入 stockCode) + // 2. 初始化事件追踪(传入 stockCode) const { trackStockSearched, trackTabChanged, @@ -147,19 +87,12 @@ const CompanyIndex = () => { /> {/* 股票行情卡片:价格、关键指标、主力动态、公司信息、股票对比 */} + {/* 优化:数据获取已下沉到组件内部,Props 从 11 个精简为 4 个 */} {/* Tab 切换区域:概览、行情、财务、预测 */}