refactor(DeepAnalysis): TypeScript 重构,提取 useDeepAnalysisData Hook
- 新增 types.ts:API 类型定义、状态接口、Tab 映射常量 - 新增 hooks/useDeepAnalysisData.ts:提取数据获取逻辑 - 懒加载:按 Tab 按需请求 - 数据缓存:已加载数据不重复请求 - 竞态处理:stockCode 变更时防止旧请求覆盖 - 重写 index.tsx:memo 优化,代码行数 229 → 81 - 新增 README.md:组件文档 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
79
src/views/Company/components/DeepAnalysis/README.md
Normal file
79
src/views/Company/components/DeepAnalysis/README.md
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# DeepAnalysis 组件
|
||||||
|
|
||||||
|
深度分析模块,展示公司战略分析、业务分析、产业链分析和发展历程。
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
DeepAnalysis/
|
||||||
|
├── index.tsx # 主组件入口(memo 优化)
|
||||||
|
├── types.ts # 类型定义
|
||||||
|
├── README.md # 本文档
|
||||||
|
│
|
||||||
|
└── hooks/
|
||||||
|
├── index.ts # Hooks 导出
|
||||||
|
└── useDeepAnalysisData.ts # 数据获取 Hook(懒加载、缓存)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
| 特性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| Tab 懒加载 | 切换 Tab 时按需加载数据 |
|
||||||
|
| 数据缓存 | 已加载的数据不重复请求 |
|
||||||
|
| 竞态处理 | stockCode 变更时防止旧请求覆盖新数据 |
|
||||||
|
| memo 优化 | 避免不必要的重渲染 |
|
||||||
|
|
||||||
|
## API 接口映射
|
||||||
|
|
||||||
|
| Tab | API Key | 接口 |
|
||||||
|
|-----|---------|------|
|
||||||
|
| 战略分析 | comprehensive | `/api/company/comprehensive-analysis` |
|
||||||
|
| 业务分析 | comprehensive | 同上(共用) |
|
||||||
|
| 产业链 | valueChain | `/api/company/value-chain-analysis` |
|
||||||
|
| 发展历程 | keyFactors | `/api/company/key-factors-timeline` |
|
||||||
|
|
||||||
|
## 数据流
|
||||||
|
|
||||||
|
```
|
||||||
|
DeepAnalysis
|
||||||
|
├── useDeepAnalysisData (Hook)
|
||||||
|
│ ├── data: { comprehensive, valueChain, keyFactors, industryRank }
|
||||||
|
│ ├── loading: { comprehensive, valueChain, keyFactors, industryRank }
|
||||||
|
│ └── loadTabData(tabKey) → loadApiData(apiKey)
|
||||||
|
│
|
||||||
|
└── DeepAnalysisTab (展示组件)
|
||||||
|
├── StrategyTab
|
||||||
|
├── BusinessTab
|
||||||
|
├── ValueChainTab
|
||||||
|
└── DevelopmentTab
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import DeepAnalysis from '@views/Company/components/DeepAnalysis';
|
||||||
|
|
||||||
|
<DeepAnalysis stockCode="600000" />
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hook 使用
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { useDeepAnalysisData } from '@views/Company/components/DeepAnalysis/hooks';
|
||||||
|
|
||||||
|
const { data, loading, loadTabData, resetData } = useDeepAnalysisData(stockCode);
|
||||||
|
|
||||||
|
// 手动加载某个 Tab
|
||||||
|
loadTabData('valueChain');
|
||||||
|
|
||||||
|
// 重置所有数据
|
||||||
|
resetData();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 性能优化
|
||||||
|
|
||||||
|
- `React.memo` 包装主组件
|
||||||
|
- `useCallback` 稳定化事件处理函数
|
||||||
|
- `useRef` 追踪已加载状态(避免重复请求)
|
||||||
|
- 竞态条件检测(stockCode 变更时忽略旧请求)
|
||||||
1
src/views/Company/components/DeepAnalysis/hooks/index.ts
Normal file
1
src/views/Company/components/DeepAnalysis/hooks/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { useDeepAnalysisData } from './useDeepAnalysisData';
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
/**
|
||||||
|
* useDeepAnalysisData Hook
|
||||||
|
*
|
||||||
|
* 管理深度分析模块的数据获取逻辑:
|
||||||
|
* - 按 Tab 懒加载数据
|
||||||
|
* - 已加载数据缓存,避免重复请求
|
||||||
|
* - 竞态条件处理
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useState, useCallback, useRef, useEffect } from 'react';
|
||||||
|
import axios from '@utils/axiosConfig';
|
||||||
|
import { logger } from '@utils/logger';
|
||||||
|
import type {
|
||||||
|
ApiKey,
|
||||||
|
ApiLoadingState,
|
||||||
|
DataState,
|
||||||
|
UseDeepAnalysisDataReturn,
|
||||||
|
} from '../types';
|
||||||
|
import { TAB_API_MAP } from '../types';
|
||||||
|
|
||||||
|
/** API 端点映射 */
|
||||||
|
const API_ENDPOINTS: Record<ApiKey, string> = {
|
||||||
|
comprehensive: '/api/company/comprehensive-analysis',
|
||||||
|
valueChain: '/api/company/value-chain-analysis',
|
||||||
|
keyFactors: '/api/company/key-factors-timeline',
|
||||||
|
industryRank: '/api/financial/industry-rank',
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 初始数据状态 */
|
||||||
|
const initialDataState: DataState = {
|
||||||
|
comprehensive: null,
|
||||||
|
valueChain: null,
|
||||||
|
keyFactors: null,
|
||||||
|
industryRank: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 初始 loading 状态 */
|
||||||
|
const initialLoadingState: ApiLoadingState = {
|
||||||
|
comprehensive: false,
|
||||||
|
valueChain: false,
|
||||||
|
keyFactors: false,
|
||||||
|
industryRank: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 深度分析数据 Hook
|
||||||
|
*
|
||||||
|
* @param stockCode 股票代码
|
||||||
|
* @returns 数据、loading 状态、加载函数
|
||||||
|
*/
|
||||||
|
export const useDeepAnalysisData = (stockCode: string): UseDeepAnalysisDataReturn => {
|
||||||
|
// 数据状态
|
||||||
|
const [data, setData] = useState<DataState>(initialDataState);
|
||||||
|
|
||||||
|
// Loading 状态
|
||||||
|
const [loading, setLoading] = useState<ApiLoadingState>(initialLoadingState);
|
||||||
|
|
||||||
|
// 已加载的接口记录
|
||||||
|
const loadedApisRef = useRef<Record<ApiKey, boolean>>({
|
||||||
|
comprehensive: false,
|
||||||
|
valueChain: false,
|
||||||
|
keyFactors: false,
|
||||||
|
industryRank: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 当前 stockCode(用于竞态条件检测)
|
||||||
|
const currentStockCodeRef = useRef(stockCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载指定 API 数据
|
||||||
|
*/
|
||||||
|
const loadApiData = useCallback(
|
||||||
|
async (apiKey: ApiKey) => {
|
||||||
|
if (!stockCode) return;
|
||||||
|
|
||||||
|
// 已加载则跳过
|
||||||
|
if (loadedApisRef.current[apiKey]) return;
|
||||||
|
|
||||||
|
// 设置 loading
|
||||||
|
setLoading((prev) => ({ ...prev, [apiKey]: true }));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const endpoint = `${API_ENDPOINTS[apiKey]}/${stockCode}`;
|
||||||
|
const { data: response } = await axios.get(endpoint);
|
||||||
|
|
||||||
|
// 检查 stockCode 是否已变更(防止竞态)
|
||||||
|
if (currentStockCodeRef.current !== stockCode) return;
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setData((prev) => ({ ...prev, [apiKey]: response.data }));
|
||||||
|
loadedApisRef.current[apiKey] = true;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('DeepAnalysis', `loadApiData:${apiKey}`, err, { stockCode });
|
||||||
|
} finally {
|
||||||
|
// 清除 loading(再次检查 stockCode)
|
||||||
|
if (currentStockCodeRef.current === stockCode) {
|
||||||
|
setLoading((prev) => ({ ...prev, [apiKey]: false }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[stockCode]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 Tab 加载对应数据
|
||||||
|
*/
|
||||||
|
const loadTabData = useCallback(
|
||||||
|
(tabKey: string) => {
|
||||||
|
const apiKey = TAB_API_MAP[tabKey];
|
||||||
|
if (apiKey) {
|
||||||
|
loadApiData(apiKey);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[loadApiData]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置所有数据
|
||||||
|
*/
|
||||||
|
const resetData = useCallback(() => {
|
||||||
|
setData(initialDataState);
|
||||||
|
setLoading(initialLoadingState);
|
||||||
|
loadedApisRef.current = {
|
||||||
|
comprehensive: false,
|
||||||
|
valueChain: false,
|
||||||
|
keyFactors: false,
|
||||||
|
industryRank: false,
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// stockCode 变更时重置并加载默认数据
|
||||||
|
useEffect(() => {
|
||||||
|
if (stockCode) {
|
||||||
|
currentStockCodeRef.current = stockCode;
|
||||||
|
resetData();
|
||||||
|
// 只加载默认 Tab(comprehensive)
|
||||||
|
loadApiData('comprehensive');
|
||||||
|
}
|
||||||
|
}, [stockCode, loadApiData, resetData]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
loading,
|
||||||
|
loadTabData,
|
||||||
|
resetData,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useDeepAnalysisData;
|
||||||
@@ -1,228 +0,0 @@
|
|||||||
// src/views/Company/components/DeepAnalysis/index.js
|
|
||||||
// 深度分析 - 独立一级 Tab 组件(懒加载版本)
|
|
||||||
|
|
||||||
import React, { useState, useEffect, useCallback, useRef } from "react";
|
|
||||||
import { logger } from "@utils/logger";
|
|
||||||
import axios from "@utils/axiosConfig";
|
|
||||||
|
|
||||||
// 复用原有的展示组件
|
|
||||||
import DeepAnalysisTab from "../CompanyOverview/DeepAnalysisTab";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tab 与 API 接口映射
|
|
||||||
* - strategy 和 business 共用 comprehensive 接口
|
|
||||||
*/
|
|
||||||
const TAB_API_MAP = {
|
|
||||||
strategy: "comprehensive",
|
|
||||||
business: "comprehensive",
|
|
||||||
valueChain: "valueChain",
|
|
||||||
development: "keyFactors",
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 深度分析组件
|
|
||||||
*
|
|
||||||
* 功能:
|
|
||||||
* - 按 Tab 懒加载数据(默认只加载战略分析)
|
|
||||||
* - 已加载的数据缓存,切换 Tab 不重复请求
|
|
||||||
* - 管理展开状态
|
|
||||||
*
|
|
||||||
* @param {Object} props
|
|
||||||
* @param {string} props.stockCode - 股票代码
|
|
||||||
*/
|
|
||||||
const DeepAnalysis = ({ stockCode }) => {
|
|
||||||
// 当前 Tab
|
|
||||||
const [activeTab, setActiveTab] = useState("strategy");
|
|
||||||
|
|
||||||
// 数据状态
|
|
||||||
const [comprehensiveData, setComprehensiveData] = useState(null);
|
|
||||||
const [valueChainData, setValueChainData] = useState(null);
|
|
||||||
const [keyFactorsData, setKeyFactorsData] = useState(null);
|
|
||||||
const [industryRankData, setIndustryRankData] = useState(null);
|
|
||||||
|
|
||||||
// 各接口独立的 loading 状态
|
|
||||||
const [comprehensiveLoading, setComprehensiveLoading] = useState(false);
|
|
||||||
const [valueChainLoading, setValueChainLoading] = useState(false);
|
|
||||||
const [keyFactorsLoading, setKeyFactorsLoading] = useState(false);
|
|
||||||
const [industryRankLoading, setIndustryRankLoading] = useState(false);
|
|
||||||
|
|
||||||
// 已加载的接口记录(用于缓存判断)
|
|
||||||
const loadedApisRef = useRef({
|
|
||||||
comprehensive: false,
|
|
||||||
valueChain: false,
|
|
||||||
keyFactors: false,
|
|
||||||
industryRank: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 业务板块展开状态
|
|
||||||
const [expandedSegments, setExpandedSegments] = useState({});
|
|
||||||
|
|
||||||
// 用于追踪当前 stockCode,避免竞态条件
|
|
||||||
const currentStockCodeRef = useRef(stockCode);
|
|
||||||
|
|
||||||
// 切换业务板块展开状态
|
|
||||||
const toggleSegmentExpansion = (segmentIndex) => {
|
|
||||||
setExpandedSegments((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[segmentIndex]: !prev[segmentIndex],
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 加载指定接口的数据
|
|
||||||
*/
|
|
||||||
const loadApiData = useCallback(
|
|
||||||
async (apiKey) => {
|
|
||||||
if (!stockCode) return;
|
|
||||||
|
|
||||||
// 已加载则跳过
|
|
||||||
if (loadedApisRef.current[apiKey]) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
switch (apiKey) {
|
|
||||||
case "comprehensive":
|
|
||||||
setComprehensiveLoading(true);
|
|
||||||
const { data: comprehensiveRes } = await axios.get(
|
|
||||||
`/api/company/comprehensive-analysis/${stockCode}`
|
|
||||||
);
|
|
||||||
// 检查 stockCode 是否已变更(防止竞态)
|
|
||||||
if (currentStockCodeRef.current === stockCode) {
|
|
||||||
if (comprehensiveRes.success)
|
|
||||||
setComprehensiveData(comprehensiveRes.data);
|
|
||||||
loadedApisRef.current.comprehensive = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "valueChain":
|
|
||||||
setValueChainLoading(true);
|
|
||||||
const { data: valueChainRes } = await axios.get(
|
|
||||||
`/api/company/value-chain-analysis/${stockCode}`
|
|
||||||
);
|
|
||||||
if (currentStockCodeRef.current === stockCode) {
|
|
||||||
if (valueChainRes.success) setValueChainData(valueChainRes.data);
|
|
||||||
loadedApisRef.current.valueChain = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "keyFactors":
|
|
||||||
setKeyFactorsLoading(true);
|
|
||||||
const { data: keyFactorsRes } = await axios.get(
|
|
||||||
`/api/company/key-factors-timeline/${stockCode}`
|
|
||||||
);
|
|
||||||
if (currentStockCodeRef.current === stockCode) {
|
|
||||||
if (keyFactorsRes.success) setKeyFactorsData(keyFactorsRes.data);
|
|
||||||
loadedApisRef.current.keyFactors = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "industryRank":
|
|
||||||
setIndustryRankLoading(true);
|
|
||||||
const { data: industryRankRes } = await axios.get(
|
|
||||||
`/api/financial/industry-rank/${stockCode}`
|
|
||||||
);
|
|
||||||
if (currentStockCodeRef.current === stockCode) {
|
|
||||||
if (industryRankRes.success) setIndustryRankData(industryRankRes.data);
|
|
||||||
loadedApisRef.current.industryRank = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
logger.error("DeepAnalysis", `loadApiData:${apiKey}`, err, {
|
|
||||||
stockCode,
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
// 清除 loading 状态
|
|
||||||
if (apiKey === "comprehensive") setComprehensiveLoading(false);
|
|
||||||
if (apiKey === "valueChain") setValueChainLoading(false);
|
|
||||||
if (apiKey === "keyFactors") setKeyFactorsLoading(false);
|
|
||||||
if (apiKey === "industryRank") setIndustryRankLoading(false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[stockCode]
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据 Tab 加载对应的数据
|
|
||||||
*/
|
|
||||||
const loadTabData = useCallback(
|
|
||||||
(tabKey) => {
|
|
||||||
const apiKey = TAB_API_MAP[tabKey];
|
|
||||||
if (apiKey) {
|
|
||||||
loadApiData(apiKey);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[loadApiData]
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tab 切换回调
|
|
||||||
*/
|
|
||||||
const handleTabChange = useCallback(
|
|
||||||
(index, tabKey) => {
|
|
||||||
setActiveTab(tabKey);
|
|
||||||
loadTabData(tabKey);
|
|
||||||
},
|
|
||||||
[loadTabData]
|
|
||||||
);
|
|
||||||
|
|
||||||
// stockCode 变更时重置并加载默认 Tab 数据
|
|
||||||
useEffect(() => {
|
|
||||||
if (stockCode) {
|
|
||||||
// 更新 ref
|
|
||||||
currentStockCodeRef.current = stockCode;
|
|
||||||
|
|
||||||
// 重置所有数据和状态
|
|
||||||
setComprehensiveData(null);
|
|
||||||
setValueChainData(null);
|
|
||||||
setKeyFactorsData(null);
|
|
||||||
setIndustryRankData(null);
|
|
||||||
setExpandedSegments({});
|
|
||||||
loadedApisRef.current = {
|
|
||||||
comprehensive: false,
|
|
||||||
valueChain: false,
|
|
||||||
keyFactors: false,
|
|
||||||
industryRank: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 重置为默认 Tab 并加载数据
|
|
||||||
setActiveTab("strategy");
|
|
||||||
// 只加载默认 Tab 的核心数据(comprehensive),其他数据按需加载
|
|
||||||
loadApiData("comprehensive");
|
|
||||||
}
|
|
||||||
}, [stockCode, loadApiData]);
|
|
||||||
|
|
||||||
// 计算当前 Tab 的 loading 状态
|
|
||||||
const getCurrentLoading = () => {
|
|
||||||
const apiKey = TAB_API_MAP[activeTab];
|
|
||||||
switch (apiKey) {
|
|
||||||
case "comprehensive":
|
|
||||||
return comprehensiveLoading;
|
|
||||||
case "valueChain":
|
|
||||||
return valueChainLoading;
|
|
||||||
case "keyFactors":
|
|
||||||
return keyFactorsLoading;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DeepAnalysisTab
|
|
||||||
comprehensiveData={comprehensiveData}
|
|
||||||
valueChainData={valueChainData}
|
|
||||||
keyFactorsData={keyFactorsData}
|
|
||||||
industryRankData={industryRankData}
|
|
||||||
loading={getCurrentLoading()}
|
|
||||||
cardBg="white"
|
|
||||||
expandedSegments={expandedSegments}
|
|
||||||
onToggleSegment={toggleSegmentExpansion}
|
|
||||||
activeTab={activeTab}
|
|
||||||
onTabChange={handleTabChange}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DeepAnalysis;
|
|
||||||
81
src/views/Company/components/DeepAnalysis/index.tsx
Normal file
81
src/views/Company/components/DeepAnalysis/index.tsx
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* DeepAnalysis - 深度分析组件
|
||||||
|
*
|
||||||
|
* 独立一级 Tab 组件,支持:
|
||||||
|
* - 按 Tab 懒加载数据
|
||||||
|
* - 已加载数据缓存
|
||||||
|
* - 业务板块展开状态管理
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useState, useCallback, useEffect, memo } from 'react';
|
||||||
|
import DeepAnalysisTab from '../CompanyOverview/DeepAnalysisTab';
|
||||||
|
import type { DeepAnalysisTabKey } from '../CompanyOverview/DeepAnalysisTab/types';
|
||||||
|
import { useDeepAnalysisData } from './hooks';
|
||||||
|
import { TAB_API_MAP } from './types';
|
||||||
|
import type { DeepAnalysisProps } from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 深度分析组件
|
||||||
|
*
|
||||||
|
* @param stockCode 股票代码
|
||||||
|
*/
|
||||||
|
const DeepAnalysis: React.FC<DeepAnalysisProps> = memo(({ stockCode }) => {
|
||||||
|
// 当前 Tab
|
||||||
|
const [activeTab, setActiveTab] = useState<DeepAnalysisTabKey>('strategy');
|
||||||
|
|
||||||
|
// 业务板块展开状态
|
||||||
|
const [expandedSegments, setExpandedSegments] = useState<Record<number, boolean>>({});
|
||||||
|
|
||||||
|
// 数据获取 Hook
|
||||||
|
const { data, loading, loadTabData } = useDeepAnalysisData(stockCode);
|
||||||
|
|
||||||
|
// stockCode 变更时重置 UI 状态
|
||||||
|
useEffect(() => {
|
||||||
|
if (stockCode) {
|
||||||
|
setActiveTab('strategy');
|
||||||
|
setExpandedSegments({});
|
||||||
|
}
|
||||||
|
}, [stockCode]);
|
||||||
|
|
||||||
|
// 切换业务板块展开状态
|
||||||
|
const toggleSegmentExpansion = useCallback((segmentIndex: number) => {
|
||||||
|
setExpandedSegments((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[segmentIndex]: !prev[segmentIndex],
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Tab 切换回调
|
||||||
|
const handleTabChange = useCallback(
|
||||||
|
(index: number, tabKey: DeepAnalysisTabKey) => {
|
||||||
|
setActiveTab(tabKey);
|
||||||
|
loadTabData(tabKey);
|
||||||
|
},
|
||||||
|
[loadTabData]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 获取当前 Tab 的 loading 状态
|
||||||
|
const currentLoading = (() => {
|
||||||
|
const apiKey = TAB_API_MAP[activeTab];
|
||||||
|
return apiKey ? loading[apiKey] : false;
|
||||||
|
})();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DeepAnalysisTab
|
||||||
|
comprehensiveData={data.comprehensive}
|
||||||
|
valueChainData={data.valueChain}
|
||||||
|
keyFactorsData={data.keyFactors}
|
||||||
|
industryRankData={data.industryRank}
|
||||||
|
loading={currentLoading}
|
||||||
|
cardBg="white"
|
||||||
|
expandedSegments={expandedSegments}
|
||||||
|
onToggleSegment={toggleSegmentExpansion}
|
||||||
|
activeTab={activeTab}
|
||||||
|
onTabChange={handleTabChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
DeepAnalysis.displayName = 'DeepAnalysis';
|
||||||
|
|
||||||
|
export default DeepAnalysis;
|
||||||
72
src/views/Company/components/DeepAnalysis/types.ts
Normal file
72
src/views/Company/components/DeepAnalysis/types.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* DeepAnalysis 组件类型定义
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 复用 DeepAnalysisTab 的数据类型
|
||||||
|
export type {
|
||||||
|
ComprehensiveData,
|
||||||
|
ValueChainData,
|
||||||
|
KeyFactorsData,
|
||||||
|
IndustryRankData,
|
||||||
|
DeepAnalysisTabKey,
|
||||||
|
} from '../CompanyOverview/DeepAnalysisTab/types';
|
||||||
|
|
||||||
|
/** API 接口类型 */
|
||||||
|
export type ApiKey = 'comprehensive' | 'valueChain' | 'keyFactors' | 'industryRank';
|
||||||
|
|
||||||
|
/** Tab 与 API 映射 */
|
||||||
|
export const TAB_API_MAP: Record<string, ApiKey> = {
|
||||||
|
strategy: 'comprehensive',
|
||||||
|
business: 'comprehensive',
|
||||||
|
valueChain: 'valueChain',
|
||||||
|
development: 'keyFactors',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
/** API 加载状态 */
|
||||||
|
export interface ApiLoadingState {
|
||||||
|
comprehensive: boolean;
|
||||||
|
valueChain: boolean;
|
||||||
|
keyFactors: boolean;
|
||||||
|
industryRank: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** API 已加载标记 */
|
||||||
|
export interface ApiLoadedState {
|
||||||
|
comprehensive: boolean;
|
||||||
|
valueChain: boolean;
|
||||||
|
keyFactors: boolean;
|
||||||
|
industryRank: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 数据状态 */
|
||||||
|
export interface DataState {
|
||||||
|
comprehensive: ComprehensiveData | null;
|
||||||
|
valueChain: ValueChainData | null;
|
||||||
|
keyFactors: KeyFactorsData | null;
|
||||||
|
industryRank: IndustryRankData[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Hook 返回值 */
|
||||||
|
export interface UseDeepAnalysisDataReturn {
|
||||||
|
/** 各接口数据 */
|
||||||
|
data: DataState;
|
||||||
|
/** 各接口 loading 状态 */
|
||||||
|
loading: ApiLoadingState;
|
||||||
|
/** 加载指定 Tab 的数据 */
|
||||||
|
loadTabData: (tabKey: string) => void;
|
||||||
|
/** 重置所有数据 */
|
||||||
|
resetData: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 组件 Props */
|
||||||
|
export interface DeepAnalysisProps {
|
||||||
|
stockCode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入类型用于内部使用
|
||||||
|
import type {
|
||||||
|
ComprehensiveData,
|
||||||
|
ValueChainData,
|
||||||
|
KeyFactorsData,
|
||||||
|
IndustryRankData,
|
||||||
|
} from '../CompanyOverview/DeepAnalysisTab/types';
|
||||||
Reference in New Issue
Block a user