From 90e2a48d66cca1b45c9f74aed7ad2e660d567e77 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Fri, 19 Dec 2025 18:53:50 +0800 Subject: [PATCH] =?UTF-8?q?feat(BasicInfoTab):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=AA=A8=E6=9E=B6=E5=B1=8F=E5=B9=B6=E9=80=82=E9=85=8D=E5=BB=B6?= =?UTF-8?q?=E8=BF=9F=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 各 Panel 组件适配新的 hooks 参数格式 - 新增 BasicInfoTabSkeleton 骨架屏组件 - 新增 CompanyOverviewNavSkeleton 导航骨架屏组件 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../components/AnnouncementsPanel.tsx | 8 +- .../components/BasicInfoTabSkeleton.tsx | 271 ++++++++++++++++++ .../BasicInfoTab/components/BranchesPanel.tsx | 10 +- .../components/BusinessInfoPanel.tsx | 14 +- .../components/CompanyOverviewNavSkeleton.tsx | 197 +++++++++++++ .../components/DisclosureSchedulePanel.tsx | 6 +- .../components/ShareholderPanel.tsx | 9 +- .../BasicInfoTab/components/index.ts | 3 + .../components/management/ManagementPanel.tsx | 9 +- .../CompanyOverview/BasicInfoTab/index.tsx | 23 +- 10 files changed, 518 insertions(+), 32 deletions(-) create mode 100644 src/views/Company/components/CompanyOverview/BasicInfoTab/components/BasicInfoTabSkeleton.tsx create mode 100644 src/views/Company/components/CompanyOverview/BasicInfoTab/components/CompanyOverviewNavSkeleton.tsx diff --git a/src/views/Company/components/CompanyOverview/BasicInfoTab/components/AnnouncementsPanel.tsx b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/AnnouncementsPanel.tsx index 65749056..89536abf 100644 --- a/src/views/Company/components/CompanyOverview/BasicInfoTab/components/AnnouncementsPanel.tsx +++ b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/AnnouncementsPanel.tsx @@ -31,10 +31,14 @@ import LoadingState from "./LoadingState"; interface AnnouncementsPanelProps { stockCode: string; + /** SubTabContainer 传递的激活状态,控制是否加载数据 */ + isActive?: boolean; + /** 激活次数,变化时触发重新请求 */ + activationKey?: number; } -const AnnouncementsPanel: React.FC = ({ stockCode }) => { - const { announcements, loading } = useAnnouncementsData(stockCode); +const AnnouncementsPanel: React.FC = ({ stockCode, isActive = true, activationKey }) => { + const { announcements, loading } = useAnnouncementsData({ stockCode, enabled: isActive, refreshKey: activationKey }); const { isOpen, onOpen, onClose } = useDisclosure(); const [selectedAnnouncement, setSelectedAnnouncement] = useState(null); diff --git a/src/views/Company/components/CompanyOverview/BasicInfoTab/components/BasicInfoTabSkeleton.tsx b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/BasicInfoTabSkeleton.tsx new file mode 100644 index 00000000..c0bcea9b --- /dev/null +++ b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/BasicInfoTabSkeleton.tsx @@ -0,0 +1,271 @@ +/** + * BasicInfoTab 骨架屏组件 + * 用于各个 Tab 面板的加载状态显示 + */ + +import React from 'react'; +import { + Box, + VStack, + HStack, + SimpleGrid, + Skeleton, + SkeletonText, + SkeletonCircle, +} from '@chakra-ui/react'; + +// 黑金主题骨架屏样式 +const skeletonStyles = { + startColor: 'rgba(212, 175, 55, 0.1)', + endColor: 'rgba(212, 175, 55, 0.2)', +}; + +// 卡片骨架屏样式 +const cardStyle = { + bg: 'linear-gradient(145deg, rgba(30, 30, 35, 0.95), rgba(20, 20, 25, 0.98))', + border: '1px solid', + borderColor: 'rgba(212, 175, 55, 0.2)', + borderRadius: '12px', + p: 4, +}; + +/** + * 分支机构骨架屏 + */ +export const BranchesSkeleton: React.FC = () => ( + + {[1, 2, 3, 4].map((i) => ( + + {/* 顶部金色装饰线 */} + + + {/* 标题行 */} + + + + + + + + + {/* 分隔线 */} + + + {/* 信息网格 */} + + {[1, 2, 3, 4].map((j) => ( + + + + + ))} + + + + ))} + +); + +/** + * 工商信息骨架屏 + */ +export const BusinessInfoSkeleton: React.FC = () => ( + + {/* 上半部分:工商信息 + 服务机构 */} + + {/* 工商信息卡片 */} + + + + + + + {[1, 2, 3, 4].map((i) => ( + + + + + + ))} + + + + {/* 服务机构卡片 */} + + + + + + + {[1, 2].map((i) => ( + + + + + + + + ))} + + + + + {/* 下半部分:主营业务 + 经营范围 */} + + {[1, 2].map((i) => ( + + + + + + + + ))} + + +); + +/** + * 股权结构骨架屏 + */ +export const ShareholderSkeleton: React.FC = () => ( + + + {/* 实际控制人 + 股权集中度 */} + + {[1, 2].map((i) => ( + + + + + + + {[1, 2, 3].map((j) => ( + + + + + ))} + + + ))} + + + {/* 十大股东表格 */} + + {[1, 2].map((i) => ( + + + + + + + {/* 表头 */} + + + + + + + {/* 表格行 */} + {[1, 2, 3, 4, 5].map((j) => ( + + + + + + + ))} + + + ))} + + + +); + +/** + * 管理团队骨架屏 + */ +export const ManagementSkeleton: React.FC = () => ( + + + {/* 每个分类 */} + {[1, 2, 3].map((i) => ( + + {/* 分类标题 */} + + + + + + + {/* 人员卡片网格 */} + + {[1, 2, 3, 4].map((j) => ( + + + + + + + + + + + + ))} + + + ))} + + +); + +/** + * 通用内容骨架屏 + */ +export const ContentSkeleton: React.FC = () => ( + + + +); + +export default { + BranchesSkeleton, + BusinessInfoSkeleton, + ShareholderSkeleton, + ManagementSkeleton, + ContentSkeleton, +}; diff --git a/src/views/Company/components/CompanyOverview/BasicInfoTab/components/BranchesPanel.tsx b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/BranchesPanel.tsx index 639f2be7..a4536124 100644 --- a/src/views/Company/components/CompanyOverview/BasicInfoTab/components/BranchesPanel.tsx +++ b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/BranchesPanel.tsx @@ -16,10 +16,12 @@ import { FaSitemap, FaBuilding, FaCheckCircle, FaTimesCircle } from "react-icons import { useBranchesData } from "../../hooks/useBranchesData"; import { THEME } from "../config"; import { formatDate } from "../utils"; -import LoadingState from "./LoadingState"; +import { BranchesSkeleton } from "./BasicInfoTabSkeleton"; interface BranchesPanelProps { stockCode: string; + /** SubTabContainer 传递的激活状态,控制是否加载数据 */ + isActive?: boolean; } // 黑金卡片样式 @@ -65,11 +67,11 @@ const InfoItem: React.FC<{ label: string; value: string | number }> = ({ label, ); -const BranchesPanel: React.FC = ({ stockCode }) => { - const { branches, loading } = useBranchesData(stockCode); +const BranchesPanel: React.FC = ({ stockCode, isActive = true }) => { + const { branches, loading } = useBranchesData({ stockCode, enabled: isActive }); if (loading) { - return ; + return ; } if (branches.length === 0) { diff --git a/src/views/Company/components/CompanyOverview/BasicInfoTab/components/BusinessInfoPanel.tsx b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/BusinessInfoPanel.tsx index 8e4a1b1f..eb8a0e16 100644 --- a/src/views/Company/components/CompanyOverview/BasicInfoTab/components/BusinessInfoPanel.tsx +++ b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/BusinessInfoPanel.tsx @@ -10,7 +10,6 @@ import { SimpleGrid, Center, Icon, - Spinner, } from "@chakra-ui/react"; import { FaBuilding, @@ -27,9 +26,12 @@ import { import { COLORS, GLASS, glassCardStyle } from "@views/Company/theme"; import { THEME } from "../config"; import { useBasicInfo } from "../../hooks/useBasicInfo"; +import { BusinessInfoSkeleton } from "./BasicInfoTabSkeleton"; interface BusinessInfoPanelProps { stockCode: string; + /** SubTabContainer 传递的激活状态,控制是否加载数据 */ + isActive?: boolean; } // 区块标题组件 @@ -150,15 +152,11 @@ const TextSection: React.FC<{ ); -const BusinessInfoPanel: React.FC = ({ stockCode }) => { - const { basicInfo, loading } = useBasicInfo(stockCode); +const BusinessInfoPanel: React.FC = ({ stockCode, isActive = true }) => { + const { basicInfo, loading } = useBasicInfo({ stockCode, enabled: isActive }); if (loading) { - return ( -
- -
- ); + return ; } if (!basicInfo) { diff --git a/src/views/Company/components/CompanyOverview/BasicInfoTab/components/CompanyOverviewNavSkeleton.tsx b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/CompanyOverviewNavSkeleton.tsx new file mode 100644 index 00000000..7804d1d6 --- /dev/null +++ b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/CompanyOverviewNavSkeleton.tsx @@ -0,0 +1,197 @@ +/** + * 公司概览 - 导航骨架屏组件 + * + * 用于懒加载时显示,让二级导航立即可见 + * 导航使用真实 UI,内容区域显示骨架屏 + */ + +import React from 'react'; +import { + Box, + Flex, + HStack, + Text, + Icon, + Skeleton, + VStack, + Card, + CardBody, + Table, + Thead, + Tbody, + Tr, + Th, + Td, +} from '@chakra-ui/react'; +import { + FaShareAlt, + FaUserTie, + FaSitemap, + FaInfoCircle, +} from 'react-icons/fa'; + +// 深空 FUI 主题配置(与 SubTabContainer 保持一致) +const DEEP_SPACE = { + bgGlass: 'rgba(12, 14, 28, 0.6)', + borderGold: 'rgba(212, 175, 55, 0.2)', + borderGoldHover: 'rgba(212, 175, 55, 0.5)', + glowGold: '0 0 30px rgba(212, 175, 55, 0.25), 0 4px 20px rgba(0, 0, 0, 0.3)', + innerGlow: 'inset 0 1px 0 rgba(255, 255, 255, 0.08)', + textWhite: 'rgba(255, 255, 255, 0.95)', + textDark: '#0A0A14', + selectedBg: 'linear-gradient(135deg, rgba(212, 175, 55, 0.95) 0%, rgba(184, 150, 12, 0.95) 100%)', + radius: '12px', + radiusLG: '16px', +}; + +// 导航配置(与主组件 config.ts 保持同步) +const OVERVIEW_TABS = [ + { key: 'shareholder', name: '股权结构', icon: FaShareAlt }, + { key: 'management', name: '管理团队', icon: FaUserTie }, + { key: 'branches', name: '分支机构', icon: FaSitemap }, + { key: 'business', name: '工商信息', icon: FaInfoCircle }, +]; + +/** + * 股权结构内容骨架屏 + */ +const ShareholderContentSkeleton: React.FC = () => ( + + {/* 表格骨架屏 */} + + + + + + + + + + + + + {[1, 2, 3, 4, 5].map((i) => ( + + + + + + + ))} + +
+ + + + + + + +
+ + + + + + + +
+
+
+
+); + +/** + * CompanyOverview 导航骨架屏 + * + * 显示真实的导航 Tab(默认选中第一个),内容区域显示骨架屏 + */ +const CompanyOverviewNavSkeleton: React.FC = () => { + return ( + + {/* 导航栏容器 - compact 模式(无外边距) */} + + {/* 顶部金色光条 */} + + + {/* Tab 列表 */} + + + {OVERVIEW_TABS.map((tab, idx) => { + const isSelected = idx === 0; + + return ( + + + + {tab.name} + + + ); + })} + + + + + {/* 内容区域骨架屏 */} + + + ); +}; + +export default CompanyOverviewNavSkeleton; diff --git a/src/views/Company/components/CompanyOverview/BasicInfoTab/components/DisclosureSchedulePanel.tsx b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/DisclosureSchedulePanel.tsx index 28bbf86d..6b8ef55a 100644 --- a/src/views/Company/components/CompanyOverview/BasicInfoTab/components/DisclosureSchedulePanel.tsx +++ b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/DisclosureSchedulePanel.tsx @@ -19,10 +19,12 @@ import LoadingState from "./LoadingState"; interface DisclosureSchedulePanelProps { stockCode: string; + /** SubTabContainer 传递的激活状态,控制是否加载数据 */ + isActive?: boolean; } -const DisclosureSchedulePanel: React.FC = ({ stockCode }) => { - const { disclosureSchedule, loading } = useDisclosureData(stockCode); +const DisclosureSchedulePanel: React.FC = ({ stockCode, isActive = true }) => { + const { disclosureSchedule, loading } = useDisclosureData({ stockCode, enabled: isActive }); if (loading) { return ; diff --git a/src/views/Company/components/CompanyOverview/BasicInfoTab/components/ShareholderPanel.tsx b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/ShareholderPanel.tsx index 4b9a4989..63bba0c4 100644 --- a/src/views/Company/components/CompanyOverview/BasicInfoTab/components/ShareholderPanel.tsx +++ b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/ShareholderPanel.tsx @@ -11,9 +11,12 @@ import { ShareholdersTable, } from "../../components/shareholder"; import TabPanelContainer from "@components/TabPanelContainer"; +import { ShareholderSkeleton } from "./BasicInfoTabSkeleton"; interface ShareholderPanelProps { stockCode: string; + /** SubTabContainer 传递的激活状态,控制是否加载数据 */ + isActive?: boolean; } /** @@ -23,17 +26,17 @@ interface ShareholderPanelProps { * - ConcentrationCard: 股权集中度卡片 * - ShareholdersTable: 股东表格(合并版,支持十大股东和十大流通股东) */ -const ShareholderPanel: React.FC = ({ stockCode }) => { +const ShareholderPanel: React.FC = ({ stockCode, isActive = true }) => { const { actualControl, concentration, topShareholders, topCirculationShareholders, loading, - } = useShareholderData(stockCode); + } = useShareholderData({ stockCode, enabled: isActive }); return ( - + }> {/* 实际控制人 + 股权集中度 左右分布 */} diff --git a/src/views/Company/components/CompanyOverview/BasicInfoTab/components/index.ts b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/index.ts index e4abb538..2040ef41 100644 --- a/src/views/Company/components/CompanyOverview/BasicInfoTab/components/index.ts +++ b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/index.ts @@ -9,3 +9,6 @@ export { ManagementPanel } from "./management"; export { default as AnnouncementsPanel } from "./AnnouncementsPanel"; export { default as BranchesPanel } from "./BranchesPanel"; export { default as BusinessInfoPanel } from "./BusinessInfoPanel"; + +// 骨架屏组件 +export * from "./BasicInfoTabSkeleton"; diff --git a/src/views/Company/components/CompanyOverview/BasicInfoTab/components/management/ManagementPanel.tsx b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/management/ManagementPanel.tsx index bfac87b0..5901fd48 100644 --- a/src/views/Company/components/CompanyOverview/BasicInfoTab/components/management/ManagementPanel.tsx +++ b/src/views/Company/components/CompanyOverview/BasicInfoTab/components/management/ManagementPanel.tsx @@ -13,6 +13,7 @@ import { useManagementData } from "../../../hooks/useManagementData"; import { THEME } from "../../config"; import TabPanelContainer from "@components/TabPanelContainer"; import CategorySection from "./CategorySection"; +import { ManagementSkeleton } from "../BasicInfoTabSkeleton"; import type { ManagementPerson, ManagementCategory, @@ -22,6 +23,8 @@ import type { interface ManagementPanelProps { stockCode: string; + /** SubTabContainer 传递的激活状态,控制是否加载数据 */ + isActive?: boolean; } /** @@ -68,8 +71,8 @@ const categorizeManagement = (management: ManagementPerson[]): CategorizedManage return categories; }; -const ManagementPanel: React.FC = ({ stockCode }) => { - const { management, loading } = useManagementData(stockCode); +const ManagementPanel: React.FC = ({ stockCode, isActive = true }) => { + const { management, loading } = useManagementData({ stockCode, enabled: isActive }); // 使用 useMemo 缓存分类计算结果 const categorizedManagement = useMemo( @@ -78,7 +81,7 @@ const ManagementPanel: React.FC = ({ stockCode }) => { ); return ( - + }> {CATEGORY_ORDER.map((category) => { const config = CATEGORY_CONFIG[category]; const people = categorizedManagement[category]; diff --git a/src/views/Company/components/CompanyOverview/BasicInfoTab/index.tsx b/src/views/Company/components/CompanyOverview/BasicInfoTab/index.tsx index 03d8a5fb..f17d77cc 100644 --- a/src/views/Company/components/CompanyOverview/BasicInfoTab/index.tsx +++ b/src/views/Company/components/CompanyOverview/BasicInfoTab/index.tsx @@ -2,6 +2,7 @@ // 基本信息 Tab 组件 - 使用 SubTabContainer 通用组件 import React, { useMemo } from "react"; +import { Card, CardBody } from "@chakra-ui/react"; import SubTabContainer, { type SubTabConfig } from "@components/SubTabContainer"; import { THEME, TAB_CONFIG, getEnabledTabs } from "./config"; @@ -65,16 +66,18 @@ const BasicInfoTab: React.FC = ({ const tabs = useMemo(() => buildTabsConfig(enabledTabs), [enabledTabs]); return ( - + + + + + ); };