/** * TabContainer 通用 Tab 容器组件 * * 功能: * - 管理 Tab 切换状态(支持受控/非受控模式) * - 动态渲染 Tab 导航和内容 * - 支持多种主题预设(黑金、默认、深色、浅色) * - 支持自定义主题颜色 * - 支持懒加载 * * @example * // 基础用法(传入 components) * console.log('切换到', key)} * /> * * @example * // 自定义渲染用法(使用 children) * * 自定义内容 1 * 自定义内容 2 * */ import React, { useState, useCallback, useMemo } from 'react'; import { Card, CardBody, Tabs, TabPanels, TabPanel, } from '@chakra-ui/react'; import TabNavigation from './TabNavigation'; import { THEME_PRESETS, DEFAULT_CONFIG } from './constants'; import type { TabContainerProps, ThemeColors } from './types'; // 导出类型和常量 export type { TabConfig, ThemeColors, ThemePreset, TabContainerProps } from './types'; export { THEME_PRESETS } from './constants'; const TabContainer: React.FC = ({ tabs, componentProps = {}, onTabChange, defaultIndex = 0, index: controlledIndex, themePreset = DEFAULT_CONFIG.themePreset, themeColors: customThemeColors, isLazy = DEFAULT_CONFIG.isLazy, size = DEFAULT_CONFIG.size, borderRadius = DEFAULT_CONFIG.borderRadius, shadow = DEFAULT_CONFIG.shadow, panelPadding = DEFAULT_CONFIG.panelPadding, children, }) => { // 内部状态(非受控模式) const [internalIndex, setInternalIndex] = useState(defaultIndex); // 当前索引(支持受控/非受控) const currentIndex = controlledIndex ?? internalIndex; // 合并主题颜色(自定义颜色优先) const themeColors: Required = useMemo(() => ({ ...THEME_PRESETS[themePreset], ...customThemeColors, }), [themePreset, customThemeColors]); /** * 处理 Tab 切换 */ const handleTabChange = useCallback((newIndex: number) => { const tabKey = tabs[newIndex]?.key || ''; // 触发回调 onTabChange?.(newIndex, tabKey, currentIndex); // 非受控模式下更新内部状态 if (controlledIndex === undefined) { setInternalIndex(newIndex); } }, [tabs, onTabChange, currentIndex, controlledIndex]); /** * 渲染 Tab 内容 */ const renderTabPanels = () => { // 如果传入了 children,直接渲染 children if (children) { return children; } // 否则根据 tabs 配置渲染 return tabs.map((tab) => { const Component = tab.component; return ( {Component ? : null} ); }); }; return ( {/* Tab 导航 */} {/* Tab 内容面板 */} {renderTabPanels()} ); }; export default TabContainer;