refactor: 抽取 TabPanelContainer 通用容器组件

- 新增 TabPanelContainer 组件,统一处理 loading 状态和 VStack 布局
- ShareholderPanel 使用 TabPanelContainer 替代原有 loading 判断和 VStack
- ManagementPanel 使用 TabPanelContainer 替代原有 loading 判断和 VStack
- 组件使用 React.memo 优化渲染性能

🤖 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 10:58:25 +08:00
parent 26bc5fece0
commit 4672a24353
4 changed files with 64 additions and 16 deletions

View File

@@ -2,7 +2,7 @@
// 股权结构 Tab Panel - 使用拆分后的子组件
import React from "react";
import { VStack, SimpleGrid, Box } from "@chakra-ui/react";
import { SimpleGrid, Box } from "@chakra-ui/react";
import { useShareholderData } from "../../hooks/useShareholderData";
import {
@@ -10,7 +10,7 @@ import {
ConcentrationCard,
ShareholdersTable,
} from "../../components/shareholder";
import LoadingState from "./LoadingState";
import TabPanelContainer from "./TabPanelContainer";
interface ShareholderPanelProps {
stockCode: string;
@@ -32,12 +32,8 @@ const ShareholderPanel: React.FC<ShareholderPanelProps> = ({ stockCode }) => {
loading,
} = useShareholderData(stockCode);
if (loading) {
return <LoadingState message="加载股权结构数据..." />;
}
return (
<VStack spacing={6} align="stretch">
<TabPanelContainer loading={loading} loadingMessage="加载股权结构数据...">
{/* 实际控制人 + 股权集中度 左右分布 */}
<SimpleGrid columns={{ base: 1, lg: 2 }} spacing={6}>
<Box>
@@ -57,7 +53,7 @@ const ShareholderPanel: React.FC<ShareholderPanelProps> = ({ stockCode }) => {
<ShareholdersTable type="circulation" shareholders={topCirculationShareholders} />
</Box>
</SimpleGrid>
</VStack>
</TabPanelContainer>
);
};

View File

@@ -0,0 +1,56 @@
/**
* Tab 面板通用容器组件
*
* 提供统一的 loading 状态处理和布局包裹
* 用于 ShareholderPanel、ManagementPanel 等 Tab 面板
*/
import React, { memo } from 'react';
import { VStack } from '@chakra-ui/react';
import LoadingState from './LoadingState';
interface TabPanelContainerProps {
/** 是否处于加载状态 */
loading?: boolean;
/** 加载状态显示的文案 */
loadingMessage?: string;
/** 子组件间距,默认 6 */
spacing?: number;
/** 子组件 */
children: React.ReactNode;
}
/**
* Tab 面板通用容器
*
* 功能:
* 1. 统一处理 loading 状态,显示 LoadingState 组件
* 2. 提供 VStack 布局包裹,统一 spacing 和 align
*
* @example
* ```tsx
* <TabPanelContainer loading={loading} loadingMessage="加载数据...">
* <YourContent />
* </TabPanelContainer>
* ```
*/
const TabPanelContainer: React.FC<TabPanelContainerProps> = memo(({
loading = false,
loadingMessage = '加载中...',
spacing = 6,
children,
}) => {
if (loading) {
return <LoadingState message={loadingMessage} />;
}
return (
<VStack spacing={spacing} align="stretch">
{children}
</VStack>
);
});
TabPanelContainer.displayName = 'TabPanelContainer';
export default TabPanelContainer;

View File

@@ -2,6 +2,7 @@
// 组件导出
export { default as LoadingState } from "./LoadingState";
export { default as TabPanelContainer } from "./TabPanelContainer";
export { default as ShareholderPanel } from "./ShareholderPanel";
export { ManagementPanel } from "./management";
export { default as AnnouncementsPanel } from "./AnnouncementsPanel";

View File

@@ -2,7 +2,6 @@
// 管理团队 Tab Panel重构版
import React, { useMemo } from "react";
import { VStack } from "@chakra-ui/react";
import {
FaUserTie,
FaCrown,
@@ -12,7 +11,7 @@ import {
import { useManagementData } from "../../../hooks/useManagementData";
import { THEME } from "../../config";
import LoadingState from "../LoadingState";
import TabPanelContainer from "../TabPanelContainer";
import CategorySection from "./CategorySection";
import type {
ManagementPerson,
@@ -78,12 +77,8 @@ const ManagementPanel: React.FC<ManagementPanelProps> = ({ stockCode }) => {
[management]
);
if (loading) {
return <LoadingState message="加载管理团队数据..." />;
}
return (
<VStack spacing={6} align="stretch">
<TabPanelContainer loading={loading} loadingMessage="加载管理团队数据...">
{CATEGORY_ORDER.map((category) => {
const config = CATEGORY_CONFIG[category];
const people = categorizedManagement[category];
@@ -98,7 +93,7 @@ const ManagementPanel: React.FC<ManagementPanelProps> = ({ stockCode }) => {
/>
);
})}
</VStack>
</TabPanelContainer>
);
};