# Company 目录结构说明 > 最后更新:2025-12-10 ## 目录结构 ``` src/views/Company/ ├── index.js # 页面入口(95行,纯组合层) ├── STRUCTURE.md # 本文档 │ ├── components/ # UI 组件 │ ├── CompanyHeader/ # 页面头部 │ │ ├── index.js # 组合导出 │ │ ├── SearchBar.js # 股票搜索栏 │ │ └── WatchlistButton.js # 自选股按钮 │ │ │ ├── CompanyTabs/ # Tab 切换容器 │ │ ├── index.js # Tab 容器(状态管理 + 内容渲染) │ │ └── TabNavigation.js # Tab 导航栏 │ │ │ ├── CompanyOverview/ # Tab: 公司概览(TypeScript 拆分) │ │ ├── index.tsx # 主组件(组合层,约 50 行) │ │ ├── CompanyHeaderCard.tsx # 头部卡片组件(黑金主题,约 200 行) │ │ ├── BasicInfoTab/ # 基本信息 Tab(TypeScript 可配置化重构) │ │ │ ├── index.tsx # 主组件(可配置,约 120 行) │ │ │ ├── config.ts # Tab 配置 + 黑金主题(约 90 行) │ │ │ ├── utils.ts # 格式化工具函数(约 50 行) │ │ │ └── components/ # 子组件 │ │ │ ├── index.ts # 组件统一导出 │ │ │ ├── LoadingState.tsx # 加载状态组件(黑金主题 Spinner) │ │ │ ├── ShareholderPanel.tsx # 股权结构面板(实控人、十大股东、股权集中度) │ │ │ ├── ManagementPanel.tsx # 管理团队面板(高管列表表格) │ │ │ ├── AnnouncementsPanel.tsx # 公告信息面板(公告列表 + 披露日程) │ │ │ ├── BranchesPanel.tsx # 分支机构面板(分支列表表格) │ │ │ └── BusinessInfoPanel.tsx # 工商信息面板(注册资本、成立日期等) │ │ ├── DeepAnalysisTab.js # 深度分析 Tab │ │ ├── NewsEventsTab.js # 新闻事件 Tab │ │ ├── types.ts # 类型定义(约 120 行) │ │ ├── utils.ts # 格式化工具(约 20 行) │ │ └── hooks/ │ │ └── useCompanyOverviewData.ts # 数据 Hook(约 100 行) │ │ │ ├── MarketDataView/ # Tab: 股票行情(TypeScript 拆分) │ │ ├── index.tsx # 主组件入口(~1049 行) │ │ ├── types.ts # 类型定义(~383 行) │ │ ├── constants.ts # 主题配置、常量 │ │ ├── services/ │ │ │ └── marketService.ts # API 服务层 │ │ ├── hooks/ │ │ │ └── useMarketData.ts # 数据获取 Hook │ │ ├── utils/ │ │ │ ├── formatUtils.ts # 格式化工具函数 │ │ │ └── chartOptions.ts # ECharts 图表配置生成器 │ │ └── components/ │ │ ├── index.ts # 组件导出 │ │ ├── ThemedCard.tsx # 主题化卡片 │ │ ├── MarkdownRenderer.tsx # Markdown 渲染 │ │ ├── StockSummaryCard.tsx # 股票概览卡片 │ │ └── AnalysisModal.tsx # 涨幅分析模态框 │ │ │ ├── FinancialPanorama/ # Tab: 财务全景(2153 行,待拆分) │ │ └── index.js │ │ │ └── ForecastReport/ # Tab: 盈利预测(161 行,待拆分) │ └── index.js │ ├── hooks/ # 自定义 Hooks │ ├── useCompanyStock.js # 股票代码管理(URL 同步) │ ├── useCompanyWatchlist.js # 自选股管理(Redux 集成) │ └── useCompanyEvents.js # PostHog 事件追踪 │ └── constants/ # 常量定义 └── index.js # Tab 配置、Toast 消息、默认值 ``` --- ## 文件职责说明 ### 入口文件 #### `index.js` - 页面入口 - **职责**:纯组合层,协调 Hooks 和 Components - **代码行数**:95 行 - **依赖**: - `useCompanyStock` - 股票代码状态 - `useCompanyWatchlist` - 自选股状态 - `useCompanyEvents` - 事件追踪 - `CompanyHeader` - 页面头部 - `CompanyTabs` - Tab 切换区 --- ### Hooks 目录 #### `useCompanyStock.js` - 股票代码管理 - **功能**: - 管理当前股票代码状态 - 双向同步 URL 参数(支持浏览器前进/后退) - 处理搜索输入和提交 - **返回值**: ```js { stockCode, // 当前确认的股票代码 inputCode, // 输入框中的值(未确认) setInputCode, // 更新输入框 handleSearch, // 执行搜索 handleKeyPress, // 处理回车键 } ``` - **依赖**:`react-router-dom` (useSearchParams) #### `useCompanyWatchlist.js` - 自选股管理 - **功能**: - 检查当前股票是否在自选股中 - 提供添加/移除自选股功能 - 与 Redux stockSlice 同步 - **返回值**: ```js { isInWatchlist, // 是否在自选股中 isLoading, // 操作进行中 toggle, // 切换自选状态 } ``` - **依赖**:Redux (`stockSlice`)、`AuthContext`、Chakra UI (useToast) #### `useCompanyEvents.js` - 事件追踪 - **功能**: - 页面浏览追踪 - 股票搜索追踪 - Tab 切换追踪 - 自选股操作追踪 - **返回值**: ```js { trackStockSearched, // 追踪股票搜索 trackTabChanged, // 追踪 Tab 切换 trackWatchlistAdded, // 追踪添加自选 trackWatchlistRemoved, // 追踪移除自选 } ``` - **依赖**:PostHog (`usePostHogTrack`) --- ### Components 目录 #### `CompanyHeader/` - 页面头部 | 文件 | 职责 | |------|------| | `index.js` | 组合 SearchBar 和 WatchlistButton | | `SearchBar.js` | 股票代码搜索输入框 | | `WatchlistButton.js` | 自选股添加/移除按钮 | **Props 接口**: ```js ``` #### `CompanyTabs/` - Tab 切换 | 文件 | 职责 | |------|------| | `index.js` | Tab 容器,管理切换状态,渲染 Tab 内容 | | `TabNavigation.js` | Tab 导航栏(4个 Tab 按钮) | **Props 接口**: ```js ``` --- ### Constants 目录 #### `constants/index.js` - 常量配置 - `COMPANY_TABS` - Tab 配置数组(key, name, icon) - `TAB_SELECTED_STYLE` - Tab 选中样式 - `TOAST_MESSAGES` - Toast 消息配置 - `DEFAULT_STOCK_CODE` - 默认股票代码 ('000001') - `URL_PARAM_NAME` - URL 参数名 ('scode') - `getTabNameByIndex()` - 根据索引获取 Tab 名称 --- ### Tab 内容组件(`components/` 目录下) | 组件 | Tab 名称 | 职责 | 代码行数 | |------|----------|------|----------| | `CompanyOverview/` | 公司概览 | 公司基本信息、相关事件 | - | | `MarketDataView/` | 股票行情 | K线图、实时行情 | - | | `FinancialPanorama/` | 财务全景 | 财务报表、指标分析 | 2153 行 | | `ForecastReport/` | 盈利预测 | 分析师预测、目标价 | 161 行 | > 📌 所有 Tab 内容组件已文件夹化并统一放置在 `components/` 目录下 --- ## 数据流示意 ``` ┌─────────────────────────────────────────────────────────────┐ │ index.js (页面入口) │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │ │ │ useCompanyStock │ │useCompanyWatchlist│ │useCompanyEvents│ │ │ │ │ │ │ │ │ │ │ • stockCode │ │ • isInWatchlist │ │ • track* │ │ │ │ • inputCode │ │ • toggle │ │ functions │ │ │ │ • handleSearch │ │ │ │ │ │ │ └────────┬────────┘ └────────┬────────┘ └──────┬──────┘ │ │ │ │ │ │ │ └──────────┬─────────┴───────────────────┘ │ │ ▼ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ CompanyHeader │ │ │ │ ┌─────────────┐ ┌──────────────────┐ │ │ │ │ │ SearchBar │ │ WatchlistButton │ │ │ │ │ └─────────────┘ └──────────────────┘ │ │ │ └───────────────────────────────────────────────────────┘ │ │ ▼ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ CompanyTabs │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │ │ TabNavigation │ │ │ │ │ │ [概览] [行情] [财务] [预测] │ │ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │ │ TabPanels │ │ │ │ │ │ • CompanyOverview │ │ │ │ │ │ • MarketDataView │ │ │ │ │ │ • FinancialPanorama │ │ │ │ │ │ • ForecastReport │ │ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ └───────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` --- ## 重构记录 ### 2025-12-09 重构 **改动概述**: - `index.js` 从 **349 行** 精简至 **95 行**(减少 73%) - 提取 **3 个自定义 Hooks** - 提取 **2 个组件目录**(CompanyHeader、CompanyTabs) - 抽离常量到 `constants/index.js` **修复的问题**: 1. **无限循环 Bug**:`useCompanyWatchlist` 中使用 `useRef` 防止重复初始化 2. **Hook 调用顺序**:确保 `useCompanyEvents` 在 `useCompanyStock` 之后调用(依赖 stockCode) 3. **类型检查**:`CompanyOverview.js` 中 `event.keywords` 渲染时添加类型检查,支持字符串和对象两种格式 **设计原则**: - **关注点分离**:每个 Hook 只负责单一职责 - **纯组合层**:index.js 不包含业务逻辑,只负责组合 - **Props 透传**:通过 Props 将状态和回调传递给子组件 ### 2025-12-09 文件夹化 **改动概述**: - 所有 4 个 Tab 内容组件统一移动到 `components/` 目录 - `CompanyOverview.js` → `components/CompanyOverview/index.js` - `MarketDataView.js` → `components/MarketDataView/index.js` - `FinancialPanorama.js` → `components/FinancialPanorama/index.js`(2153 行) - `ForecastReport.js` → `components/ForecastReport/index.js`(161 行) - 更新 `CompanyTabs/index.js` 中的导入路径 **目的**: - 统一目录结构,所有组件都在 `components/` 下 - 为后期组件拆分做准备,便于添加子组件、hooks、utils 等 ### 2025-12-10 CompanyOverview 拆分(TypeScript) **改动概述**: - `CompanyOverview/index.js` 从 **330 行** 精简至 **50 行**(减少 85%) - 采用 **TypeScript** 进行拆分,提高类型安全性 - 提取 **1 个自定义 Hook**(`useCompanyOverviewData`) - 提取 **1 个子组件**(`CompanyHeaderCard`) - 抽离类型定义到 `types.ts` - 抽离工具函数到 `utils.ts` **拆分后文件结构**: ``` CompanyOverview/ ├── index.tsx # 主组件(组合层,约 60 行) ├── CompanyHeaderCard.tsx # 头部卡片组件(约 130 行) ├── BasicInfoTab.js # 基本信息 Tab(懒加载版本,约 994 行) ├── DeepAnalysisTab.js # 深度分析 Tab ├── NewsEventsTab.js # 新闻事件 Tab ├── types.ts # 类型定义(约 50 行) ├── utils.ts # 格式化工具(约 20 行) └── hooks/ ├── useBasicInfo.ts # 基本信息 Hook(1 API) ├── useShareholderData.ts # 股权结构 Hook(4 APIs) ├── useManagementData.ts # 管理团队 Hook(1 API) ├── useAnnouncementsData.ts # 公告数据 Hook(1 API) ├── useBranchesData.ts # 分支机构 Hook(1 API) ├── useDisclosureData.ts # 披露日程 Hook(1 API) └── useCompanyOverviewData.ts # [已废弃] 原合并 Hook ``` **懒加载架构**(2025-12-10 优化): - `index.tsx` 只加载 `useBasicInfo`(1 个 API)用于头部卡片 - `BasicInfoTab.js` 使用 `isLazy` + 独立子组件实现懒加载 - 每个内层 Tab 使用独立 Hook,点击时才加载数据 **Hooks 说明**: | Hook | API 数量 | 用途 | |------|----------|------| | `useBasicInfo` | 1 | 公司基本信息(头部卡片 + 工商信息 Tab) | | `useShareholderData` | 4 | 实控人、股权集中度、十大股东、十大流通股东 | | `useManagementData` | 1 | 管理团队数据 | | `useAnnouncementsData` | 1 | 公司公告列表 | | `useBranchesData` | 1 | 分支机构列表 | | `useDisclosureData` | 1 | 财报披露日程 | **类型定义**(`types.ts`): - `BasicInfo` - 公司基本信息 - `ActualControl` - 实际控制人 - `Concentration` - 股权集中度 - `Management` - 管理层信息 - `Shareholder` - 股东信息 - `Branch` - 分支机构 - `Announcement` - 公告信息 - `DisclosureSchedule` - 披露计划 - `CompanyOverviewData` - Hook 返回值类型 - `CompanyOverviewProps` - 组件 Props 类型 - `CompanyHeaderCardProps` - 头部卡片 Props 类型 **工具函数**(`utils.ts`): - `formatRegisteredCapital(value)` - 格式化注册资本(万元/亿元) - `formatDate(dateString)` - 格式化日期 **设计原则**: - **渐进式 TypeScript 迁移**:新拆分的文件使用 TypeScript,旧文件暂保持 JS - **关注点分离**:数据加载逻辑提取到 Hook,UI 逻辑保留在组件 - **类型复用**:统一的类型定义便于在多个文件间共享 - **懒加载优化**:减少首屏 API 请求,按需加载数据 ### 2025-12-10 懒加载优化 **改动概述**: - 将 `useCompanyOverviewData`(9 个 API)拆分为 6 个独立 Hook - `CompanyOverview/index.tsx` 只加载 `useBasicInfo`(1 个 API) - `BasicInfoTab.js` 使用 5 个懒加载子组件,配合 `isLazy` 实现按需加载 - 页面初次加载从 **9 个 API** 减少到 **1 个 API** **懒加载子组件**(BasicInfoTab.js 内部): | 子组件 | Hook | 功能 | |--------|------|------| | `ShareholderTabPanel` | `useShareholderData` | 股权结构(4 APIs) | | `ManagementTabPanel` | `useManagementData` | 管理团队 | | `AnnouncementsTabPanel` | `useAnnouncementsData` + `useDisclosureData` | 公告 + 披露日程 | | `BranchesTabPanel` | `useBranchesData` | 分支机构 | | `BusinessInfoTabPanel` | - | 工商信息(使用父组件传入的 basicInfo) | **实现原理**: - Chakra UI `Tabs` 的 `isLazy` 属性延迟渲染 TabPanel - 每个 TabPanel 使用独立子组件,组件内调用 Hook - 子组件只在首次激活时渲染,此时 Hook 才执行并发起 API 请求 | Tab 模块 | 中文名称 | 功能说明 | |-------------------|------|----------------------------| | CompanyOverview | 公司概览 | 公司基本信息、股权结构、管理层、公告等(9个接口) | | DeepAnalysis | 深度分析 | 公司深度研究报告、投资逻辑分析 | | MarketDataView | 股票行情 | K线图、实时行情、技术指标 | | FinancialPanorama | 财务全景 | 财务报表(资产负债表、利润表、现金流)、财务指标分析 | | ForecastReport | 盈利预测 | 分析师预测、目标价、评级 | | DynamicTracking | 动态跟踪 | 相关事件、新闻动态、投资日历 | ### 2025-12-10 MarketDataView TypeScript 拆分 **改动概述**: - `MarketDataView/index.js` 从 **2060 行** 拆分为 **12 个 TypeScript 文件** - 采用 **TypeScript** 进行重构,提高类型安全性 - 提取 **1 个自定义 Hook**(`useMarketData`) - 提取 **4 个子组件**(ThemedCard、MarkdownRenderer、StockSummaryCard、AnalysisModal) - 抽离 API 服务到 `services/marketService.ts` - 抽离图表配置到 `utils/chartOptions.ts` **拆分后文件结构**: ``` MarketDataView/ ├── index.tsx # 主组件入口(~1049 行) ├── types.ts # 类型定义(~383 行) ├── constants.ts # 主题配置、常量(~49 行) ├── services/ │ └── marketService.ts # API 服务层(~173 行) ├── hooks/ │ └── useMarketData.ts # 数据获取 Hook(~193 行) ├── utils/ │ ├── formatUtils.ts # 格式化工具函数(~175 行) │ └── chartOptions.ts # ECharts 图表配置生成器(~698 行) └── components/ ├── index.ts # 组件导出(~8 行) ├── ThemedCard.tsx # 主题化卡片(~32 行) ├── MarkdownRenderer.tsx # Markdown 渲染(~65 行) ├── StockSummaryCard.tsx # 股票概览卡片(~133 行) └── AnalysisModal.tsx # 涨幅分析模态框(~188 行) ``` **文件职责说明**: | 文件 | 行数 | 职责 | |------|------|------| | `index.tsx` | ~1049 | 主组件,包含 5 个 Tab 面板(交易数据、融资融券、大宗交易、龙虎榜、股权质押) | | `types.ts` | ~383 | 所有 TypeScript 类型定义(Theme、TradeDayData、MinuteData、FundingData 等) | | `constants.ts` | ~49 | 主题配置(light/dark)、周期选项常量 | | `marketService.ts` | ~173 | API 服务封装(getMarketData、getMinuteData、getBigDealData 等) | | `useMarketData.ts` | ~193 | 数据获取 Hook,管理所有市场数据状态 | | `formatUtils.ts` | ~175 | 数字/日期/涨跌幅格式化工具 | | `chartOptions.ts` | ~698 | ECharts 配置生成器(K线图、分钟图、融资融券图、质押图) | | `ThemedCard.tsx` | ~32 | 主题化卡片容器组件 | | `MarkdownRenderer.tsx` | ~65 | Markdown 内容渲染组件 | | `StockSummaryCard.tsx` | ~133 | 股票概览卡片(价格、涨跌幅、成交量等) | | `AnalysisModal.tsx` | ~188 | 涨幅分析详情模态框 | **类型定义**(`types.ts`): - `Theme` - 主题配置类型 - `TradeDayData` - 日线交易数据 - `MinuteData` - 分钟线数据 - `FundingDayData` - 融资融券数据 - `BigDealData` / `BigDealDayStats` - 大宗交易数据 - `UnusualData` / `UnusualDayData` - 龙虎榜数据 - `PledgeData` - 股权质押数据 - `RiseAnalysis` - 涨幅分析数据 - `MarketSummary` - 市场概览数据 - `VerificationReport` - 验证报告数据 - 各组件 Props 类型 **Hook 返回值**(`useMarketData`): ```typescript { loading: boolean; summary: MarketSummary | null; tradeData: TradeDayData[]; minuteData: MinuteData | null; minuteLoading: boolean; fundingData: FundingDayData[]; bigDealData: BigDealData | null; unusualData: UnusualData | null; pledgeData: PledgeData | null; analysisMap: Record; refetch: () => Promise; loadMinuteData: () => Promise; } ``` **设计原则**: - **TypeScript 类型安全**:所有数据结构有完整类型定义 - **服务层分离**:API 调用统一在 `marketService.ts` 中管理 - **图表配置抽离**:复杂的 ECharts 配置集中在 `chartOptions.ts` - **组件复用**:通用组件(ThemedCard、MarkdownRenderer)可在其他模块使用