feat: 添加Company 页面 Tab 切换组件

This commit is contained in:
zdl
2025-12-09 15:01:16 +08:00
parent ed1c7b9fa9
commit c61d58b0e3
3 changed files with 198 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
// src/views/Company/components/CompanyTabs/TabNavigation.js
// Tab 导航组件 - 动态渲染 Tab 按钮
import React from 'react';
import {
TabList,
Tab,
HStack,
Icon,
Text,
} from '@chakra-ui/react';
import { COMPANY_TABS, TAB_SELECTED_STYLE } from '../../constants';
/**
* Tab 导航组件
*
* @param {Object} props
* @param {string} props.tabBg - Tab 列表背景色
* @param {string} props.activeBg - 激活状态背景色
*/
const TabNavigation = ({ tabBg, activeBg }) => {
return (
<TabList p={4} bg={tabBg}>
{COMPANY_TABS.map((tab, index) => (
<Tab
key={tab.key}
_selected={{
bg: activeBg,
color: 'white',
...TAB_SELECTED_STYLE,
}}
mr={index < COMPANY_TABS.length - 1 ? 2 : 0}
>
<HStack spacing={2}>
<Icon as={tab.icon} />
<Text>{tab.name}</Text>
</HStack>
</Tab>
))}
</TabList>
);
};
export default TabNavigation;

View File

@@ -0,0 +1,100 @@
// src/views/Company/components/CompanyTabs/index.js
// Tab 容器组件 - 管理 Tab 切换和内容渲染
import React, { useState } from 'react';
import {
Card,
CardBody,
Tabs,
TabPanels,
TabPanel,
Divider,
useColorModeValue,
} from '@chakra-ui/react';
import TabNavigation from './TabNavigation';
import { COMPANY_TABS, getTabNameByIndex } from '../../constants';
// 子组件导入
import FinancialPanorama from '../../FinancialPanorama';
import ForecastReport from '../../ForecastReport';
import MarketDataView from '../../MarketDataView';
import CompanyOverview from '../../CompanyOverview';
/**
* Tab 组件映射
* key 与 COMPANY_TABS 中的 key 对应
*/
const TAB_COMPONENTS = {
overview: CompanyOverview,
market: MarketDataView,
financial: FinancialPanorama,
forecast: ForecastReport,
};
/**
* Tab 容器组件
*
* 功能:
* - 管理 Tab 切换状态
* - 动态渲染 Tab 导航和内容
* - 触发 Tab 变更追踪
*
* @param {Object} props
* @param {string} props.stockCode - 当前股票代码
* @param {Function} props.onTabChange - Tab 变更回调 (index, tabName, prevIndex) => void
* @param {string} props.bgColor - 背景颜色
*/
const CompanyTabs = ({ stockCode, onTabChange, bgColor }) => {
const [currentIndex, setCurrentIndex] = useState(0);
// 主题相关颜色
const tabBg = useColorModeValue('gray.50', 'gray.700');
const activeBg = useColorModeValue('blue.500', 'blue.400');
/**
* 处理 Tab 切换
*/
const handleTabChange = (index) => {
const tabName = getTabNameByIndex(index);
// 触发追踪回调
onTabChange?.(index, tabName, currentIndex);
// 更新状态
setCurrentIndex(index);
};
return (
<Card bg={bgColor} shadow="lg">
<CardBody p={0}>
<Tabs
variant="soft-rounded"
colorScheme="blue"
size="lg"
index={currentIndex}
onChange={handleTabChange}
>
{/* Tab 导航 */}
<TabNavigation tabBg={tabBg} activeBg={activeBg} />
<Divider />
{/* Tab 内容面板 */}
<TabPanels>
{COMPANY_TABS.map((tab) => {
const Component = TAB_COMPONENTS[tab.key];
return (
<TabPanel key={tab.key} p={6}>
<Component stockCode={stockCode} />
</TabPanel>
);
})}
</TabPanels>
</Tabs>
</CardBody>
</Card>
);
};
export default CompanyTabs;

View File

@@ -0,0 +1,53 @@
// src/views/Company/constants/index.js
// 公司详情页面常量配置
import { FaChartLine, FaMoneyBillWave, FaChartBar, FaInfoCircle } from 'react-icons/fa';
/**
* Tab 配置
* @type {Array<{key: string, name: string, icon: React.ComponentType}>}
*/
export const COMPANY_TABS = [
{ key: 'overview', name: '公司概览', icon: FaInfoCircle },
{ key: 'market', name: '股票行情', icon: FaChartLine },
{ key: 'financial', name: '财务全景', icon: FaMoneyBillWave },
{ key: 'forecast', name: '盈利预测', icon: FaChartBar },
];
/**
* Tab 选中状态样式
*/
export const TAB_SELECTED_STYLE = {
transform: 'scale(1.02)',
transition: 'all 0.2s',
};
/**
* Toast 消息配置
*/
export const TOAST_MESSAGES = {
WATCHLIST_ADD: { title: '已加入自选', status: 'success', duration: 1500 },
WATCHLIST_REMOVE: { title: '已从自选移除', status: 'info', duration: 1500 },
WATCHLIST_ERROR: { title: '操作失败,请稍后重试', status: 'error', duration: 2000 },
INVALID_CODE: { title: '无效的股票代码', status: 'error', duration: 2000 },
LOGIN_REQUIRED: { title: '请先登录后再加入自选', status: 'warning', duration: 2000 },
};
/**
* 默认股票代码
*/
export const DEFAULT_STOCK_CODE = '000001';
/**
* URL 参数名
*/
export const URL_PARAM_NAME = 'scode';
/**
* 根据索引获取 Tab 名称
* @param {number} index - Tab 索引
* @returns {string} Tab 名称
*/
export const getTabNameByIndex = (index) => {
return COMPANY_TABS[index]?.name || 'Unknown';
};