refactor(FinancialPanorama): 重构为 SubTabContainer 二级导航
- 主组件从 Chakra Tabs 迁移到 SubTabContainer - 新增 PeriodSelector 时间选择器组件 - IndustryRankingView 增加深色主题支持 - 拆分出 6 个独立 Tab 组件到 tabs/ 目录 - 类型定义优化,props 改为可选
This commit is contained in:
@@ -21,14 +21,23 @@ import type { IndustryRankingViewProps } from '../types';
|
|||||||
|
|
||||||
export const IndustryRankingView: React.FC<IndustryRankingViewProps> = ({
|
export const IndustryRankingView: React.FC<IndustryRankingViewProps> = ({
|
||||||
industryRank,
|
industryRank,
|
||||||
bgColor,
|
bgColor = 'white',
|
||||||
borderColor,
|
borderColor = 'gray.200',
|
||||||
|
textColor,
|
||||||
|
labelColor,
|
||||||
}) => {
|
}) => {
|
||||||
|
// 判断是否为深色主题
|
||||||
|
const isDarkTheme = bgColor === 'gray.800' || bgColor === 'gray.900';
|
||||||
|
const resolvedTextColor = textColor || (isDarkTheme ? 'white' : 'gray.800');
|
||||||
|
const resolvedLabelColor = labelColor || (isDarkTheme ? 'gray.400' : 'gray.500');
|
||||||
|
const cardBg = isDarkTheme ? 'transparent' : 'white';
|
||||||
|
const headingColor = isDarkTheme ? 'yellow.500' : 'gray.800';
|
||||||
|
|
||||||
if (!industryRank || !Array.isArray(industryRank) || industryRank.length === 0) {
|
if (!industryRank || !Array.isArray(industryRank) || industryRank.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card bg={cardBg} borderColor={borderColor} borderWidth="1px">
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<Text textAlign="center" color="gray.500" py={8}>
|
<Text textAlign="center" color={resolvedLabelColor} py={8}>
|
||||||
暂无行业排名数据
|
暂无行业排名数据
|
||||||
</Text>
|
</Text>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
@@ -39,17 +48,32 @@ export const IndustryRankingView: React.FC<IndustryRankingViewProps> = ({
|
|||||||
return (
|
return (
|
||||||
<VStack spacing={4} align="stretch">
|
<VStack spacing={4} align="stretch">
|
||||||
{industryRank.map((periodData, periodIdx) => (
|
{industryRank.map((periodData, periodIdx) => (
|
||||||
<Card key={periodIdx}>
|
<Card
|
||||||
<CardHeader>
|
key={periodIdx}
|
||||||
|
bg={cardBg}
|
||||||
|
borderColor={borderColor}
|
||||||
|
borderWidth="1px"
|
||||||
|
>
|
||||||
|
<CardHeader pb={2}>
|
||||||
<HStack justify="space-between">
|
<HStack justify="space-between">
|
||||||
<Heading size="sm">{periodData.report_type} 行业排名</Heading>
|
<Heading size="sm" color={headingColor}>
|
||||||
<Badge colorScheme="purple">{periodData.period}</Badge>
|
{periodData.report_type} 行业排名
|
||||||
|
</Heading>
|
||||||
|
<Badge
|
||||||
|
bg={isDarkTheme ? 'transparent' : undefined}
|
||||||
|
borderWidth={isDarkTheme ? '1px' : 0}
|
||||||
|
borderColor={isDarkTheme ? 'yellow.600' : undefined}
|
||||||
|
color={isDarkTheme ? 'yellow.500' : undefined}
|
||||||
|
colorScheme={isDarkTheme ? undefined : 'purple'}
|
||||||
|
>
|
||||||
|
{periodData.period}
|
||||||
|
</Badge>
|
||||||
</HStack>
|
</HStack>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody>
|
<CardBody pt={2}>
|
||||||
{periodData.rankings?.map((ranking, idx) => (
|
{periodData.rankings?.map((ranking, idx) => (
|
||||||
<Box key={idx} mb={6}>
|
<Box key={idx} mb={6}>
|
||||||
<Text fontWeight="bold" mb={3}>
|
<Text fontWeight="bold" mb={3} color={resolvedTextColor}>
|
||||||
{ranking.industry_name} ({ranking.level_description})
|
{ranking.industry_name} ({ranking.level_description})
|
||||||
</Text>
|
</Text>
|
||||||
<SimpleGrid columns={{ base: 1, md: 2, lg: 4 }} spacing={3}>
|
<SimpleGrid columns={{ base: 1, md: 2, lg: 4 }} spacing={3}>
|
||||||
@@ -65,6 +89,15 @@ export const IndustryRankingView: React.FC<IndustryRankingViewProps> = ({
|
|||||||
metric.key.includes('margin') ||
|
metric.key.includes('margin') ||
|
||||||
metric.key === 'roe';
|
metric.key === 'roe';
|
||||||
|
|
||||||
|
// 格式化数值
|
||||||
|
const formattedValue = isPercentMetric
|
||||||
|
? formatUtils.formatPercent(metricData.value)
|
||||||
|
: metricData.value?.toFixed(2) ?? '-';
|
||||||
|
|
||||||
|
const formattedAvg = isPercentMetric
|
||||||
|
? formatUtils.formatPercent(metricData.industry_avg)
|
||||||
|
: metricData.industry_avg?.toFixed(2) ?? '-';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
key={metric.key}
|
key={metric.key}
|
||||||
@@ -74,14 +107,12 @@ export const IndustryRankingView: React.FC<IndustryRankingViewProps> = ({
|
|||||||
borderWidth="1px"
|
borderWidth="1px"
|
||||||
borderColor={borderColor}
|
borderColor={borderColor}
|
||||||
>
|
>
|
||||||
<Text fontSize="xs" color="gray.500">
|
<Text fontSize="xs" color={resolvedLabelColor}>
|
||||||
{metric.name}
|
{metric.name}
|
||||||
</Text>
|
</Text>
|
||||||
<HStack mt={1}>
|
<HStack mt={1} spacing={2}>
|
||||||
<Text fontWeight="bold">
|
<Text fontWeight="bold" fontSize="lg" color={resolvedTextColor}>
|
||||||
{isPercentMetric
|
{formattedValue}
|
||||||
? formatUtils.formatPercent(metricData.value)
|
|
||||||
: metricData.value?.toFixed(2) || '-'}
|
|
||||||
</Text>
|
</Text>
|
||||||
{metricData.rank && (
|
{metricData.rank && (
|
||||||
<Badge
|
<Badge
|
||||||
@@ -92,11 +123,8 @@ export const IndustryRankingView: React.FC<IndustryRankingViewProps> = ({
|
|||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</HStack>
|
</HStack>
|
||||||
<Text fontSize="xs" color="gray.500" mt={1}>
|
<Text fontSize="xs" color={resolvedLabelColor} mt={1}>
|
||||||
行业均值:{' '}
|
行业均值: {formattedAvg}
|
||||||
{isPercentMetric
|
|
||||||
? formatUtils.formatPercent(metricData.industry_avg)
|
|
||||||
: metricData.industry_avg?.toFixed(2) || '-'}
|
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* 期数选择器组件
|
||||||
|
* 用于选择显示的财务报表期数,并提供刷新功能
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardBody,
|
||||||
|
HStack,
|
||||||
|
Text,
|
||||||
|
Select,
|
||||||
|
IconButton,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { RepeatIcon } from '@chakra-ui/icons';
|
||||||
|
|
||||||
|
export interface PeriodSelectorProps {
|
||||||
|
/** 当前选中的期数 */
|
||||||
|
selectedPeriods: number;
|
||||||
|
/** 期数变更回调 */
|
||||||
|
onPeriodsChange: (periods: number) => void;
|
||||||
|
/** 刷新回调 */
|
||||||
|
onRefresh: () => void;
|
||||||
|
/** 是否加载中 */
|
||||||
|
isLoading?: boolean;
|
||||||
|
/** 可选期数列表,默认 [4, 8, 12, 16] */
|
||||||
|
periodOptions?: number[];
|
||||||
|
/** 标签文本 */
|
||||||
|
label?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PeriodSelector: React.FC<PeriodSelectorProps> = memo(({
|
||||||
|
selectedPeriods,
|
||||||
|
onPeriodsChange,
|
||||||
|
onRefresh,
|
||||||
|
isLoading = false,
|
||||||
|
periodOptions = [4, 8, 12, 16],
|
||||||
|
label = '显示期数:',
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<HStack justify="space-between">
|
||||||
|
<HStack>
|
||||||
|
<Text fontSize="sm" color="gray.600">
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
|
<Select
|
||||||
|
value={selectedPeriods}
|
||||||
|
onChange={(e) => onPeriodsChange(Number(e.target.value))}
|
||||||
|
w="150px"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
{periodOptions.map((period) => (
|
||||||
|
<option key={period} value={period}>
|
||||||
|
最近{period}期
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</HStack>
|
||||||
|
<IconButton
|
||||||
|
icon={<RepeatIcon />}
|
||||||
|
onClick={onRefresh}
|
||||||
|
isLoading={isLoading}
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
aria-label="刷新数据"
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
PeriodSelector.displayName = 'PeriodSelector';
|
||||||
|
|
||||||
|
export { PeriodSelector };
|
||||||
|
export default PeriodSelector;
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
* 股票信息头部组件
|
* 股票信息头部组件 - 黑金主题
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
Card,
|
Box,
|
||||||
CardBody,
|
|
||||||
Grid,
|
Grid,
|
||||||
GridItem,
|
GridItem,
|
||||||
VStack,
|
VStack,
|
||||||
@@ -18,93 +17,153 @@ import {
|
|||||||
StatNumber,
|
StatNumber,
|
||||||
Alert,
|
Alert,
|
||||||
AlertIcon,
|
AlertIcon,
|
||||||
Box,
|
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { formatUtils } from '@services/financialService';
|
import { formatUtils } from '@services/financialService';
|
||||||
import type { StockInfoHeaderProps } from '../types';
|
import type { StockInfoHeaderProps } from '../types';
|
||||||
|
|
||||||
|
// 黑金主题配置
|
||||||
|
const darkGoldTheme = {
|
||||||
|
bgCard: 'rgba(26, 32, 44, 0.95)',
|
||||||
|
border: 'rgba(212, 175, 55, 0.3)',
|
||||||
|
borderHover: 'rgba(212, 175, 55, 0.5)',
|
||||||
|
gold: '#D4AF37',
|
||||||
|
goldLight: '#F4D03F',
|
||||||
|
orange: '#FF9500',
|
||||||
|
red: '#FF4444',
|
||||||
|
green: '#00C851',
|
||||||
|
textPrimary: 'rgba(255, 255, 255, 0.92)',
|
||||||
|
textSecondary: 'rgba(255, 255, 255, 0.7)',
|
||||||
|
textMuted: 'rgba(255, 255, 255, 0.5)',
|
||||||
|
tagBg: 'rgba(212, 175, 55, 0.15)',
|
||||||
|
};
|
||||||
|
|
||||||
export const StockInfoHeader: React.FC<StockInfoHeaderProps> = ({
|
export const StockInfoHeader: React.FC<StockInfoHeaderProps> = ({
|
||||||
stockInfo,
|
stockInfo,
|
||||||
positiveColor,
|
|
||||||
negativeColor,
|
|
||||||
}) => {
|
}) => {
|
||||||
if (!stockInfo) return null;
|
if (!stockInfo) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card mb={4}>
|
<Box
|
||||||
<CardBody>
|
mb={4}
|
||||||
<Grid templateColumns="repeat(6, 1fr)" gap={4}>
|
bg={darkGoldTheme.bgCard}
|
||||||
<GridItem colSpan={{ base: 6, md: 2 }}>
|
border="1px solid"
|
||||||
<VStack align="start">
|
borderColor={darkGoldTheme.border}
|
||||||
<Text fontSize="xs" color="gray.500">
|
borderRadius="xl"
|
||||||
股票名称
|
p={5}
|
||||||
</Text>
|
boxShadow="0 4px 20px rgba(0, 0, 0, 0.3)"
|
||||||
<HStack>
|
transition="all 0.3s ease"
|
||||||
<Heading size="md">{stockInfo.stock_name}</Heading>
|
_hover={{
|
||||||
<Badge>{stockInfo.stock_code}</Badge>
|
borderColor: darkGoldTheme.borderHover,
|
||||||
</HStack>
|
boxShadow: '0 8px 30px rgba(212, 175, 55, 0.15)',
|
||||||
</VStack>
|
}}
|
||||||
</GridItem>
|
>
|
||||||
<GridItem>
|
<Grid templateColumns="repeat(6, 1fr)" gap={4} alignItems="center">
|
||||||
<Stat>
|
<GridItem colSpan={{ base: 6, md: 2 }}>
|
||||||
<StatLabel>最新EPS</StatLabel>
|
<VStack align="start" spacing={1}>
|
||||||
<StatNumber>
|
<Text fontSize="xs" color={darkGoldTheme.textMuted}>
|
||||||
{stockInfo.key_metrics?.eps?.toFixed(3) || '-'}
|
股票名称
|
||||||
</StatNumber>
|
</Text>
|
||||||
</Stat>
|
<HStack>
|
||||||
</GridItem>
|
<Heading
|
||||||
<GridItem>
|
size="md"
|
||||||
<Stat>
|
bgGradient={`linear(to-r, ${darkGoldTheme.gold}, ${darkGoldTheme.orange})`}
|
||||||
<StatLabel>ROE</StatLabel>
|
bgClip="text"
|
||||||
<StatNumber>
|
|
||||||
{formatUtils.formatPercent(stockInfo.key_metrics?.roe)}
|
|
||||||
</StatNumber>
|
|
||||||
</Stat>
|
|
||||||
</GridItem>
|
|
||||||
<GridItem>
|
|
||||||
<Stat>
|
|
||||||
<StatLabel>营收增长</StatLabel>
|
|
||||||
<StatNumber
|
|
||||||
color={
|
|
||||||
stockInfo.growth_rates?.revenue_growth
|
|
||||||
? stockInfo.growth_rates.revenue_growth > 0
|
|
||||||
? positiveColor
|
|
||||||
: negativeColor
|
|
||||||
: 'gray.500'
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{formatUtils.formatPercent(stockInfo.growth_rates?.revenue_growth)}
|
{stockInfo.stock_name}
|
||||||
</StatNumber>
|
</Heading>
|
||||||
</Stat>
|
<Badge
|
||||||
</GridItem>
|
bg={darkGoldTheme.tagBg}
|
||||||
<GridItem>
|
color={darkGoldTheme.gold}
|
||||||
<Stat>
|
fontSize="xs"
|
||||||
<StatLabel>利润增长</StatLabel>
|
px={2}
|
||||||
<StatNumber
|
py={1}
|
||||||
color={
|
borderRadius="md"
|
||||||
stockInfo.growth_rates?.profit_growth
|
|
||||||
? stockInfo.growth_rates.profit_growth > 0
|
|
||||||
? positiveColor
|
|
||||||
: negativeColor
|
|
||||||
: 'gray.500'
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{formatUtils.formatPercent(stockInfo.growth_rates?.profit_growth)}
|
{stockInfo.stock_code}
|
||||||
</StatNumber>
|
</Badge>
|
||||||
</Stat>
|
</HStack>
|
||||||
</GridItem>
|
</VStack>
|
||||||
</Grid>
|
</GridItem>
|
||||||
{stockInfo.latest_forecast && (
|
<GridItem>
|
||||||
<Alert status="info" mt={4}>
|
<Stat>
|
||||||
<AlertIcon />
|
<StatLabel color={darkGoldTheme.textMuted} fontSize="xs">
|
||||||
<Box>
|
最新EPS
|
||||||
<Text fontWeight="bold">{stockInfo.latest_forecast.forecast_type}</Text>
|
</StatLabel>
|
||||||
<Text fontSize="sm">{stockInfo.latest_forecast.content}</Text>
|
<StatNumber color={darkGoldTheme.goldLight} fontSize="lg">
|
||||||
</Box>
|
{stockInfo.key_metrics?.eps?.toFixed(3) || '-'}
|
||||||
</Alert>
|
</StatNumber>
|
||||||
)}
|
</Stat>
|
||||||
</CardBody>
|
</GridItem>
|
||||||
</Card>
|
<GridItem>
|
||||||
|
<Stat>
|
||||||
|
<StatLabel color={darkGoldTheme.textMuted} fontSize="xs">
|
||||||
|
ROE
|
||||||
|
</StatLabel>
|
||||||
|
<StatNumber color={darkGoldTheme.goldLight} fontSize="lg">
|
||||||
|
{formatUtils.formatPercent(stockInfo.key_metrics?.roe)}
|
||||||
|
</StatNumber>
|
||||||
|
</Stat>
|
||||||
|
</GridItem>
|
||||||
|
<GridItem>
|
||||||
|
<Stat>
|
||||||
|
<StatLabel color={darkGoldTheme.textMuted} fontSize="xs">
|
||||||
|
营收增长
|
||||||
|
</StatLabel>
|
||||||
|
<StatNumber
|
||||||
|
fontSize="lg"
|
||||||
|
color={
|
||||||
|
stockInfo.growth_rates?.revenue_growth
|
||||||
|
? stockInfo.growth_rates.revenue_growth > 0
|
||||||
|
? darkGoldTheme.red
|
||||||
|
: darkGoldTheme.green
|
||||||
|
: darkGoldTheme.textMuted
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{formatUtils.formatPercent(stockInfo.growth_rates?.revenue_growth)}
|
||||||
|
</StatNumber>
|
||||||
|
</Stat>
|
||||||
|
</GridItem>
|
||||||
|
<GridItem>
|
||||||
|
<Stat>
|
||||||
|
<StatLabel color={darkGoldTheme.textMuted} fontSize="xs">
|
||||||
|
利润增长
|
||||||
|
</StatLabel>
|
||||||
|
<StatNumber
|
||||||
|
fontSize="lg"
|
||||||
|
color={
|
||||||
|
stockInfo.growth_rates?.profit_growth
|
||||||
|
? stockInfo.growth_rates.profit_growth > 0
|
||||||
|
? darkGoldTheme.red
|
||||||
|
: darkGoldTheme.green
|
||||||
|
: darkGoldTheme.textMuted
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{formatUtils.formatPercent(stockInfo.growth_rates?.profit_growth)}
|
||||||
|
</StatNumber>
|
||||||
|
</Stat>
|
||||||
|
</GridItem>
|
||||||
|
</Grid>
|
||||||
|
{stockInfo.latest_forecast && (
|
||||||
|
<Alert
|
||||||
|
status="info"
|
||||||
|
mt={4}
|
||||||
|
bg="rgba(212, 175, 55, 0.1)"
|
||||||
|
borderRadius="lg"
|
||||||
|
border="1px solid"
|
||||||
|
borderColor={darkGoldTheme.border}
|
||||||
|
>
|
||||||
|
<AlertIcon color={darkGoldTheme.gold} />
|
||||||
|
<Box>
|
||||||
|
<Text fontWeight="bold" color={darkGoldTheme.gold}>
|
||||||
|
{stockInfo.latest_forecast.forecast_type}
|
||||||
|
</Text>
|
||||||
|
<Text fontSize="sm" color={darkGoldTheme.textSecondary}>
|
||||||
|
{stockInfo.latest_forecast.content}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* 组件统一导出
|
* 组件统一导出
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export { PeriodSelector } from './PeriodSelector';
|
||||||
export { StockInfoHeader } from './StockInfoHeader';
|
export { StockInfoHeader } from './StockInfoHeader';
|
||||||
export { BalanceSheetTable } from './BalanceSheetTable';
|
export { BalanceSheetTable } from './BalanceSheetTable';
|
||||||
export { IncomeStatementTable } from './IncomeStatementTable';
|
export { IncomeStatementTable } from './IncomeStatementTable';
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* 财务全景组件
|
* 财务全景组件
|
||||||
* 重构后的主组件,使用模块化结构
|
* 重构后的主组件,使用模块化结构和 SubTabContainer 二级导航
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, ReactNode } from 'react';
|
import React, { useState, useMemo, ReactNode } from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Container,
|
Container,
|
||||||
@@ -11,20 +11,12 @@ import {
|
|||||||
HStack,
|
HStack,
|
||||||
Card,
|
Card,
|
||||||
CardBody,
|
CardBody,
|
||||||
CardHeader,
|
|
||||||
Heading,
|
|
||||||
Text,
|
Text,
|
||||||
Badge,
|
|
||||||
Select,
|
Select,
|
||||||
IconButton,
|
IconButton,
|
||||||
Alert,
|
Alert,
|
||||||
AlertIcon,
|
AlertIcon,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
Tabs,
|
|
||||||
TabList,
|
|
||||||
TabPanels,
|
|
||||||
Tab,
|
|
||||||
TabPanel,
|
|
||||||
Modal,
|
Modal,
|
||||||
ModalOverlay,
|
ModalOverlay,
|
||||||
ModalContent,
|
ModalContent,
|
||||||
@@ -40,25 +32,25 @@ import {
|
|||||||
Td,
|
Td,
|
||||||
TableContainer,
|
TableContainer,
|
||||||
Divider,
|
Divider,
|
||||||
Tooltip,
|
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { RepeatIcon } from '@chakra-ui/icons';
|
import { RepeatIcon } from '@chakra-ui/icons';
|
||||||
|
import { BarChart3, DollarSign, TrendingUp } from 'lucide-react';
|
||||||
import ReactECharts from 'echarts-for-react';
|
import ReactECharts from 'echarts-for-react';
|
||||||
import { formatUtils } from '@services/financialService';
|
import { formatUtils } from '@services/financialService';
|
||||||
|
|
||||||
|
// 通用组件
|
||||||
|
import SubTabContainer, { type SubTabConfig } from '@components/SubTabContainer';
|
||||||
|
|
||||||
// 内部模块导入
|
// 内部模块导入
|
||||||
import { useFinancialData } from './hooks';
|
import { useFinancialData } from './hooks';
|
||||||
import { COLORS } from './constants';
|
import { COLORS } from './constants';
|
||||||
import { calculateYoYChange, getCellBackground, getMetricChartOption } from './utils';
|
import { calculateYoYChange, getCellBackground, getMetricChartOption } from './utils';
|
||||||
import {
|
import {
|
||||||
StockInfoHeader,
|
StockInfoHeader,
|
||||||
BalanceSheetTable,
|
|
||||||
IncomeStatementTable,
|
|
||||||
CashflowTable,
|
|
||||||
FinancialMetricsTable,
|
FinancialMetricsTable,
|
||||||
MainBusinessAnalysis,
|
MainBusinessAnalysis,
|
||||||
ComparisonAnalysis,
|
|
||||||
} from './components';
|
} from './components';
|
||||||
|
import { BalanceSheetTab, IncomeStatementTab, CashflowTab } from './tabs';
|
||||||
import type { FinancialPanoramaProps } from './types';
|
import type { FinancialPanoramaProps } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,29 +65,24 @@ const FinancialPanorama: React.FC<FinancialPanoramaProps> = ({ stockCode: propSt
|
|||||||
cashflow,
|
cashflow,
|
||||||
financialMetrics,
|
financialMetrics,
|
||||||
mainBusiness,
|
mainBusiness,
|
||||||
forecast,
|
|
||||||
industryRank,
|
|
||||||
comparison,
|
|
||||||
loading,
|
loading,
|
||||||
error,
|
error,
|
||||||
refetch,
|
refetch,
|
||||||
currentStockCode,
|
|
||||||
selectedPeriods,
|
selectedPeriods,
|
||||||
setSelectedPeriods,
|
setSelectedPeriods,
|
||||||
} = useFinancialData({ stockCode: propStockCode });
|
} = useFinancialData({ stockCode: propStockCode });
|
||||||
|
|
||||||
// UI 状态
|
// UI 状态
|
||||||
const [activeTab, setActiveTab] = useState(0);
|
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const [modalContent, setModalContent] = useState<ReactNode>(null);
|
const [modalContent, setModalContent] = useState<ReactNode>(null);
|
||||||
|
|
||||||
// 颜色配置
|
// 颜色配置
|
||||||
const { bgColor, hoverBg, positiveColor, negativeColor, borderColor } = COLORS;
|
const { bgColor, hoverBg, positiveColor, negativeColor } = COLORS;
|
||||||
|
|
||||||
// 点击指标行显示图表
|
// 点击指标行显示图表
|
||||||
const showMetricChart = (
|
const showMetricChart = (
|
||||||
metricName: string,
|
metricName: string,
|
||||||
metricKey: string,
|
_metricKey: string,
|
||||||
data: Array<{ period: string; [key: string]: unknown }>,
|
data: Array<{ period: string; [key: string]: unknown }>,
|
||||||
dataPath: string
|
dataPath: string
|
||||||
) => {
|
) => {
|
||||||
@@ -204,6 +191,45 @@ const FinancialPanorama: React.FC<FinancialPanoramaProps> = ({ stockCode: propSt
|
|||||||
hoverBg,
|
hoverBg,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Tab 配置 - 只保留三大财务报表
|
||||||
|
const tabConfigs: SubTabConfig[] = useMemo(
|
||||||
|
() => [
|
||||||
|
{ key: 'balance', name: '资产负债表', icon: BarChart3, component: BalanceSheetTab },
|
||||||
|
{ key: 'income', name: '利润表', icon: DollarSign, component: IncomeStatementTab },
|
||||||
|
{ key: 'cashflow', name: '现金流量表', icon: TrendingUp, component: CashflowTab },
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 传递给 Tab 组件的 props
|
||||||
|
const componentProps = useMemo(
|
||||||
|
() => ({
|
||||||
|
// 数据
|
||||||
|
balanceSheet,
|
||||||
|
incomeStatement,
|
||||||
|
cashflow,
|
||||||
|
// 工具函数
|
||||||
|
showMetricChart,
|
||||||
|
calculateYoYChange,
|
||||||
|
getCellBackground,
|
||||||
|
// 颜色配置
|
||||||
|
positiveColor,
|
||||||
|
negativeColor,
|
||||||
|
bgColor,
|
||||||
|
hoverBg,
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
balanceSheet,
|
||||||
|
incomeStatement,
|
||||||
|
cashflow,
|
||||||
|
showMetricChart,
|
||||||
|
positiveColor,
|
||||||
|
negativeColor,
|
||||||
|
bgColor,
|
||||||
|
hoverBg,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container maxW="container.xl" py={5}>
|
<Container maxW="container.xl" py={5}>
|
||||||
<VStack spacing={6} align="stretch">
|
<VStack spacing={6} align="stretch">
|
||||||
@@ -250,124 +276,35 @@ const FinancialPanorama: React.FC<FinancialPanoramaProps> = ({ stockCode: propSt
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 主要内容区域 */}
|
{/* 财务指标速览 */}
|
||||||
{!loading && stockInfo && (
|
{!loading && stockInfo && (
|
||||||
<Tabs
|
<FinancialMetricsTable data={financialMetrics} {...tableProps} />
|
||||||
index={activeTab}
|
)}
|
||||||
onChange={setActiveTab}
|
|
||||||
variant="enclosed"
|
|
||||||
colorScheme="blue"
|
|
||||||
>
|
|
||||||
<TabList>
|
|
||||||
<Tab>财务概览</Tab>
|
|
||||||
<Tab>资产负债表</Tab>
|
|
||||||
<Tab>利润表</Tab>
|
|
||||||
<Tab>现金流量表</Tab>
|
|
||||||
<Tab>财务指标</Tab>
|
|
||||||
<Tab>主营业务</Tab>
|
|
||||||
</TabList>
|
|
||||||
|
|
||||||
<TabPanels>
|
{/* 主营业务 */}
|
||||||
{/* 财务概览 */}
|
{!loading && stockInfo && (
|
||||||
<TabPanel>
|
<Card>
|
||||||
<VStack spacing={4} align="stretch">
|
<CardBody>
|
||||||
<ComparisonAnalysis comparison={comparison} />
|
<Text fontSize="lg" fontWeight="bold" mb={4}>
|
||||||
<FinancialMetricsTable data={financialMetrics} {...tableProps} />
|
主营业务
|
||||||
</VStack>
|
</Text>
|
||||||
</TabPanel>
|
<MainBusinessAnalysis mainBusiness={mainBusiness} />
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 资产负债表 */}
|
{/* 三大财务报表 - 使用 SubTabContainer 二级导航 */}
|
||||||
<TabPanel>
|
{!loading && stockInfo && (
|
||||||
<Card>
|
<Card bg="gray.900" shadow="md" border="1px solid" borderColor="rgba(212, 175, 55, 0.3)">
|
||||||
<CardHeader>
|
<CardBody p={0}>
|
||||||
<VStack align="stretch" spacing={2}>
|
<SubTabContainer
|
||||||
<HStack justify="space-between">
|
tabs={tabConfigs}
|
||||||
<Heading size="md">资产负债表</Heading>
|
componentProps={componentProps}
|
||||||
<HStack spacing={2}>
|
themePreset="blackGold"
|
||||||
<Badge colorScheme="blue">
|
isLazy
|
||||||
显示最近{Math.min(balanceSheet.length, 8)}期
|
/>
|
||||||
</Badge>
|
</CardBody>
|
||||||
<Text fontSize="sm" color="gray.500">
|
</Card>
|
||||||
红涨绿跌 | 同比变化
|
|
||||||
</Text>
|
|
||||||
</HStack>
|
|
||||||
</HStack>
|
|
||||||
<Text fontSize="xs" color="gray.500">
|
|
||||||
提示:表格可横向滚动查看更多数据,点击行查看历史趋势
|
|
||||||
</Text>
|
|
||||||
</VStack>
|
|
||||||
</CardHeader>
|
|
||||||
<CardBody>
|
|
||||||
<BalanceSheetTable data={balanceSheet} {...tableProps} />
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</TabPanel>
|
|
||||||
|
|
||||||
{/* 利润表 */}
|
|
||||||
<TabPanel>
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<VStack align="stretch" spacing={2}>
|
|
||||||
<HStack justify="space-between">
|
|
||||||
<Heading size="md">利润表</Heading>
|
|
||||||
<HStack spacing={2}>
|
|
||||||
<Badge colorScheme="blue">
|
|
||||||
显示最近{Math.min(incomeStatement.length, 8)}期
|
|
||||||
</Badge>
|
|
||||||
<Text fontSize="sm" color="gray.500">
|
|
||||||
红涨绿跌 | 同比变化
|
|
||||||
</Text>
|
|
||||||
</HStack>
|
|
||||||
</HStack>
|
|
||||||
<Text fontSize="xs" color="gray.500">
|
|
||||||
提示:Q1、中报、Q3、年报数据为累计值,同比显示与去年同期对比
|
|
||||||
</Text>
|
|
||||||
</VStack>
|
|
||||||
</CardHeader>
|
|
||||||
<CardBody>
|
|
||||||
<IncomeStatementTable data={incomeStatement} {...tableProps} />
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</TabPanel>
|
|
||||||
|
|
||||||
{/* 现金流量表 */}
|
|
||||||
<TabPanel>
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<VStack align="stretch" spacing={2}>
|
|
||||||
<HStack justify="space-between">
|
|
||||||
<Heading size="md">现金流量表</Heading>
|
|
||||||
<HStack spacing={2}>
|
|
||||||
<Badge colorScheme="blue">
|
|
||||||
显示最近{Math.min(cashflow.length, 8)}期
|
|
||||||
</Badge>
|
|
||||||
<Text fontSize="sm" color="gray.500">
|
|
||||||
红涨绿跌 | 同比变化
|
|
||||||
</Text>
|
|
||||||
</HStack>
|
|
||||||
</HStack>
|
|
||||||
<Text fontSize="xs" color="gray.500">
|
|
||||||
提示:现金流数据为累计值,正值红色表示现金流入,负值绿色表示现金流出
|
|
||||||
</Text>
|
|
||||||
</VStack>
|
|
||||||
</CardHeader>
|
|
||||||
<CardBody>
|
|
||||||
<CashflowTable data={cashflow} {...tableProps} />
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</TabPanel>
|
|
||||||
|
|
||||||
{/* 财务指标 */}
|
|
||||||
<TabPanel>
|
|
||||||
<FinancialMetricsTable data={financialMetrics} {...tableProps} />
|
|
||||||
</TabPanel>
|
|
||||||
|
|
||||||
{/* 主营业务 */}
|
|
||||||
<TabPanel>
|
|
||||||
<MainBusinessAnalysis mainBusiness={mainBusiness} />
|
|
||||||
</TabPanel>
|
|
||||||
</TabPanels>
|
|
||||||
</Tabs>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 错误提示 */}
|
{/* 错误提示 */}
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* 资产负债表 Tab
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardBody,
|
||||||
|
CardHeader,
|
||||||
|
VStack,
|
||||||
|
HStack,
|
||||||
|
Heading,
|
||||||
|
Badge,
|
||||||
|
Text,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { BalanceSheetTable } from '../components';
|
||||||
|
import type { BalanceSheetData } from '../types';
|
||||||
|
|
||||||
|
export interface BalanceSheetTabProps {
|
||||||
|
balanceSheet: BalanceSheetData[];
|
||||||
|
showMetricChart: (name: string, key: string, data: unknown[], path: string) => void;
|
||||||
|
calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number };
|
||||||
|
getCellBackground: (change: number, intensity: number) => string;
|
||||||
|
positiveColor: string;
|
||||||
|
negativeColor: string;
|
||||||
|
bgColor: string;
|
||||||
|
hoverBg: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BalanceSheetTab: React.FC<BalanceSheetTabProps> = ({
|
||||||
|
balanceSheet,
|
||||||
|
showMetricChart,
|
||||||
|
calculateYoYChange,
|
||||||
|
getCellBackground,
|
||||||
|
positiveColor,
|
||||||
|
negativeColor,
|
||||||
|
bgColor,
|
||||||
|
hoverBg,
|
||||||
|
}) => {
|
||||||
|
const tableProps = {
|
||||||
|
showMetricChart,
|
||||||
|
calculateYoYChange,
|
||||||
|
getCellBackground,
|
||||||
|
positiveColor,
|
||||||
|
negativeColor,
|
||||||
|
bgColor,
|
||||||
|
hoverBg,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<VStack align="stretch" spacing={2}>
|
||||||
|
<HStack justify="space-between">
|
||||||
|
<Heading size="md">资产负债表</Heading>
|
||||||
|
<HStack spacing={2}>
|
||||||
|
<Badge colorScheme="blue">
|
||||||
|
显示最近{Math.min(balanceSheet.length, 8)}期
|
||||||
|
</Badge>
|
||||||
|
<Text fontSize="sm" color="gray.500">
|
||||||
|
红涨绿跌 | 同比变化
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
</HStack>
|
||||||
|
<Text fontSize="xs" color="gray.500">
|
||||||
|
提示:表格可横向滚动查看更多数据,点击行查看历史趋势
|
||||||
|
</Text>
|
||||||
|
</VStack>
|
||||||
|
</CardHeader>
|
||||||
|
<CardBody>
|
||||||
|
<BalanceSheetTable data={balanceSheet} {...tableProps} />
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BalanceSheetTab;
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* 现金流量表 Tab
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardBody,
|
||||||
|
CardHeader,
|
||||||
|
VStack,
|
||||||
|
HStack,
|
||||||
|
Heading,
|
||||||
|
Badge,
|
||||||
|
Text,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { CashflowTable } from '../components';
|
||||||
|
import type { CashflowData } from '../types';
|
||||||
|
|
||||||
|
export interface CashflowTabProps {
|
||||||
|
cashflow: CashflowData[];
|
||||||
|
showMetricChart: (name: string, key: string, data: unknown[], path: string) => void;
|
||||||
|
calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number };
|
||||||
|
getCellBackground: (change: number, intensity: number) => string;
|
||||||
|
positiveColor: string;
|
||||||
|
negativeColor: string;
|
||||||
|
bgColor: string;
|
||||||
|
hoverBg: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CashflowTab: React.FC<CashflowTabProps> = ({
|
||||||
|
cashflow,
|
||||||
|
showMetricChart,
|
||||||
|
calculateYoYChange,
|
||||||
|
getCellBackground,
|
||||||
|
positiveColor,
|
||||||
|
negativeColor,
|
||||||
|
bgColor,
|
||||||
|
hoverBg,
|
||||||
|
}) => {
|
||||||
|
const tableProps = {
|
||||||
|
showMetricChart,
|
||||||
|
calculateYoYChange,
|
||||||
|
getCellBackground,
|
||||||
|
positiveColor,
|
||||||
|
negativeColor,
|
||||||
|
bgColor,
|
||||||
|
hoverBg,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<VStack align="stretch" spacing={2}>
|
||||||
|
<HStack justify="space-between">
|
||||||
|
<Heading size="md">现金流量表</Heading>
|
||||||
|
<HStack spacing={2}>
|
||||||
|
<Badge colorScheme="blue">
|
||||||
|
显示最近{Math.min(cashflow.length, 8)}期
|
||||||
|
</Badge>
|
||||||
|
<Text fontSize="sm" color="gray.500">
|
||||||
|
红涨绿跌 | 同比变化
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
</HStack>
|
||||||
|
<Text fontSize="xs" color="gray.500">
|
||||||
|
提示:现金流数据为累计值,正值红色表示现金流入,负值绿色表示现金流出
|
||||||
|
</Text>
|
||||||
|
</VStack>
|
||||||
|
</CardHeader>
|
||||||
|
<CardBody>
|
||||||
|
<CashflowTable data={cashflow} {...tableProps} />
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CashflowTab;
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* 利润表 Tab
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardBody,
|
||||||
|
CardHeader,
|
||||||
|
VStack,
|
||||||
|
HStack,
|
||||||
|
Heading,
|
||||||
|
Badge,
|
||||||
|
Text,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { IncomeStatementTable } from '../components';
|
||||||
|
import type { IncomeStatementData } from '../types';
|
||||||
|
|
||||||
|
export interface IncomeStatementTabProps {
|
||||||
|
incomeStatement: IncomeStatementData[];
|
||||||
|
showMetricChart: (name: string, key: string, data: unknown[], path: string) => void;
|
||||||
|
calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number };
|
||||||
|
getCellBackground: (change: number, intensity: number) => string;
|
||||||
|
positiveColor: string;
|
||||||
|
negativeColor: string;
|
||||||
|
bgColor: string;
|
||||||
|
hoverBg: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IncomeStatementTab: React.FC<IncomeStatementTabProps> = ({
|
||||||
|
incomeStatement,
|
||||||
|
showMetricChart,
|
||||||
|
calculateYoYChange,
|
||||||
|
getCellBackground,
|
||||||
|
positiveColor,
|
||||||
|
negativeColor,
|
||||||
|
bgColor,
|
||||||
|
hoverBg,
|
||||||
|
}) => {
|
||||||
|
const tableProps = {
|
||||||
|
showMetricChart,
|
||||||
|
calculateYoYChange,
|
||||||
|
getCellBackground,
|
||||||
|
positiveColor,
|
||||||
|
negativeColor,
|
||||||
|
bgColor,
|
||||||
|
hoverBg,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<VStack align="stretch" spacing={2}>
|
||||||
|
<HStack justify="space-between">
|
||||||
|
<Heading size="md">利润表</Heading>
|
||||||
|
<HStack spacing={2}>
|
||||||
|
<Badge colorScheme="blue">
|
||||||
|
显示最近{Math.min(incomeStatement.length, 8)}期
|
||||||
|
</Badge>
|
||||||
|
<Text fontSize="sm" color="gray.500">
|
||||||
|
红涨绿跌 | 同比变化
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
</HStack>
|
||||||
|
<Text fontSize="xs" color="gray.500">
|
||||||
|
提示:Q1、中报、Q3、年报数据为累计值,同比显示与去年同期对比
|
||||||
|
</Text>
|
||||||
|
</VStack>
|
||||||
|
</CardHeader>
|
||||||
|
<CardBody>
|
||||||
|
<IncomeStatementTable data={incomeStatement} {...tableProps} />
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default IncomeStatementTab;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* 主营业务 Tab
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { MainBusinessAnalysis } from '../components';
|
||||||
|
import type { MainBusinessData } from '../types';
|
||||||
|
|
||||||
|
export interface MainBusinessTabProps {
|
||||||
|
mainBusiness: MainBusinessData | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MainBusinessTab: React.FC<MainBusinessTabProps> = ({ mainBusiness }) => {
|
||||||
|
return <MainBusinessAnalysis mainBusiness={mainBusiness} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MainBusinessTab;
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* 财务指标 Tab
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { FinancialMetricsTable } from '../components';
|
||||||
|
import type { FinancialMetricsData } from '../types';
|
||||||
|
|
||||||
|
export interface MetricsTabProps {
|
||||||
|
financialMetrics: FinancialMetricsData[];
|
||||||
|
showMetricChart: (name: string, key: string, data: unknown[], path: string) => void;
|
||||||
|
calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number };
|
||||||
|
getCellBackground: (change: number, intensity: number) => string;
|
||||||
|
positiveColor: string;
|
||||||
|
negativeColor: string;
|
||||||
|
bgColor: string;
|
||||||
|
hoverBg: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MetricsTab: React.FC<MetricsTabProps> = ({
|
||||||
|
financialMetrics,
|
||||||
|
showMetricChart,
|
||||||
|
calculateYoYChange,
|
||||||
|
getCellBackground,
|
||||||
|
positiveColor,
|
||||||
|
negativeColor,
|
||||||
|
bgColor,
|
||||||
|
hoverBg,
|
||||||
|
}) => {
|
||||||
|
const tableProps = {
|
||||||
|
showMetricChart,
|
||||||
|
calculateYoYChange,
|
||||||
|
getCellBackground,
|
||||||
|
positiveColor,
|
||||||
|
negativeColor,
|
||||||
|
bgColor,
|
||||||
|
hoverBg,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <FinancialMetricsTable data={financialMetrics} {...tableProps} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MetricsTab;
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* 财务概览 Tab
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { VStack } from '@chakra-ui/react';
|
||||||
|
import { ComparisonAnalysis, FinancialMetricsTable } from '../components';
|
||||||
|
import type { FinancialMetricsData, ComparisonData } from '../types';
|
||||||
|
|
||||||
|
export interface OverviewTabProps {
|
||||||
|
comparison: ComparisonData[];
|
||||||
|
financialMetrics: FinancialMetricsData[];
|
||||||
|
showMetricChart: (name: string, key: string, data: unknown[], path: string) => void;
|
||||||
|
calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number };
|
||||||
|
getCellBackground: (change: number, intensity: number) => string;
|
||||||
|
positiveColor: string;
|
||||||
|
negativeColor: string;
|
||||||
|
bgColor: string;
|
||||||
|
hoverBg: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const OverviewTab: React.FC<OverviewTabProps> = ({
|
||||||
|
comparison,
|
||||||
|
financialMetrics,
|
||||||
|
showMetricChart,
|
||||||
|
calculateYoYChange,
|
||||||
|
getCellBackground,
|
||||||
|
positiveColor,
|
||||||
|
negativeColor,
|
||||||
|
bgColor,
|
||||||
|
hoverBg,
|
||||||
|
}) => {
|
||||||
|
const tableProps = {
|
||||||
|
showMetricChart,
|
||||||
|
calculateYoYChange,
|
||||||
|
getCellBackground,
|
||||||
|
positiveColor,
|
||||||
|
negativeColor,
|
||||||
|
bgColor,
|
||||||
|
hoverBg,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VStack spacing={4} align="stretch">
|
||||||
|
<ComparisonAnalysis comparison={comparison} />
|
||||||
|
<FinancialMetricsTable data={financialMetrics} {...tableProps} />
|
||||||
|
</VStack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OverviewTab;
|
||||||
12
src/views/Company/components/FinancialPanorama/tabs/index.ts
Normal file
12
src/views/Company/components/FinancialPanorama/tabs/index.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Tab 组件统一导出
|
||||||
|
* 仅保留三大财务报表 Tab
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { default as BalanceSheetTab } from './BalanceSheetTab';
|
||||||
|
export { default as IncomeStatementTab } from './IncomeStatementTab';
|
||||||
|
export { default as CashflowTab } from './CashflowTab';
|
||||||
|
|
||||||
|
export type { BalanceSheetTabProps } from './BalanceSheetTab';
|
||||||
|
export type { IncomeStatementTabProps } from './IncomeStatementTab';
|
||||||
|
export type { CashflowTabProps } from './CashflowTab';
|
||||||
@@ -392,8 +392,10 @@ export interface MainBusinessAnalysisProps {
|
|||||||
/** 行业排名 Props */
|
/** 行业排名 Props */
|
||||||
export interface IndustryRankingViewProps {
|
export interface IndustryRankingViewProps {
|
||||||
industryRank: IndustryRankData[];
|
industryRank: IndustryRankData[];
|
||||||
bgColor: string;
|
bgColor?: string;
|
||||||
borderColor: string;
|
borderColor?: string;
|
||||||
|
textColor?: string;
|
||||||
|
labelColor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 股票对比 Props */
|
/** 股票对比 Props */
|
||||||
|
|||||||
Reference in New Issue
Block a user