refactor: 抽取通用 Tab 容器组件,重构 BasicInfoTab 和 DeepAnalysisTab

新增组件:
- TabPanelContainer: 三级容器,统一 loading 状态 + VStack 布局 + 免责声明
- SubTabContainer: 二级导航容器,支持黑金/默认主题预设

重构:
- BasicInfoTab: 使用 SubTabContainer 替代原有 Tabs 实现
- DeepAnalysisTab: 拆分为 4 个子 Tab(战略分析/业务结构/产业链/发展历程)
- TabContainer: 样式调整,与 SubTabContainer 保持一致

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-12 11:55:50 +08:00
parent f85d0a99dd
commit 155a921113
15 changed files with 572 additions and 234 deletions

View File

@@ -0,0 +1,100 @@
/**
* TabPanelContainer - Tab 面板通用容器组件
*
* 提供统一的:
* - Loading 状态处理
* - VStack 布局
* - 免责声明(可选)
*
* @example
* ```tsx
* <TabPanelContainer loading={loading} showDisclaimer>
* <YourContent />
* </TabPanelContainer>
* ```
*/
import React, { memo } from 'react';
import { VStack, Center, Spinner, Text, Box } from '@chakra-ui/react';
// 默认免责声明文案
const DEFAULT_DISCLAIMER =
'免责声明本内容由AI模型基于新闻、公告、研报等公开信息自动分析和生成未经许可严禁转载。所有内容仅供参考不构成任何投资建议请投资者注意风险独立审慎决策。';
export interface TabPanelContainerProps {
/** 是否处于加载状态 */
loading?: boolean;
/** 加载状态显示的文案 */
loadingMessage?: string;
/** 加载状态高度 */
loadingHeight?: string;
/** 子组件间距,默认 6 */
spacing?: number;
/** 内边距,默认 4 */
padding?: number;
/** 是否显示免责声明,默认 false */
showDisclaimer?: boolean;
/** 自定义免责声明文案 */
disclaimerText?: string;
/** 子组件 */
children: React.ReactNode;
}
/**
* 加载状态组件
*/
const LoadingState: React.FC<{ message: string; height: string }> = ({
message,
height,
}) => (
<Center h={height}>
<VStack spacing={3}>
<Spinner size="lg" color="blue.500" thickness="3px" />
<Text fontSize="sm" color="gray.500">
{message}
</Text>
</VStack>
</Center>
);
/**
* 免责声明组件
*/
const DisclaimerText: React.FC<{ text: string }> = ({ text }) => (
<Text mt={4} color="gray.500" fontSize="12px" lineHeight="1.5">
{text}
</Text>
);
/**
* Tab 面板通用容器
*/
const TabPanelContainer: React.FC<TabPanelContainerProps> = memo(
({
loading = false,
loadingMessage = '加载中...',
loadingHeight = '200px',
spacing = 6,
padding = 4,
showDisclaimer = false,
disclaimerText = DEFAULT_DISCLAIMER,
children,
}) => {
if (loading) {
return <LoadingState message={loadingMessage} height={loadingHeight} />;
}
return (
<Box p={padding}>
<VStack spacing={spacing} align="stretch">
{children}
</VStack>
{showDisclaimer && <DisclaimerText text={disclaimerText} />}
</Box>
);
}
);
TabPanelContainer.displayName = 'TabPanelContainer';
export default TabPanelContainer;