Compare commits
2 Commits
fba7a7ee96
...
a47e0feed8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a47e0feed8 | ||
|
|
13fa91a998 |
49
src/components/TabContainer/TabNavigation.tsx
Normal file
49
src/components/TabContainer/TabNavigation.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* TabNavigation 通用导航组件
|
||||||
|
*
|
||||||
|
* 渲染 Tab 按钮列表,支持图标 + 文字
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { TabList, Tab, HStack, Icon, Text } from '@chakra-ui/react';
|
||||||
|
import type { TabNavigationProps } from './types';
|
||||||
|
|
||||||
|
const TabNavigation: React.FC<TabNavigationProps> = ({
|
||||||
|
tabs,
|
||||||
|
themeColors,
|
||||||
|
borderRadius = 'lg',
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<TabList
|
||||||
|
py={4}
|
||||||
|
bg={themeColors.bg}
|
||||||
|
borderTopLeftRadius={borderRadius}
|
||||||
|
borderTopRightRadius={borderRadius}
|
||||||
|
>
|
||||||
|
{tabs.map((tab, index) => (
|
||||||
|
<Tab
|
||||||
|
key={tab.key}
|
||||||
|
color={themeColors.unselectedText}
|
||||||
|
borderRadius="full"
|
||||||
|
px={4}
|
||||||
|
py={2}
|
||||||
|
_selected={{
|
||||||
|
bg: themeColors.selectedBg,
|
||||||
|
color: themeColors.selectedText,
|
||||||
|
}}
|
||||||
|
_hover={{
|
||||||
|
color: themeColors.selectedText,
|
||||||
|
}}
|
||||||
|
mr={index < tabs.length - 1 ? 2 : 0}
|
||||||
|
>
|
||||||
|
<HStack spacing={2}>
|
||||||
|
{tab.icon && <Icon as={tab.icon} boxSize="18px" />}
|
||||||
|
<Text fontSize="15px">{tab.name}</Text>
|
||||||
|
</HStack>
|
||||||
|
</Tab>
|
||||||
|
))}
|
||||||
|
</TabList>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TabNavigation;
|
||||||
56
src/components/TabContainer/constants.ts
Normal file
56
src/components/TabContainer/constants.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* TabContainer 常量和主题预设
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { ThemeColors, ThemePreset } from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主题预设配置
|
||||||
|
*/
|
||||||
|
export const THEME_PRESETS: Record<ThemePreset, Required<ThemeColors>> = {
|
||||||
|
// 黑金主题(原 Company 模块风格)
|
||||||
|
blackGold: {
|
||||||
|
bg: '#1A202C',
|
||||||
|
selectedBg: '#C9A961',
|
||||||
|
selectedText: '#FFFFFF',
|
||||||
|
unselectedText: '#D4AF37',
|
||||||
|
dividerColor: 'gray.600',
|
||||||
|
},
|
||||||
|
// 默认主题(Chakra 风格)
|
||||||
|
default: {
|
||||||
|
bg: 'white',
|
||||||
|
selectedBg: 'blue.500',
|
||||||
|
selectedText: 'white',
|
||||||
|
unselectedText: 'gray.600',
|
||||||
|
dividerColor: 'gray.200',
|
||||||
|
},
|
||||||
|
// 深色主题
|
||||||
|
dark: {
|
||||||
|
bg: 'gray.800',
|
||||||
|
selectedBg: 'blue.400',
|
||||||
|
selectedText: 'white',
|
||||||
|
unselectedText: 'gray.300',
|
||||||
|
dividerColor: 'gray.600',
|
||||||
|
},
|
||||||
|
// 浅色主题
|
||||||
|
light: {
|
||||||
|
bg: 'gray.50',
|
||||||
|
selectedBg: 'blue.500',
|
||||||
|
selectedText: 'white',
|
||||||
|
unselectedText: 'gray.700',
|
||||||
|
dividerColor: 'gray.300',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认配置
|
||||||
|
*/
|
||||||
|
export const DEFAULT_CONFIG = {
|
||||||
|
themePreset: 'blackGold' as ThemePreset,
|
||||||
|
isLazy: true,
|
||||||
|
size: 'lg' as const,
|
||||||
|
showDivider: true,
|
||||||
|
borderRadius: 'lg',
|
||||||
|
shadow: 'lg',
|
||||||
|
panelPadding: 0,
|
||||||
|
};
|
||||||
140
src/components/TabContainer/index.tsx
Normal file
140
src/components/TabContainer/index.tsx
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
/**
|
||||||
|
* TabContainer 通用 Tab 容器组件
|
||||||
|
*
|
||||||
|
* 功能:
|
||||||
|
* - 管理 Tab 切换状态(支持受控/非受控模式)
|
||||||
|
* - 动态渲染 Tab 导航和内容
|
||||||
|
* - 支持多种主题预设(黑金、默认、深色、浅色)
|
||||||
|
* - 支持自定义主题颜色
|
||||||
|
* - 支持懒加载
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // 基础用法(传入 components)
|
||||||
|
* <TabContainer
|
||||||
|
* tabs={[
|
||||||
|
* { key: 'tab1', name: 'Tab 1', icon: FaHome, component: Tab1Content },
|
||||||
|
* { key: 'tab2', name: 'Tab 2', icon: FaUser, component: Tab2Content },
|
||||||
|
* ]}
|
||||||
|
* componentProps={{ userId: '123' }}
|
||||||
|
* onTabChange={(index, key) => console.log('切换到', key)}
|
||||||
|
* />
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // 自定义渲染用法(使用 children)
|
||||||
|
* <TabContainer tabs={tabs} themePreset="dark">
|
||||||
|
* <TabPanel>自定义内容 1</TabPanel>
|
||||||
|
* <TabPanel>自定义内容 2</TabPanel>
|
||||||
|
* </TabContainer>
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useState, useCallback, useMemo } from 'react';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardBody,
|
||||||
|
Tabs,
|
||||||
|
TabPanels,
|
||||||
|
TabPanel,
|
||||||
|
Divider,
|
||||||
|
} 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<TabContainerProps> = ({
|
||||||
|
tabs,
|
||||||
|
componentProps = {},
|
||||||
|
onTabChange,
|
||||||
|
defaultIndex = 0,
|
||||||
|
index: controlledIndex,
|
||||||
|
themePreset = DEFAULT_CONFIG.themePreset,
|
||||||
|
themeColors: customThemeColors,
|
||||||
|
isLazy = DEFAULT_CONFIG.isLazy,
|
||||||
|
size = DEFAULT_CONFIG.size,
|
||||||
|
showDivider = DEFAULT_CONFIG.showDivider,
|
||||||
|
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<ThemeColors> = 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 (
|
||||||
|
<TabPanel key={tab.key} px={panelPadding} py={panelPadding}>
|
||||||
|
{Component ? <Component {...componentProps} /> : null}
|
||||||
|
</TabPanel>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card shadow={shadow} bg={themeColors.bg} borderRadius={borderRadius}>
|
||||||
|
<CardBody p={0}>
|
||||||
|
<Tabs
|
||||||
|
isLazy={isLazy}
|
||||||
|
variant="soft-rounded"
|
||||||
|
colorScheme="blue"
|
||||||
|
size={size}
|
||||||
|
index={currentIndex}
|
||||||
|
onChange={handleTabChange}
|
||||||
|
>
|
||||||
|
{/* Tab 导航 */}
|
||||||
|
<TabNavigation
|
||||||
|
tabs={tabs}
|
||||||
|
themeColors={themeColors}
|
||||||
|
borderRadius={borderRadius}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 分割线 */}
|
||||||
|
{showDivider && <Divider borderColor={themeColors.dividerColor} />}
|
||||||
|
|
||||||
|
{/* Tab 内容面板 */}
|
||||||
|
<TabPanels>{renderTabPanels()}</TabPanels>
|
||||||
|
</Tabs>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TabContainer;
|
||||||
87
src/components/TabContainer/types.ts
Normal file
87
src/components/TabContainer/types.ts
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* TabContainer 通用 Tab 容器组件类型定义
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { ComponentType, ReactNode } from 'react';
|
||||||
|
import type { IconType } from 'react-icons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tab 配置项
|
||||||
|
*/
|
||||||
|
export interface TabConfig {
|
||||||
|
/** Tab 唯一标识 */
|
||||||
|
key: string;
|
||||||
|
/** Tab 显示名称 */
|
||||||
|
name: string;
|
||||||
|
/** Tab 图标(可选) */
|
||||||
|
icon?: IconType | ComponentType;
|
||||||
|
/** Tab 内容组件(可选,如果不传则使用 children 渲染) */
|
||||||
|
component?: ComponentType<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主题颜色配置
|
||||||
|
*/
|
||||||
|
export interface ThemeColors {
|
||||||
|
/** 容器背景色 */
|
||||||
|
bg?: string;
|
||||||
|
/** 选中 Tab 背景色 */
|
||||||
|
selectedBg?: string;
|
||||||
|
/** 选中 Tab 文字颜色 */
|
||||||
|
selectedText?: string;
|
||||||
|
/** 未选中 Tab 文字颜色 */
|
||||||
|
unselectedText?: string;
|
||||||
|
/** 分割线颜色 */
|
||||||
|
dividerColor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预设主题类型
|
||||||
|
*/
|
||||||
|
export type ThemePreset = 'blackGold' | 'default' | 'dark' | 'light';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TabContainer 组件 Props
|
||||||
|
*/
|
||||||
|
export interface TabContainerProps {
|
||||||
|
/** Tab 配置数组 */
|
||||||
|
tabs: TabConfig[];
|
||||||
|
/** 传递给 Tab 内容组件的通用 props */
|
||||||
|
componentProps?: Record<string, any>;
|
||||||
|
/** Tab 变更回调 */
|
||||||
|
onTabChange?: (index: number, tabKey: string, prevIndex: number) => void;
|
||||||
|
/** 默认选中的 Tab 索引 */
|
||||||
|
defaultIndex?: number;
|
||||||
|
/** 受控模式下的当前索引 */
|
||||||
|
index?: number;
|
||||||
|
/** 主题预设 */
|
||||||
|
themePreset?: ThemePreset;
|
||||||
|
/** 自定义主题颜色(优先级高于预设) */
|
||||||
|
themeColors?: ThemeColors;
|
||||||
|
/** 是否启用懒加载 */
|
||||||
|
isLazy?: boolean;
|
||||||
|
/** Tab 尺寸 */
|
||||||
|
size?: 'sm' | 'md' | 'lg';
|
||||||
|
/** 是否显示分割线 */
|
||||||
|
showDivider?: boolean;
|
||||||
|
/** 容器圆角 */
|
||||||
|
borderRadius?: string;
|
||||||
|
/** 容器阴影 */
|
||||||
|
shadow?: string;
|
||||||
|
/** 自定义 Tab 面板内边距 */
|
||||||
|
panelPadding?: number | string;
|
||||||
|
/** 子元素(用于自定义渲染 Tab 内容) */
|
||||||
|
children?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TabNavigation 组件 Props
|
||||||
|
*/
|
||||||
|
export interface TabNavigationProps {
|
||||||
|
/** Tab 配置数组 */
|
||||||
|
tabs: TabConfig[];
|
||||||
|
/** 主题颜色 */
|
||||||
|
themeColors: Required<ThemeColors>;
|
||||||
|
/** 容器圆角 */
|
||||||
|
borderRadius?: string;
|
||||||
|
}
|
||||||
@@ -388,11 +388,14 @@ export const PINGAN_BANK_DATA = {
|
|||||||
comprehensiveAnalysis: {
|
comprehensiveAnalysis: {
|
||||||
qualitative_analysis: {
|
qualitative_analysis: {
|
||||||
core_positioning: {
|
core_positioning: {
|
||||||
one_line_intro: '中国领先的股份制商业银行,平安集团综合金融战略的核心载体',
|
one_line_intro: '零售基因+综合金融,低估值高弹性股份行',
|
||||||
investment_highlights: '1. 背靠平安集团,综合金融优势显著,交叉销售和客户资源共享带来持续增长动力;\n2. 零售转型成效显著,零售业务收入占比超50%,个人客户突破1.2亿户;\n3. 金融科技领先同业,AI、大数据、区块链等技术应用深化,运营效率持续提升;\n4. 风险管理体系完善,不良贷款率控制在较低水平,拨备覆盖率保持充足。',
|
investment_highlights: '1. 零售AUM 4.2万亿、抵押贷占比63%,低不良+高拨备形成稀缺安全垫\n2. 背靠平安集团,保险-银行-投资生态协同,交叉销售成本趋近于零\n3. 战略收缩高风险消费贷、发力科技/绿色/普惠"五篇大文章",资产重构带来息差与估值双升期权',
|
||||||
business_model_desc: '平安银行以零售银行业务为核心驱动,依托平安集团综合金融平台,构建"三位一体"(智能化银行、移动化银行、综合化银行)发展模式。通过科技赋能实现业务流程数字化,降本增效的同时提升客户体验。对公业务聚焦供应链金融和产业互联网,服务实体经济高质量发展。'
|
business_model_desc: '以零售金融为压舱石,通过按揭、私行财富、信用卡获取低成本负债;对公金融做精行业赛道,输出供应链金融与跨境金融解决方案;同业金融做专投资交易,赚取做市与波段收益。三大条线共享同一中台风控、科技平台与集团客户池,形成"负债降本-资产优价-中收增厚"的正循环,盈利核心=净息差+财富管理手续费+交易价差,集团生态降低获客与资本占用,实现轻资本高回报'
|
||||||
},
|
},
|
||||||
strategy: '坚持"科技引领、零售突破、对公做精"战略方针,深化数字化转型,打造智能化零售银行标杆。持续推进组织架构扁平化和敏捷化改革,提升经营效率。强化风险管理,保持资产质量稳定。'
|
strategy: {
|
||||||
|
strategy_description: '以"零售做强、对公做精、同业做专"为主线,通过压降高风险资产、深耕科技绿色普惠、强化集团协同,实现轻资本、弱周期、高股息的高质量增长。',
|
||||||
|
strategic_initiatives: '2025年AI 138个项目落地,构建智能风控、智能投顾与智能运营,目标3年降低单位成本10%以上;发行800亿元资本债,用于置换存量高成本次级债并支持科技绿色贷款扩张,目标2026年科技绿色贷款占比提升至15%'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
competitive_position: {
|
competitive_position: {
|
||||||
ranking: {
|
ranking: {
|
||||||
|
|||||||
@@ -1,27 +1,23 @@
|
|||||||
/**
|
/**
|
||||||
* 免责声明组件
|
* 免责声明组件
|
||||||
*
|
*
|
||||||
* 显示 AI 分析内容的免责声明警告框
|
* 显示 AI 分析内容的免责声明提示
|
||||||
* 使用位置:深度分析各 Card 底部(共 6 处)
|
* 使用位置:深度分析各 Card 底部(共 6 处)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Alert, AlertIcon, Box, Text } from '@chakra-ui/react';
|
import { Text } from '@chakra-ui/react';
|
||||||
|
|
||||||
const DisclaimerBox: React.FC = () => {
|
const DisclaimerBox: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Alert status="warning" variant="left-accent" mb={4}>
|
<Text
|
||||||
<AlertIcon />
|
mb={4}
|
||||||
<Box fontSize="xs" lineHeight="1.4">
|
color="gray.500"
|
||||||
<Text fontWeight="medium" mb={1}>
|
fontSize="12px"
|
||||||
免责声明
|
lineHeight="1.5"
|
||||||
|
>
|
||||||
|
免责声明:本内容由AI模型基于新闻、公告、研报等公开信息自动分析和生成,未经许可严禁转载。所有内容仅供参考,不构成任何投资建议,请投资者注意风险,独立审慎决策。
|
||||||
</Text>
|
</Text>
|
||||||
<Text>
|
|
||||||
本内容由AI模型基于新闻、公告、研报等公开信息自动分析和生成,未经许可严禁转载。
|
|
||||||
所有内容仅供参考,不构成任何投资建议,请投资者注意风险,独立审慎决策。
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
</Alert>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,14 @@ const DeepAnalysisTab: React.FC<DeepAnalysisTabProps> = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* 战略分析 */}
|
||||||
|
{comprehensiveData?.qualitative_analysis?.strategy && (
|
||||||
|
<StrategyAnalysisCard
|
||||||
|
strategy={comprehensiveData.qualitative_analysis.strategy}
|
||||||
|
cardBg={cardBg}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 竞争地位分析 */}
|
{/* 竞争地位分析 */}
|
||||||
{comprehensiveData?.competitive_position && (
|
{comprehensiveData?.competitive_position && (
|
||||||
<CompetitiveAnalysisCard
|
<CompetitiveAnalysisCard
|
||||||
@@ -66,6 +74,17 @@ const DeepAnalysisTab: React.FC<DeepAnalysisTabProps> = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* 业务板块详情 */}
|
||||||
|
{comprehensiveData?.business_segments &&
|
||||||
|
comprehensiveData.business_segments.length > 0 && (
|
||||||
|
<BusinessSegmentsCard
|
||||||
|
businessSegments={comprehensiveData.business_segments}
|
||||||
|
expandedSegments={expandedSegments}
|
||||||
|
onToggleSegment={onToggleSegment}
|
||||||
|
cardBg={cardBg}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 产业链分析 */}
|
{/* 产业链分析 */}
|
||||||
{valueChainData && (
|
{valueChainData && (
|
||||||
<ValueChainCard valueChainData={valueChainData} cardBg={cardBg} />
|
<ValueChainCard valueChainData={valueChainData} cardBg={cardBg} />
|
||||||
@@ -91,25 +110,6 @@ const DeepAnalysisTab: React.FC<DeepAnalysisTabProps> = ({
|
|||||||
)}
|
)}
|
||||||
</GridItem>
|
</GridItem>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* 业务板块详情 */}
|
|
||||||
{comprehensiveData?.business_segments &&
|
|
||||||
comprehensiveData.business_segments.length > 0 && (
|
|
||||||
<BusinessSegmentsCard
|
|
||||||
businessSegments={comprehensiveData.business_segments}
|
|
||||||
expandedSegments={expandedSegments}
|
|
||||||
onToggleSegment={onToggleSegment}
|
|
||||||
cardBg={cardBg}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 战略分析 */}
|
|
||||||
{comprehensiveData?.qualitative_analysis?.strategy && (
|
|
||||||
<StrategyAnalysisCard
|
|
||||||
strategy={comprehensiveData.qualitative_analysis.strategy}
|
|
||||||
cardBg={cardBg}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</VStack>
|
</VStack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
// 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 } from '../../constants';
|
|
||||||
|
|
||||||
// 黑金主题颜色配置
|
|
||||||
const THEME_COLORS = {
|
|
||||||
bg: '#1A202C', // 背景纯黑
|
|
||||||
selectedBg: '#C9A961', // 选中项金色背景
|
|
||||||
selectedText: '#FFFFFF', // 选中项白色文字
|
|
||||||
unselectedText: '#D4AF37', // 未选中项金色
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tab 导航组件(黑金主题)
|
|
||||||
*/
|
|
||||||
const TabNavigation = () => {
|
|
||||||
return (
|
|
||||||
<TabList py={4} bg={THEME_COLORS.bg} borderTopLeftRadius="16px" borderTopRightRadius="16px">
|
|
||||||
{COMPANY_TABS.map((tab, index) => (
|
|
||||||
<Tab
|
|
||||||
key={tab.key}
|
|
||||||
color={THEME_COLORS.unselectedText}
|
|
||||||
borderRadius="full"
|
|
||||||
px={4}
|
|
||||||
py={2}
|
|
||||||
_selected={{
|
|
||||||
bg: THEME_COLORS.selectedBg,
|
|
||||||
color: THEME_COLORS.selectedText,
|
|
||||||
}}
|
|
||||||
_hover={{
|
|
||||||
color: THEME_COLORS.selectedText,
|
|
||||||
}}
|
|
||||||
mr={index < COMPANY_TABS.length - 1 ? 2 : 0}
|
|
||||||
>
|
|
||||||
<HStack spacing={2}>
|
|
||||||
<Icon as={tab.icon} boxSize="18px" />
|
|
||||||
<Text fontSize="15px">{tab.name}</Text>
|
|
||||||
</HStack>
|
|
||||||
</Tab>
|
|
||||||
))}
|
|
||||||
</TabList>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TabNavigation;
|
|
||||||
@@ -1,17 +1,8 @@
|
|||||||
// src/views/Company/components/CompanyTabs/index.js
|
// src/views/Company/components/CompanyTabs/index.js
|
||||||
// Tab 容器组件 - 管理 Tab 切换和内容渲染
|
// Tab 容器组件 - 使用通用 TabContainer 组件
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import {
|
import TabContainer from '@components/TabContainer';
|
||||||
Card,
|
|
||||||
CardBody,
|
|
||||||
Tabs,
|
|
||||||
TabPanels,
|
|
||||||
TabPanel,
|
|
||||||
Divider,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
|
|
||||||
import TabNavigation from './TabNavigation';
|
|
||||||
import { COMPANY_TABS, getTabNameByIndex } from '../../constants';
|
import { COMPANY_TABS, getTabNameByIndex } from '../../constants';
|
||||||
|
|
||||||
// 子组件导入(Tab 内容组件)
|
// 子组件导入(Tab 内容组件)
|
||||||
@@ -24,7 +15,6 @@ import DynamicTracking from '../DynamicTracking';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tab 组件映射
|
* Tab 组件映射
|
||||||
* key 与 COMPANY_TABS 中的 key 对应
|
|
||||||
*/
|
*/
|
||||||
const TAB_COMPONENTS = {
|
const TAB_COMPONENTS = {
|
||||||
overview: CompanyOverview,
|
overview: CompanyOverview,
|
||||||
@@ -36,11 +26,25 @@ const TAB_COMPONENTS = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tab 容器组件
|
* 构建 TabContainer 所需的 tabs 配置
|
||||||
|
* 合并 COMPANY_TABS 和对应的组件
|
||||||
|
*/
|
||||||
|
const buildTabsConfig = () => {
|
||||||
|
return COMPANY_TABS.map((tab) => ({
|
||||||
|
...tab,
|
||||||
|
component: TAB_COMPONENTS[tab.key],
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 预构建 tabs 配置(避免每次渲染重新计算)
|
||||||
|
const TABS_CONFIG = buildTabsConfig();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公司详情 Tab 容器组件
|
||||||
*
|
*
|
||||||
* 功能:
|
* 功能:
|
||||||
* - 管理 Tab 切换状态
|
* - 使用通用 TabContainer 组件
|
||||||
* - 动态渲染 Tab 导航和内容
|
* - 保持黑金主题风格
|
||||||
* - 触发 Tab 变更追踪
|
* - 触发 Tab 变更追踪
|
||||||
*
|
*
|
||||||
* @param {Object} props
|
* @param {Object} props
|
||||||
@@ -48,51 +52,23 @@ const TAB_COMPONENTS = {
|
|||||||
* @param {Function} props.onTabChange - Tab 变更回调 (index, tabName, prevIndex) => void
|
* @param {Function} props.onTabChange - Tab 变更回调 (index, tabName, prevIndex) => void
|
||||||
*/
|
*/
|
||||||
const CompanyTabs = ({ stockCode, onTabChange }) => {
|
const CompanyTabs = ({ stockCode, onTabChange }) => {
|
||||||
const [currentIndex, setCurrentIndex] = useState(0);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理 Tab 切换
|
* 处理 Tab 切换
|
||||||
|
* 转换 tabKey 为 tabName 以保持原有回调格式
|
||||||
*/
|
*/
|
||||||
const handleTabChange = (index) => {
|
const handleTabChange = (index, tabKey, prevIndex) => {
|
||||||
const tabName = getTabNameByIndex(index);
|
const tabName = getTabNameByIndex(index);
|
||||||
|
onTabChange?.(index, tabName, prevIndex);
|
||||||
// 触发追踪回调
|
|
||||||
onTabChange?.(index, tabName, currentIndex);
|
|
||||||
|
|
||||||
// 更新状态
|
|
||||||
setCurrentIndex(index);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card shadow="lg" bg='#1A202C'>
|
<TabContainer
|
||||||
<CardBody p={0}>
|
tabs={TABS_CONFIG}
|
||||||
<Tabs
|
componentProps={{ stockCode }}
|
||||||
isLazy
|
onTabChange={handleTabChange}
|
||||||
variant="soft-rounded"
|
themePreset="blackGold"
|
||||||
colorScheme="blue"
|
borderRadius="16px"
|
||||||
size="lg"
|
/>
|
||||||
index={currentIndex}
|
|
||||||
onChange={handleTabChange}
|
|
||||||
>
|
|
||||||
{/* Tab 导航(黑金主题) */}
|
|
||||||
<TabNavigation />
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
{/* Tab 内容面板 */}
|
|
||||||
<TabPanels>
|
|
||||||
{COMPANY_TABS.map((tab) => {
|
|
||||||
const Component = TAB_COMPONENTS[tab.key];
|
|
||||||
return (
|
|
||||||
<TabPanel key={tab.key} px={0}>
|
|
||||||
<Component stockCode={stockCode} />
|
|
||||||
</TabPanel>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</TabPanels>
|
|
||||||
</Tabs>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user