/** * SubTabContainer - 二级导航容器组件 * * 用于模块内的子功能切换(如公司档案下的股权结构、管理团队等) * 与 TabContainer(一级导航)区分:无 Card 包裹,直接融入父容器 * * @example * ```tsx * console.log('切换到', key)} * /> * ``` */ import React, { useState, useCallback, memo } from 'react'; import { Box, Tabs, TabList, TabPanels, Tab, TabPanel, Icon, HStack, Text, Spacer, } from '@chakra-ui/react'; import type { ComponentType } from 'react'; import type { IconType } from 'react-icons'; /** * Tab 配置项 */ export interface SubTabConfig { key: string; name: string; icon?: IconType | ComponentType; component?: ComponentType; } /** * 主题配置 */ export interface SubTabTheme { bg: string; borderColor: string; tabSelectedBg: string; tabSelectedColor: string; tabUnselectedColor: string; tabHoverBg: string; } /** * 预设主题 */ const THEME_PRESETS: Record = { blackGold: { bg: 'gray.900', borderColor: 'rgba(212, 175, 55, 0.3)', tabSelectedBg: '#D4AF37', tabSelectedColor: 'gray.900', tabUnselectedColor: '#D4AF37', tabHoverBg: 'gray.600', }, default: { bg: 'white', borderColor: 'gray.200', tabSelectedBg: 'blue.500', tabSelectedColor: 'white', tabUnselectedColor: 'gray.600', tabHoverBg: 'gray.100', }, }; export interface SubTabContainerProps { /** Tab 配置数组 */ tabs: SubTabConfig[]; /** 传递给 Tab 内容组件的 props */ componentProps?: Record; /** 默认选中的 Tab 索引 */ defaultIndex?: number; /** 受控模式下的当前索引 */ index?: number; /** Tab 变更回调 */ onTabChange?: (index: number, tabKey: string) => void; /** 主题预设 */ themePreset?: 'blackGold' | 'default'; /** 自定义主题(优先级高于预设) */ theme?: Partial; /** 内容区内边距 */ contentPadding?: number; /** 是否懒加载 */ isLazy?: boolean; /** TabList 右侧自定义内容 */ rightElement?: React.ReactNode; } const SubTabContainer: React.FC = memo(({ tabs, componentProps = {}, defaultIndex = 0, index: controlledIndex, onTabChange, themePreset = 'blackGold', theme: customTheme, contentPadding = 4, isLazy = true, rightElement, }) => { // 内部状态(非受控模式) const [internalIndex, setInternalIndex] = useState(defaultIndex); // 当前索引 const currentIndex = controlledIndex ?? internalIndex; // 记录已访问的 Tab 索引(用于真正的懒加载) const [visitedTabs, setVisitedTabs] = useState>( () => new Set([controlledIndex ?? defaultIndex]) ); // 合并主题 const theme: SubTabTheme = { ...THEME_PRESETS[themePreset], ...customTheme, }; /** * 处理 Tab 切换 */ const handleTabChange = useCallback( (newIndex: number) => { const tabKey = tabs[newIndex]?.key || ''; onTabChange?.(newIndex, tabKey); // 记录已访问的 Tab(用于懒加载) setVisitedTabs(prev => { if (prev.has(newIndex)) return prev; return new Set(prev).add(newIndex); }); if (controlledIndex === undefined) { setInternalIndex(newIndex); } }, [tabs, onTabChange, controlledIndex] ); return ( {tabs.map((tab) => ( {tab.icon && } {tab.name} ))} {rightElement && ( <> {rightElement} )} {tabs.map((tab, idx) => { const Component = tab.component; // 懒加载:只渲染已访问过的 Tab const shouldRender = !isLazy || visitedTabs.has(idx); return ( {shouldRender && Component ? ( ) : null} ); })} ); }); SubTabContainer.displayName = 'SubTabContainer'; export default SubTabContainer;