style(MainBusinessAnalysis): 优化历史对比表格布局
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -4,18 +4,9 @@
|
|||||||
|
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
VStack,
|
Flex,
|
||||||
Grid,
|
|
||||||
GridItem,
|
|
||||||
Box,
|
Box,
|
||||||
Heading,
|
Heading,
|
||||||
Table,
|
|
||||||
Thead,
|
|
||||||
Tbody,
|
|
||||||
Tr,
|
|
||||||
Th,
|
|
||||||
Td,
|
|
||||||
TableContainer,
|
|
||||||
Alert,
|
Alert,
|
||||||
AlertIcon,
|
AlertIcon,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
@@ -45,7 +36,7 @@ const BLACK_GOLD_THEME = {
|
|||||||
algorithm: antTheme.darkAlgorithm,
|
algorithm: antTheme.darkAlgorithm,
|
||||||
token: {
|
token: {
|
||||||
colorPrimary: '#D4AF37',
|
colorPrimary: '#D4AF37',
|
||||||
colorBgContainer: 'transparent',
|
colorBgContainer: '#1A202C',
|
||||||
colorBgElevated: '#1a1a2e',
|
colorBgElevated: '#1a1a2e',
|
||||||
colorBorder: 'rgba(212, 175, 55, 0.3)',
|
colorBorder: 'rgba(212, 175, 55, 0.3)',
|
||||||
colorText: '#e0e0e0',
|
colorText: '#e0e0e0',
|
||||||
@@ -65,41 +56,79 @@ const BLACK_GOLD_THEME = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// 历史对比表格数据行类型
|
// 固定列背景样式(防止滚动时内容重叠)
|
||||||
|
const fixedColumnStyles = `
|
||||||
|
.main-business-table .ant-table-cell-fix-left,
|
||||||
|
.main-business-table .ant-table-cell-fix-right {
|
||||||
|
background: #1A202C !important;
|
||||||
|
}
|
||||||
|
.main-business-table .ant-table-thead .ant-table-cell-fix-left,
|
||||||
|
.main-business-table .ant-table-thead .ant-table-cell-fix-right {
|
||||||
|
background: rgba(26, 32, 44, 0.95) !important;
|
||||||
|
}
|
||||||
|
.main-business-table .ant-table-tbody > tr:hover .ant-table-cell-fix-left,
|
||||||
|
.main-business-table .ant-table-tbody > tr:hover .ant-table-cell-fix-right {
|
||||||
|
background: rgba(212, 175, 55, 0.08) !important;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 历史对比表格数据行类型(包含业务明细)
|
||||||
interface HistoricalRowData {
|
interface HistoricalRowData {
|
||||||
key: string;
|
key: string;
|
||||||
business: string;
|
business: string;
|
||||||
|
grossMargin?: number;
|
||||||
|
profit?: number;
|
||||||
[period: string]: string | number | undefined;
|
[period: string]: string | number | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 历史对比表格组件
|
// 历史对比表格组件(整合业务明细)
|
||||||
interface HistoricalComparisonTableProps {
|
interface HistoricalComparisonTableProps {
|
||||||
historicalData: (ProductClassification | IndustryClassification)[];
|
historicalData: (ProductClassification | IndustryClassification)[];
|
||||||
businessItems: BusinessItem[];
|
businessItems: BusinessItem[];
|
||||||
hasProductData: boolean;
|
hasProductData: boolean;
|
||||||
|
latestReportType: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const HistoricalComparisonTable: React.FC<HistoricalComparisonTableProps> = ({
|
const HistoricalComparisonTable: React.FC<HistoricalComparisonTableProps> = ({
|
||||||
historicalData,
|
historicalData,
|
||||||
businessItems,
|
businessItems,
|
||||||
hasProductData,
|
hasProductData,
|
||||||
|
latestReportType,
|
||||||
}) => {
|
}) => {
|
||||||
// 动态生成列配置
|
// 动态生成列配置
|
||||||
const columns: ColumnsType<HistoricalRowData> = useMemo(() => {
|
const columns: ColumnsType<HistoricalRowData> = useMemo(() => {
|
||||||
const cols: ColumnsType<HistoricalRowData> = [
|
const cols: ColumnsType<HistoricalRowData> = [
|
||||||
{
|
{
|
||||||
title: '业务/期间',
|
title: '业务',
|
||||||
dataIndex: 'business',
|
dataIndex: 'business',
|
||||||
key: 'business',
|
key: 'business',
|
||||||
fixed: 'left',
|
fixed: 'left',
|
||||||
width: 150,
|
width: 150,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: `毛利率(${latestReportType})`,
|
||||||
|
dataIndex: 'grossMargin',
|
||||||
|
key: 'grossMargin',
|
||||||
|
align: 'right',
|
||||||
|
width: 120,
|
||||||
|
render: (value: number | undefined) =>
|
||||||
|
value !== undefined ? formatUtils.formatPercent(value) : '-',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: `利润(${latestReportType})`,
|
||||||
|
dataIndex: 'profit',
|
||||||
|
key: 'profit',
|
||||||
|
align: 'right',
|
||||||
|
width: 100,
|
||||||
|
render: (value: number | undefined) =>
|
||||||
|
value !== undefined ? formatUtils.formatLargeNumber(value) : '-',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// 添加各期间列
|
// 添加各期间营收列
|
||||||
historicalData.slice(0, 4).forEach((period) => {
|
historicalData.slice(0, 4).forEach((period) => {
|
||||||
cols.push({
|
cols.push({
|
||||||
title: period.report_type,
|
title: `营收(${period.report_type})`,
|
||||||
dataIndex: period.period,
|
dataIndex: period.period,
|
||||||
key: period.period,
|
key: period.period,
|
||||||
align: 'right',
|
align: 'right',
|
||||||
@@ -112,9 +141,9 @@ const HistoricalComparisonTable: React.FC<HistoricalComparisonTableProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return cols;
|
return cols;
|
||||||
}, [historicalData]);
|
}, [historicalData, latestReportType]);
|
||||||
|
|
||||||
// 生成表格数据
|
// 生成表格数据(包含业务明细)
|
||||||
const dataSource: HistoricalRowData[] = useMemo(() => {
|
const dataSource: HistoricalRowData[] = useMemo(() => {
|
||||||
return businessItems
|
return businessItems
|
||||||
.filter((item: BusinessItem) => item.content !== '合计')
|
.filter((item: BusinessItem) => item.content !== '合计')
|
||||||
@@ -122,8 +151,11 @@ const HistoricalComparisonTable: React.FC<HistoricalComparisonTableProps> = ({
|
|||||||
const row: HistoricalRowData = {
|
const row: HistoricalRowData = {
|
||||||
key: `${idx}`,
|
key: `${idx}`,
|
||||||
business: item.content,
|
business: item.content,
|
||||||
|
grossMargin: item.gross_margin || item.profit_margin,
|
||||||
|
profit: item.profit,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 添加各期间营收数据
|
||||||
historicalData.slice(0, 4).forEach((period) => {
|
historicalData.slice(0, 4).forEach((period) => {
|
||||||
const periodItems: BusinessItem[] = hasProductData
|
const periodItems: BusinessItem[] = hasProductData
|
||||||
? (period as ProductClassification).products
|
? (period as ProductClassification).products
|
||||||
@@ -145,13 +177,16 @@ const HistoricalComparisonTable: React.FC<HistoricalComparisonTableProps> = ({
|
|||||||
borderColor={THEME.border}
|
borderColor={THEME.border}
|
||||||
borderRadius="md"
|
borderRadius="md"
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
|
h="100%"
|
||||||
|
className="main-business-table"
|
||||||
>
|
>
|
||||||
|
<style>{fixedColumnStyles}</style>
|
||||||
<Box px={4} py={3} borderBottom="1px solid" borderColor={THEME.border}>
|
<Box px={4} py={3} borderBottom="1px solid" borderColor={THEME.border}>
|
||||||
<Heading size="sm" color={THEME.headingColor}>
|
<Heading size="sm" color={THEME.headingColor}>
|
||||||
主营业务历史对比
|
主营业务明细与历史对比
|
||||||
</Heading>
|
</Heading>
|
||||||
</Box>
|
</Box>
|
||||||
<Box p={4}>
|
<Box p={4} overflowX="auto">
|
||||||
<ConfigProvider theme={BLACK_GOLD_THEME}>
|
<ConfigProvider theme={BLACK_GOLD_THEME}>
|
||||||
<AntTable<HistoricalRowData>
|
<AntTable<HistoricalRowData>
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@@ -218,77 +253,35 @@ export const MainBusinessAnalysis: React.FC<MainBusinessAnalysisProps> = ({
|
|||||||
: (mainBusiness!.industry_classification! as IndustryClassification[]);
|
: (mainBusiness!.industry_classification! as IndustryClassification[]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VStack spacing={4} align="stretch">
|
<Flex
|
||||||
<Grid templateColumns="repeat(2, 1fr)" gap={4}>
|
direction={{ base: 'column', lg: 'row' }}
|
||||||
<GridItem>
|
gap={4}
|
||||||
<Box
|
>
|
||||||
bg={THEME.cardBg}
|
{/* 左侧:饼图 */}
|
||||||
border="1px solid"
|
<Box
|
||||||
borderColor={THEME.border}
|
flexShrink={0}
|
||||||
borderRadius="md"
|
w={{ base: '100%', lg: '340px' }}
|
||||||
p={4}
|
bg={THEME.cardBg}
|
||||||
>
|
border="1px solid"
|
||||||
<ReactECharts option={pieOption} style={{ height: '300px' }} />
|
borderColor={THEME.border}
|
||||||
</Box>
|
borderRadius="md"
|
||||||
</GridItem>
|
p={4}
|
||||||
<GridItem>
|
>
|
||||||
<Box
|
<ReactECharts option={pieOption} style={{ height: '280px' }} />
|
||||||
bg={THEME.cardBg}
|
</Box>
|
||||||
border="1px solid"
|
|
||||||
borderColor={THEME.border}
|
|
||||||
borderRadius="md"
|
|
||||||
overflow="hidden"
|
|
||||||
>
|
|
||||||
<Box px={4} py={3} borderBottom="1px solid" borderColor={THEME.border}>
|
|
||||||
<Heading size="sm" color={THEME.headingColor}>
|
|
||||||
业务明细 - {latestPeriod.report_type}
|
|
||||||
</Heading>
|
|
||||||
</Box>
|
|
||||||
<Box p={4}>
|
|
||||||
<TableContainer>
|
|
||||||
<Table size="sm">
|
|
||||||
<Thead>
|
|
||||||
<Tr>
|
|
||||||
<Th color={THEME.thColor} borderColor={THEME.border}>业务</Th>
|
|
||||||
<Th isNumeric color={THEME.thColor} borderColor={THEME.border}>营收</Th>
|
|
||||||
<Th isNumeric color={THEME.thColor} borderColor={THEME.border}>毛利率(%)</Th>
|
|
||||||
<Th isNumeric color={THEME.thColor} borderColor={THEME.border}>利润</Th>
|
|
||||||
</Tr>
|
|
||||||
</Thead>
|
|
||||||
<Tbody>
|
|
||||||
{businessItems
|
|
||||||
.filter((item: BusinessItem) => item.content !== '合计')
|
|
||||||
.map((item: BusinessItem, idx: number) => (
|
|
||||||
<Tr key={idx}>
|
|
||||||
<Td color={THEME.textColor} borderColor={THEME.border}>{item.content}</Td>
|
|
||||||
<Td isNumeric color={THEME.textColor} borderColor={THEME.border}>
|
|
||||||
{formatUtils.formatLargeNumber(item.revenue)}
|
|
||||||
</Td>
|
|
||||||
<Td isNumeric color={THEME.textColor} borderColor={THEME.border}>
|
|
||||||
{formatUtils.formatPercent(item.gross_margin || item.profit_margin)}
|
|
||||||
</Td>
|
|
||||||
<Td isNumeric color={THEME.textColor} borderColor={THEME.border}>
|
|
||||||
{formatUtils.formatLargeNumber(item.profit)}
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
))}
|
|
||||||
</Tbody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</GridItem>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* 历史对比 - Ant Design Table 黑金主题 */}
|
{/* 右侧:业务明细与历史对比表格 */}
|
||||||
{historicalData.length > 1 && (
|
<Box flex={1} minW={0} overflow="hidden">
|
||||||
<HistoricalComparisonTable
|
{historicalData.length > 0 && (
|
||||||
historicalData={historicalData}
|
<HistoricalComparisonTable
|
||||||
businessItems={businessItems}
|
historicalData={historicalData}
|
||||||
hasProductData={hasProductData}
|
businessItems={businessItems}
|
||||||
/>
|
hasProductData={hasProductData}
|
||||||
)}
|
latestReportType={latestPeriod.report_type}
|
||||||
</VStack>
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user