refactor(CorePositioningCard): 模块化拆分与黑金 UI 优化
- 拆分为独立目录结构: atoms/, theme.ts, index.tsx - 提取子组件: HighlightCard, ModelBlock, SectionHeader - 应用黑金风格: 金色边框、透明背景、金色标题 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,94 +0,0 @@
|
|||||||
/**
|
|
||||||
* 核心定位卡片
|
|
||||||
*
|
|
||||||
* 显示公司的核心定位、投资亮点和商业模式
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardBody,
|
|
||||||
CardHeader,
|
|
||||||
VStack,
|
|
||||||
HStack,
|
|
||||||
Text,
|
|
||||||
Heading,
|
|
||||||
Alert,
|
|
||||||
AlertIcon,
|
|
||||||
Grid,
|
|
||||||
GridItem,
|
|
||||||
Box,
|
|
||||||
Icon,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { FaLightbulb } from 'react-icons/fa';
|
|
||||||
import { DisclaimerBox } from '../atoms';
|
|
||||||
import type { QualitativeAnalysis } from '../types';
|
|
||||||
|
|
||||||
interface CorePositioningCardProps {
|
|
||||||
qualitativeAnalysis: QualitativeAnalysis;
|
|
||||||
cardBg?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CorePositioningCard: React.FC<CorePositioningCardProps> = ({
|
|
||||||
qualitativeAnalysis,
|
|
||||||
cardBg,
|
|
||||||
}) => {
|
|
||||||
const blueBg = 'blue.50';
|
|
||||||
const greenBg = 'green.50';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card bg={cardBg} shadow="md">
|
|
||||||
<CardHeader>
|
|
||||||
<HStack>
|
|
||||||
<Icon as={FaLightbulb} color="yellow.500" />
|
|
||||||
<Heading size="sm">核心定位</Heading>
|
|
||||||
</HStack>
|
|
||||||
</CardHeader>
|
|
||||||
<CardBody>
|
|
||||||
<DisclaimerBox />
|
|
||||||
<VStack spacing={4} align="stretch">
|
|
||||||
{qualitativeAnalysis.core_positioning?.one_line_intro && (
|
|
||||||
<Alert status="info" variant="left-accent">
|
|
||||||
<AlertIcon />
|
|
||||||
<Text fontWeight="bold">
|
|
||||||
{qualitativeAnalysis.core_positioning.one_line_intro}
|
|
||||||
</Text>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Grid templateColumns="repeat(2, 1fr)" gap={4}>
|
|
||||||
<GridItem colSpan={{ base: 2, md: 1 }}>
|
|
||||||
<VStack align="stretch" spacing={3}>
|
|
||||||
<Text fontWeight="bold" fontSize="sm" color="gray.600">
|
|
||||||
投资亮点
|
|
||||||
</Text>
|
|
||||||
<Box p={3} bg={blueBg} borderRadius="md">
|
|
||||||
<Text fontSize="sm" whiteSpace="pre-wrap">
|
|
||||||
{qualitativeAnalysis.core_positioning?.investment_highlights ||
|
|
||||||
'暂无数据'}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
</VStack>
|
|
||||||
</GridItem>
|
|
||||||
|
|
||||||
<GridItem colSpan={{ base: 2, md: 1 }}>
|
|
||||||
<VStack align="stretch" spacing={3}>
|
|
||||||
<Text fontWeight="bold" fontSize="sm" color="gray.600">
|
|
||||||
商业模式
|
|
||||||
</Text>
|
|
||||||
<Box p={3} bg={greenBg} borderRadius="md">
|
|
||||||
<Text fontSize="sm" whiteSpace="pre-wrap">
|
|
||||||
{qualitativeAnalysis.core_positioning?.business_model_desc ||
|
|
||||||
'暂无数据'}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
</VStack>
|
|
||||||
</GridItem>
|
|
||||||
</Grid>
|
|
||||||
</VStack>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CorePositioningCard;
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* 投资亮点卡片组件
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
import { Box, HStack, VStack, Icon, Text } from '@chakra-ui/react';
|
||||||
|
import { FaUsers } from 'react-icons/fa';
|
||||||
|
import { THEME, ICON_MAP, HIGHLIGHT_HOVER_STYLES } from '../theme';
|
||||||
|
import type { InvestmentHighlightItem } from '../../../types';
|
||||||
|
|
||||||
|
interface HighlightCardProps {
|
||||||
|
highlight: InvestmentHighlightItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HighlightCard = memo<HighlightCardProps>(({ highlight }) => {
|
||||||
|
const IconComponent = ICON_MAP[highlight.icon] || FaUsers;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
p={4}
|
||||||
|
bg={THEME.light.cardBg}
|
||||||
|
borderRadius="lg"
|
||||||
|
border="1px solid"
|
||||||
|
borderColor="whiteAlpha.100"
|
||||||
|
{...HIGHLIGHT_HOVER_STYLES}
|
||||||
|
transition="border-color 0.2s"
|
||||||
|
>
|
||||||
|
<HStack spacing={3} align="flex-start">
|
||||||
|
<Box
|
||||||
|
p={2}
|
||||||
|
bg="whiteAlpha.100"
|
||||||
|
borderRadius="md"
|
||||||
|
color={THEME.light.titleColor}
|
||||||
|
>
|
||||||
|
<Icon as={IconComponent} boxSize={4} />
|
||||||
|
</Box>
|
||||||
|
<VStack align="start" spacing={1} flex={1}>
|
||||||
|
<Text fontWeight="bold" color={THEME.light.textColor} fontSize="sm">
|
||||||
|
{highlight.title}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
fontSize="xs"
|
||||||
|
color={THEME.light.subtextColor}
|
||||||
|
lineHeight="tall"
|
||||||
|
>
|
||||||
|
{highlight.description}
|
||||||
|
</Text>
|
||||||
|
</VStack>
|
||||||
|
</HStack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
HighlightCard.displayName = 'HighlightCard';
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* 商业模式板块组件
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
import { Box, VStack, HStack, Text, Tag, Divider } from '@chakra-ui/react';
|
||||||
|
import { THEME } from '../theme';
|
||||||
|
import type { BusinessModelSection } from '../../../types';
|
||||||
|
|
||||||
|
interface ModelBlockProps {
|
||||||
|
section: BusinessModelSection;
|
||||||
|
isLast?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ModelBlock = memo<ModelBlockProps>(({ section, isLast }) => (
|
||||||
|
<Box>
|
||||||
|
<VStack align="start" spacing={2}>
|
||||||
|
<Text fontWeight="bold" color={THEME.light.textColor} fontSize="sm">
|
||||||
|
{section.title}
|
||||||
|
</Text>
|
||||||
|
<Text fontSize="xs" color={THEME.light.subtextColor} lineHeight="tall">
|
||||||
|
{section.description}
|
||||||
|
</Text>
|
||||||
|
{section.tags && section.tags.length > 0 && (
|
||||||
|
<HStack spacing={2} flexWrap="wrap" mt={1}>
|
||||||
|
{section.tags.map((tag, idx) => (
|
||||||
|
<Tag
|
||||||
|
key={idx}
|
||||||
|
size="sm"
|
||||||
|
bg={THEME.light.tagBg}
|
||||||
|
color={THEME.light.tagColor}
|
||||||
|
borderRadius="full"
|
||||||
|
px={3}
|
||||||
|
py={1}
|
||||||
|
fontSize="xs"
|
||||||
|
>
|
||||||
|
{tag}
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
</HStack>
|
||||||
|
)}
|
||||||
|
</VStack>
|
||||||
|
{!isLast && <Divider my={4} borderColor="whiteAlpha.100" />}
|
||||||
|
</Box>
|
||||||
|
));
|
||||||
|
|
||||||
|
ModelBlock.displayName = 'ModelBlock';
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 区域标题组件
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
import { HStack, Icon, Text } from '@chakra-ui/react';
|
||||||
|
import type { IconType } from 'react-icons';
|
||||||
|
import { THEME } from '../theme';
|
||||||
|
|
||||||
|
interface SectionHeaderProps {
|
||||||
|
icon: IconType;
|
||||||
|
title: string;
|
||||||
|
color?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SectionHeader = memo<SectionHeaderProps>(
|
||||||
|
({ icon, title, color = THEME.dark.titleColor }) => (
|
||||||
|
<HStack spacing={2} mb={4}>
|
||||||
|
<Icon as={icon} color={color} boxSize={4} />
|
||||||
|
<Text fontWeight="bold" color={color} fontSize="md">
|
||||||
|
{title}
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
SectionHeader.displayName = 'SectionHeader';
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* CorePositioningCard 原子组件统一导出
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { SectionHeader } from './SectionHeader';
|
||||||
|
export { HighlightCard } from './HighlightCard';
|
||||||
|
export { ModelBlock } from './ModelBlock';
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
/**
|
||||||
|
* 核心定位卡片
|
||||||
|
*
|
||||||
|
* 显示公司的核心定位、投资亮点和商业模式
|
||||||
|
* 黑金主题设计
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { memo, useMemo } from 'react';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardBody,
|
||||||
|
VStack,
|
||||||
|
Text,
|
||||||
|
Box,
|
||||||
|
Grid,
|
||||||
|
GridItem,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { FaCrown, FaStar, FaBriefcase } from 'react-icons/fa';
|
||||||
|
import type {
|
||||||
|
QualitativeAnalysis,
|
||||||
|
InvestmentHighlightItem,
|
||||||
|
} from '../../types';
|
||||||
|
import {
|
||||||
|
THEME,
|
||||||
|
CARD_STYLES,
|
||||||
|
GRID_COLUMNS,
|
||||||
|
BORDER_RIGHT_RESPONSIVE,
|
||||||
|
} from './theme';
|
||||||
|
import { SectionHeader, HighlightCard, ModelBlock } from './atoms';
|
||||||
|
|
||||||
|
// ==================== 主组件 ====================
|
||||||
|
|
||||||
|
interface CorePositioningCardProps {
|
||||||
|
qualitativeAnalysis: QualitativeAnalysis;
|
||||||
|
cardBg?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CorePositioningCard: React.FC<CorePositioningCardProps> = memo(
|
||||||
|
({ qualitativeAnalysis }) => {
|
||||||
|
const corePositioning = qualitativeAnalysis.core_positioning;
|
||||||
|
|
||||||
|
// 判断是否有结构化数据
|
||||||
|
const hasStructuredData = useMemo(
|
||||||
|
() =>
|
||||||
|
!!(
|
||||||
|
corePositioning?.features?.length ||
|
||||||
|
(Array.isArray(corePositioning?.investment_highlights) &&
|
||||||
|
corePositioning.investment_highlights.length > 0) ||
|
||||||
|
corePositioning?.business_model_sections?.length
|
||||||
|
),
|
||||||
|
[corePositioning]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 如果没有结构化数据,使用旧的文本格式渲染
|
||||||
|
if (!hasStructuredData) {
|
||||||
|
return (
|
||||||
|
<Card {...CARD_STYLES}>
|
||||||
|
<CardBody>
|
||||||
|
<VStack spacing={4} align="stretch">
|
||||||
|
<SectionHeader icon={FaCrown} title="核心定位" />
|
||||||
|
{corePositioning?.one_line_intro && (
|
||||||
|
<Box
|
||||||
|
p={4}
|
||||||
|
bg={THEME.dark.cardBg}
|
||||||
|
borderRadius="lg"
|
||||||
|
borderLeft="4px solid"
|
||||||
|
borderColor={THEME.dark.border}
|
||||||
|
>
|
||||||
|
<Text color={THEME.dark.textColor} fontWeight="medium">
|
||||||
|
{corePositioning.one_line_intro}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
<Grid templateColumns={GRID_COLUMNS.twoColumnMd} gap={4}>
|
||||||
|
<GridItem>
|
||||||
|
<Box p={4} bg={THEME.light.cardBg} borderRadius="lg">
|
||||||
|
<SectionHeader icon={FaStar} title="投资亮点" />
|
||||||
|
<Text
|
||||||
|
fontSize="sm"
|
||||||
|
color={THEME.light.subtextColor}
|
||||||
|
whiteSpace="pre-wrap"
|
||||||
|
>
|
||||||
|
{corePositioning?.investment_highlights_text ||
|
||||||
|
(typeof corePositioning?.investment_highlights === 'string'
|
||||||
|
? corePositioning.investment_highlights
|
||||||
|
: '暂无数据')}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</GridItem>
|
||||||
|
<GridItem>
|
||||||
|
<Box p={4} bg={THEME.light.cardBg} borderRadius="lg">
|
||||||
|
<SectionHeader icon={FaBriefcase} title="商业模式" />
|
||||||
|
<Text
|
||||||
|
fontSize="sm"
|
||||||
|
color={THEME.light.subtextColor}
|
||||||
|
whiteSpace="pre-wrap"
|
||||||
|
>
|
||||||
|
{corePositioning?.business_model_desc || '暂无数据'}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</GridItem>
|
||||||
|
</Grid>
|
||||||
|
</VStack>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 结构化数据渲染 - 缓存数组计算
|
||||||
|
const highlights = useMemo(
|
||||||
|
() =>
|
||||||
|
(Array.isArray(corePositioning?.investment_highlights)
|
||||||
|
? corePositioning.investment_highlights
|
||||||
|
: []) as InvestmentHighlightItem[],
|
||||||
|
[corePositioning?.investment_highlights]
|
||||||
|
);
|
||||||
|
|
||||||
|
const businessSections = useMemo(
|
||||||
|
() => corePositioning?.business_model_sections || [],
|
||||||
|
[corePositioning?.business_model_sections]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card {...CARD_STYLES}>
|
||||||
|
<CardBody p={0}>
|
||||||
|
<VStack spacing={0} align="stretch">
|
||||||
|
{/* 核心定位区域(深色背景) */}
|
||||||
|
<Box p={6} bg={THEME.dark.bg}>
|
||||||
|
<SectionHeader icon={FaCrown} title="核心定位" />
|
||||||
|
|
||||||
|
{/* 一句话介绍 */}
|
||||||
|
{corePositioning?.one_line_intro && (
|
||||||
|
<Box
|
||||||
|
p={4}
|
||||||
|
bg={THEME.dark.cardBg}
|
||||||
|
borderRadius="lg"
|
||||||
|
borderLeft="4px solid"
|
||||||
|
borderColor={THEME.dark.border}
|
||||||
|
>
|
||||||
|
<Text color={THEME.dark.textColor} fontWeight="medium">
|
||||||
|
{corePositioning.one_line_intro}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* 投资亮点 + 商业模式区域 */}
|
||||||
|
<Grid templateColumns={GRID_COLUMNS.twoColumn} bg={THEME.light.bg}>
|
||||||
|
{/* 投资亮点区域 */}
|
||||||
|
<GridItem
|
||||||
|
p={6}
|
||||||
|
borderRight={BORDER_RIGHT_RESPONSIVE}
|
||||||
|
borderColor="whiteAlpha.100"
|
||||||
|
>
|
||||||
|
<SectionHeader icon={FaStar} title="投资亮点" />
|
||||||
|
<VStack spacing={3} align="stretch">
|
||||||
|
{highlights.length > 0 ? (
|
||||||
|
highlights.map((highlight, idx) => (
|
||||||
|
<HighlightCard key={idx} highlight={highlight} />
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Text fontSize="sm" color={THEME.light.subtextColor}>
|
||||||
|
暂无数据
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</VStack>
|
||||||
|
</GridItem>
|
||||||
|
|
||||||
|
{/* 商业模式区域 */}
|
||||||
|
<GridItem p={6}>
|
||||||
|
<SectionHeader icon={FaBriefcase} title="商业模式" />
|
||||||
|
<Box
|
||||||
|
p={4}
|
||||||
|
bg={THEME.light.cardBg}
|
||||||
|
borderRadius="lg"
|
||||||
|
border="1px solid"
|
||||||
|
borderColor="whiteAlpha.100"
|
||||||
|
>
|
||||||
|
{businessSections.length > 0 ? (
|
||||||
|
businessSections.map((section, idx) => (
|
||||||
|
<ModelBlock
|
||||||
|
key={idx}
|
||||||
|
section={section}
|
||||||
|
isLast={idx === businessSections.length - 1}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Text fontSize="sm" color={THEME.light.subtextColor}>
|
||||||
|
暂无数据
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</GridItem>
|
||||||
|
</Grid>
|
||||||
|
</VStack>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
CorePositioningCard.displayName = 'CorePositioningCard';
|
||||||
|
|
||||||
|
export default CorePositioningCard;
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* CorePositioningCard 主题和样式常量
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
FaUniversity,
|
||||||
|
FaFire,
|
||||||
|
FaUsers,
|
||||||
|
FaChartLine,
|
||||||
|
FaMicrochip,
|
||||||
|
FaShieldAlt,
|
||||||
|
} from 'react-icons/fa';
|
||||||
|
import type { IconType } from 'react-icons';
|
||||||
|
|
||||||
|
// ==================== 主题常量 ====================
|
||||||
|
|
||||||
|
export const THEME = {
|
||||||
|
// 深色背景区域(核心定位)
|
||||||
|
dark: {
|
||||||
|
bg: '#1A202C',
|
||||||
|
cardBg: '#252D3A',
|
||||||
|
border: '#C9A961',
|
||||||
|
borderGradient: 'linear-gradient(90deg, #C9A961, #8B7355)',
|
||||||
|
titleColor: '#C9A961',
|
||||||
|
textColor: '#E2E8F0',
|
||||||
|
subtextColor: '#A0AEC0',
|
||||||
|
},
|
||||||
|
// 浅色背景区域(投资亮点/商业模式)
|
||||||
|
light: {
|
||||||
|
bg: '#1E2530',
|
||||||
|
cardBg: '#252D3A',
|
||||||
|
titleColor: '#C9A961',
|
||||||
|
textColor: '#E2E8F0',
|
||||||
|
subtextColor: '#A0AEC0',
|
||||||
|
tagBg: 'rgba(201, 169, 97, 0.15)',
|
||||||
|
tagColor: '#C9A961',
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// ==================== 图标映射 ====================
|
||||||
|
|
||||||
|
export const ICON_MAP: Record<string, IconType> = {
|
||||||
|
bank: FaUniversity,
|
||||||
|
fire: FaFire,
|
||||||
|
users: FaUsers,
|
||||||
|
'trending-up': FaChartLine,
|
||||||
|
cpu: FaMicrochip,
|
||||||
|
'shield-check': FaShieldAlt,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 样式常量 ====================
|
||||||
|
|
||||||
|
// 卡片通用样式(含顶部金色边框)
|
||||||
|
export const CARD_STYLES = {
|
||||||
|
bg: THEME.dark.bg,
|
||||||
|
shadow: 'lg',
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: 'whiteAlpha.100',
|
||||||
|
overflow: 'hidden',
|
||||||
|
position: 'relative',
|
||||||
|
_before: {
|
||||||
|
content: '""',
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
height: '3px',
|
||||||
|
background: THEME.dark.borderGradient,
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// HighlightCard hover 样式
|
||||||
|
export const HIGHLIGHT_HOVER_STYLES = {
|
||||||
|
_hover: { borderColor: 'whiteAlpha.200' },
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 响应式布局常量
|
||||||
|
export const GRID_COLUMNS = {
|
||||||
|
twoColumn: { base: '1fr', lg: 'repeat(2, 1fr)' },
|
||||||
|
twoColumnMd: { base: '1fr', md: 'repeat(2, 1fr)' },
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const BORDER_RIGHT_RESPONSIVE = { lg: '1px solid' } as const;
|
||||||
Reference in New Issue
Block a user