perf: CompanyOverview Tab 懒加载优化

- 拆分 loadData 为 loadBasicInfoData 和 loadDeepAnalysisData
- 首次加载仅请求 9 个基本信息接口(原 12 个)
- 深度分析 3 个接口切换 Tab 时按需加载
- 新闻动态 1 个接口切换 Tab 时按需加载
- 调整 Tab 顺序:基本信息 → 深度分析 → 新闻动态
- stockCode 变更时重置 Tab 状态和数据

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-09 17:37:11 +08:00
parent d7759b1da3
commit 04ce16df56
2 changed files with 111 additions and 57 deletions

View File

@@ -155,6 +155,7 @@ const BasicInfoTab = ({
branches = [], branches = [],
disclosureSchedule = [], disclosureSchedule = [],
cardBg, cardBg,
loading = false,
}) => { }) => {
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const [selectedAnnouncement, setSelectedAnnouncement] = React.useState(null); const [selectedAnnouncement, setSelectedAnnouncement] = React.useState(null);

View File

@@ -79,12 +79,29 @@ const formatUtils = {
// 主组件 // 主组件
const CompanyAnalysisComplete = ({ stockCode: propStockCode }) => { const CompanyAnalysisComplete = ({ stockCode: propStockCode }) => {
const [stockCode, setStockCode] = useState(propStockCode || "000001"); const [stockCode, setStockCode] = useState(propStockCode || "000001");
const [loading, setLoading] = useState(false);
// 监听props中的stockCode变化 // Tab 懒加载状态追踪
const [tabsLoaded, setTabsLoaded] = useState({
basicInfo: false,
deepAnalysis: false,
newsEvents: false,
});
const [activeTabIndex, setActiveTabIndex] = useState(0);
const [basicInfoLoading, setBasicInfoLoading] = useState(false);
const [deepAnalysisLoading, setDeepAnalysisLoading] = useState(false);
// 监听props中的stockCode变化 - 重置Tab状态
useEffect(() => { useEffect(() => {
if (propStockCode && propStockCode !== stockCode) { if (propStockCode && propStockCode !== stockCode) {
setStockCode(propStockCode); setStockCode(propStockCode);
// 重置 Tab 状态
setTabsLoaded({ basicInfo: false, deepAnalysis: false, newsEvents: false });
setActiveTabIndex(0);
// 清空深度分析和新闻数据
setComprehensiveData(null);
setValueChainData(null);
setKeyFactorsData(null);
setNewsEvents([]);
} }
}, [propStockCode, stockCode]); }, [propStockCode, stockCode]);
@@ -135,24 +152,15 @@ const CompanyAnalysisComplete = ({ stockCode: propStockCode }) => {
})); }));
}; };
// 加载数据 // 加载基本信息数据9个接口- 首次加载
const loadData = async () => { const loadBasicInfoData = async () => {
setLoading(true); if (tabsLoaded.basicInfo) return;
setBasicInfoLoading(true);
setError(null); setError(null);
try { try {
const requests = [ const requests = [
// 深度分析数据
fetch(
`${API_BASE_URL}/api/company/comprehensive-analysis/${stockCode}`
).then((r) => r.json()),
fetch(
`${API_BASE_URL}/api/company/value-chain-analysis/${stockCode}`
).then((r) => r.json()),
fetch(
`${API_BASE_URL}/api/company/key-factors-timeline/${stockCode}`
).then((r) => r.json()),
// 股票概览数据
fetch(`${API_BASE_URL}/api/stock/${stockCode}/basic-info`).then((r) => fetch(`${API_BASE_URL}/api/stock/${stockCode}/basic-info`).then((r) =>
r.json() r.json()
), ),
@@ -183,9 +191,6 @@ const CompanyAnalysisComplete = ({ stockCode: propStockCode }) => {
]; ];
const [ const [
comprehensiveRes,
valueChainRes,
keyFactorsRes,
basicRes, basicRes,
actualRes, actualRes,
concentrationRes, concentrationRes,
@@ -197,11 +202,6 @@ const CompanyAnalysisComplete = ({ stockCode: propStockCode }) => {
disclosureRes, disclosureRes,
] = await Promise.all(requests); ] = await Promise.all(requests);
// 设置深度分析数据
if (comprehensiveRes.success) setComprehensiveData(comprehensiveRes.data);
if (valueChainRes.success) setValueChainData(valueChainRes.data);
if (keyFactorsRes.success) setKeyFactorsData(keyFactorsRes.data);
// 设置股票概览数据 // 设置股票概览数据
if (basicRes.success) setBasicInfo(basicRes.data); if (basicRes.success) setBasicInfo(basicRes.data);
if (actualRes.success) setActualControl(actualRes.data); if (actualRes.success) setActualControl(actualRes.data);
@@ -213,21 +213,61 @@ const CompanyAnalysisComplete = ({ stockCode: propStockCode }) => {
if (branchesRes.success) setBranches(branchesRes.data); if (branchesRes.success) setBranches(branchesRes.data);
if (announcementsRes.success) setAnnouncements(announcementsRes.data); if (announcementsRes.success) setAnnouncements(announcementsRes.data);
if (disclosureRes.success) setDisclosureSchedule(disclosureRes.data); if (disclosureRes.success) setDisclosureSchedule(disclosureRes.data);
setTabsLoaded((prev) => ({ ...prev, basicInfo: true }));
} catch (err) { } catch (err) {
setError(err.message); setError(err.message);
logger.error("CompanyOverview", "loadData", err, { stockCode }); logger.error("CompanyOverview", "loadBasicInfoData", err, { stockCode });
} finally { } finally {
setLoading(false); setBasicInfoLoading(false);
} }
}; };
// 加载深度分析数据3个接口- Tab切换时加载
const loadDeepAnalysisData = async () => {
if (tabsLoaded.deepAnalysis) return;
setDeepAnalysisLoading(true);
try {
const requests = [
fetch(
`${API_BASE_URL}/api/company/comprehensive-analysis/${stockCode}`
).then((r) => r.json()),
fetch(
`${API_BASE_URL}/api/company/value-chain-analysis/${stockCode}`
).then((r) => r.json()),
fetch(
`${API_BASE_URL}/api/company/key-factors-timeline/${stockCode}`
).then((r) => r.json()),
];
const [comprehensiveRes, valueChainRes, keyFactorsRes] =
await Promise.all(requests);
// 设置深度分析数据
if (comprehensiveRes.success) setComprehensiveData(comprehensiveRes.data);
if (valueChainRes.success) setValueChainData(valueChainRes.data);
if (keyFactorsRes.success) setKeyFactorsData(keyFactorsRes.data);
setTabsLoaded((prev) => ({ ...prev, deepAnalysis: true }));
} catch (err) {
logger.error("CompanyOverview", "loadDeepAnalysisData", err, {
stockCode,
});
} finally {
setDeepAnalysisLoading(false);
}
};
// 首次加载 - 只加载基本信息
useEffect(() => { useEffect(() => {
if (stockCode) { if (stockCode) {
loadData(); loadBasicInfoData();
} }
}, [stockCode]); }, [stockCode]);
// 加载新闻事件 // 加载新闻事件1个接口- Tab切换时加载
const loadNewsEvents = async (page = 1, searchQuery = "") => { const loadNewsEvents = async (page = 1, searchQuery = "") => {
setNewsLoading(true); setNewsLoading(true);
try { try {
@@ -261,6 +301,11 @@ const CompanyAnalysisComplete = ({ stockCode: propStockCode }) => {
setNewsEvents(events); setNewsEvents(events);
setNewsPagination(pagination); setNewsPagination(pagination);
// 首次加载时标记为已加载
if (page === 1 && !tabsLoaded.newsEvents) {
setTabsLoaded((prev) => ({ ...prev, newsEvents: true }));
}
} catch (err) { } catch (err) {
logger.error("CompanyOverview", "loadNewsEvents", err, { logger.error("CompanyOverview", "loadNewsEvents", err, {
stockCode, stockCode,
@@ -281,13 +326,6 @@ const CompanyAnalysisComplete = ({ stockCode: propStockCode }) => {
} }
}; };
// 当基本信息加载完成后,加载新闻事件
useEffect(() => {
if (basicInfo) {
loadNewsEvents(1);
}
}, [basicInfo]);
// 处理新闻搜索 // 处理新闻搜索
const handleNewsSearch = () => { const handleNewsSearch = () => {
loadNewsEvents(1, newsSearchQuery); loadNewsEvents(1, newsSearchQuery);
@@ -298,7 +336,20 @@ const CompanyAnalysisComplete = ({ stockCode: propStockCode }) => {
loadNewsEvents(newPage, newsSearchQuery); loadNewsEvents(newPage, newsSearchQuery);
}; };
if (loading) { // Tab 切换处理 - 懒加载
const handleTabChange = (index) => {
setActiveTabIndex(index);
// index 0: 基本信息 - 已首次加载
// index 1: 深度分析 - 切换时加载
// index 2: 新闻动态 - 切换时加载
if (index === 1 && !tabsLoaded.deepAnalysis) {
loadDeepAnalysisData();
} else if (index === 2 && !tabsLoaded.newsEvents && basicInfo) {
loadNewsEvents(1);
}
};
if (basicInfoLoading && !basicInfo) {
return ( return (
<Box bg={bgColor} minH="100vh" p={4}> <Box bg={bgColor} minH="100vh" p={4}>
<Container maxW="container.xl"> <Container maxW="container.xl">
@@ -466,12 +517,13 @@ const CompanyAnalysisComplete = ({ stockCode: propStockCode }) => {
</Card> </Card>
)} )}
{/* 主要内容区 - 分为深度分析、基本信息和新闻动态 */} {/* 主要内容区 - 分为基本信息、深度分析和新闻动态 */}
<Tabs <Tabs
variant="soft-rounded" variant="soft-rounded"
colorScheme="blue" colorScheme="blue"
size="lg" size="lg"
defaultIndex={0} index={activeTabIndex}
onChange={handleTabChange}
> >
<TabList <TabList
bg={cardBg} bg={cardBg}
@@ -480,14 +532,14 @@ const CompanyAnalysisComplete = ({ stockCode: propStockCode }) => {
shadow="md" shadow="md"
flexWrap="wrap" flexWrap="wrap"
> >
<Tab fontWeight="bold">
<Icon as={FaBrain} mr={2} />
深度分析
</Tab>
<Tab fontWeight="bold"> <Tab fontWeight="bold">
<Icon as={FaInfoCircle} mr={2} /> <Icon as={FaInfoCircle} mr={2} />
基本信息 基本信息
</Tab> </Tab>
<Tab fontWeight="bold">
<Icon as={FaBrain} mr={2} />
深度分析
</Tab>
<Tab fontWeight="bold"> <Tab fontWeight="bold">
<Icon as={FaNewspaper} mr={2} /> <Icon as={FaNewspaper} mr={2} />
新闻动态 新闻动态
@@ -495,20 +547,7 @@ const CompanyAnalysisComplete = ({ stockCode: propStockCode }) => {
</TabList> </TabList>
<TabPanels> <TabPanels>
{/* 深度分析标签页 */} {/* 基本信息标签页 - 默认 Tab */}
<TabPanel p={0} pt={6}>
<DeepAnalysisTab
comprehensiveData={comprehensiveData}
valueChainData={valueChainData}
keyFactorsData={keyFactorsData}
loading={loading}
cardBg={cardBg}
expandedSegments={expandedSegments}
onToggleSegment={toggleSegmentExpansion}
/>
</TabPanel>
{/* 基本信息标签页 */}
<TabPanel p={0} pt={6}> <TabPanel p={0} pt={6}>
<BasicInfoTab <BasicInfoTab
basicInfo={basicInfo} basicInfo={basicInfo}
@@ -521,10 +560,24 @@ const CompanyAnalysisComplete = ({ stockCode: propStockCode }) => {
branches={branches} branches={branches}
disclosureSchedule={disclosureSchedule} disclosureSchedule={disclosureSchedule}
cardBg={cardBg} cardBg={cardBg}
loading={basicInfoLoading}
/> />
</TabPanel> </TabPanel>
{/* 新闻动态标签页 */} {/* 深度分析标签页 - 切换时加载 */}
<TabPanel p={0} pt={6}>
<DeepAnalysisTab
comprehensiveData={comprehensiveData}
valueChainData={valueChainData}
keyFactorsData={keyFactorsData}
loading={deepAnalysisLoading}
cardBg={cardBg}
expandedSegments={expandedSegments}
onToggleSegment={toggleSegmentExpansion}
/>
</TabPanel>
{/* 新闻动态标签页 - 切换时加载 */}
<TabPanel p={0} pt={6}> <TabPanel p={0} pt={6}>
<NewsEventsTab <NewsEventsTab
newsEvents={newsEvents} newsEvents={newsEvents}