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:
@@ -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);
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
Reference in New Issue
Block a user