refactor(StockQuoteCard): 数据下沉优化,Props 从 11 个精简为 4 个

- StockQuoteCard 使用内部 hooks 获取行情数据、基本信息和对比数据
- 更新 types.ts,简化 Props 接口
- Company/index.js 移除已下沉的数据获取逻辑(~40 行)
- 删除 Company/hooks/useStockQuote.js(已移至组件内部)

优化收益:
- Props 数量: 11 → 4 (-64%)
- Company/index.js: ~172 → ~105 行 (-39%)
- 组件可独立复用

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-17 11:17:07 +08:00
parent 348d8a0ec3
commit 7736212235
4 changed files with 65 additions and 238 deletions

View File

@@ -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<StockQuoteCardProps> = ({
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 (
<Card bg={cardBg} shadow="sm" borderWidth="1px" borderColor={borderColor}>
<CardBody>
@@ -80,16 +86,15 @@ const StockQuoteCard: React.FC<StockQuoteCardProps> = ({
<CardBody>
{/* 顶部:股票名称 + 关注/分享按钮 + 更新时间 */}
<StockHeader
name={data.name}
code={data.code}
industryL1={data.industryL1}
industry={data.industry}
indexTags={data.indexTags}
updateTime={data.updateTime}
name={quoteData.name}
code={quoteData.code}
industryL1={quoteData.industryL1}
industry={quoteData.industry}
indexTags={quoteData.indexTags}
updateTime={quoteData.updateTime}
isInWatchlist={isInWatchlist}
isWatchlistLoading={isWatchlistLoading}
onWatchlistToggle={onWatchlistToggle}
onShare={onShare}
isCompareLoading={isCompareLoading}
onCompare={handleCompare}
/>
@@ -98,7 +103,7 @@ const StockQuoteCard: React.FC<StockQuoteCardProps> = ({
<StockCompareModal
isOpen={isCompareModalOpen}
onClose={handleCloseCompare}
currentStock={data.code}
currentStock={quoteData.code}
currentStockInfo={currentStockInfo || null}
compareStock={compareStockInfo?.stock_code || ''}
compareStockInfo={compareStockInfo || null}
@@ -110,32 +115,32 @@ const StockQuoteCard: React.FC<StockQuoteCardProps> = ({
{/* 左栏:价格信息 (flex=1) */}
<Box flex="1" minWidth="0">
<PriceDisplay
currentPrice={data.currentPrice}
changePercent={data.changePercent}
currentPrice={quoteData.currentPrice}
changePercent={quoteData.changePercent}
/>
<SecondaryQuote
todayOpen={data.todayOpen}
yesterdayClose={data.yesterdayClose}
todayHigh={data.todayHigh}
todayLow={data.todayLow}
todayOpen={quoteData.todayOpen}
yesterdayClose={quoteData.yesterdayClose}
todayHigh={quoteData.todayHigh}
todayLow={quoteData.todayLow}
/>
</Box>
{/* 右栏:关键指标 + 主力动态 (flex=2) */}
<Flex flex="2" minWidth="0" gap={8} borderLeftWidth="1px" borderColor={borderColor} pl={8}>
<KeyMetrics
pe={data.pe}
eps={data.eps}
pb={data.pb}
marketCap={data.marketCap}
week52Low={data.week52Low}
week52High={data.week52High}
pe={quoteData.pe}
eps={quoteData.eps}
pb={quoteData.pb}
marketCap={quoteData.marketCap}
week52Low={quoteData.week52Low}
week52High={quoteData.week52High}
/>
<MainForceInfo
mainNetInflow={data.mainNetInflow}
institutionHolding={data.institutionHolding}
buyRatio={data.buyRatio}
sellRatio={data.sellRatio}
mainNetInflow={quoteData.mainNetInflow}
institutionHolding={quoteData.institutionHolding}
buyRatio={quoteData.buyRatio}
sellRatio={quoteData.sellRatio}
/>
</Flex>
</Flex>

View File

@@ -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 };