feat(DeepAnalysis): 竞争地位分析增加行业排名弹窗

- CompetitiveAnalysisCard 新增 Modal 弹窗展示行业排名详情
  - 点击 Badge 或查看详情按钮可打开弹窗
  - 弹窗采用黑金主题样式
  - StrategyTab 移除独立的 IndustryRankingView 展示
This commit is contained in:
zdl
2025-12-16 16:33:45 +08:00
parent 6a4c475d3a
commit 2eb2a22495
2 changed files with 128 additions and 59 deletions

View File

@@ -2,6 +2,7 @@
* 竞争地位分析卡片 * 竞争地位分析卡片
* *
* 显示竞争力评分、雷达图和竞争分析 * 显示竞争力评分、雷达图和竞争分析
* 包含行业排名弹窗功能
*/ */
import React, { memo, useMemo } from 'react'; import React, { memo, useMemo } from 'react';
@@ -22,6 +23,14 @@ import {
Icon, Icon,
Divider, Divider,
SimpleGrid, SimpleGrid,
Button,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalBody,
ModalCloseButton,
useDisclosure,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { import {
FaTrophy, FaTrophy,
@@ -33,11 +42,32 @@ import {
FaShieldAlt, FaShieldAlt,
FaRocket, FaRocket,
FaUsers, FaUsers,
FaExternalLinkAlt,
} from 'react-icons/fa'; } from 'react-icons/fa';
import ReactECharts from 'echarts-for-react'; import ReactECharts from 'echarts-for-react';
import { ScoreBar } from '../atoms'; import { ScoreBar } from '../atoms';
import { getRadarChartOption } from '../utils/chartOptions'; import { getRadarChartOption } from '../utils/chartOptions';
import type { ComprehensiveData, CompetitivePosition } from '../types'; import { IndustryRankingView } from '../../../FinancialPanorama/components';
import type { ComprehensiveData, CompetitivePosition, IndustryRankData } from '../types';
// 黑金主题弹窗样式
const MODAL_STYLES = {
content: {
bg: 'gray.900',
borderColor: 'rgba(212, 175, 55, 0.3)',
borderWidth: '1px',
maxW: '900px',
},
header: {
color: 'yellow.500',
borderBottomColor: 'rgba(212, 175, 55, 0.2)',
borderBottomWidth: '1px',
},
closeButton: {
color: 'yellow.500',
_hover: { bg: 'rgba(212, 175, 55, 0.1)' },
},
} as const;
// 样式常量 - 避免每次渲染创建新对象 // 样式常量 - 避免每次渲染创建新对象
const CARD_STYLES = { const CARD_STYLES = {
@@ -57,6 +87,7 @@ const CHART_STYLE = { height: '320px' } as const;
interface CompetitiveAnalysisCardProps { interface CompetitiveAnalysisCardProps {
comprehensiveData: ComprehensiveData; comprehensiveData: ComprehensiveData;
industryRankData?: IndustryRankData[];
} }
// 竞争对手标签组件 // 竞争对手标签组件
@@ -141,8 +172,10 @@ const AdvantagesSection = memo<AdvantagesSectionProps>(
AdvantagesSection.displayName = 'AdvantagesSection'; AdvantagesSection.displayName = 'AdvantagesSection';
const CompetitiveAnalysisCard: React.FC<CompetitiveAnalysisCardProps> = memo( const CompetitiveAnalysisCard: React.FC<CompetitiveAnalysisCardProps> = memo(
({ comprehensiveData }) => { ({ comprehensiveData, industryRankData }) => {
const competitivePosition = comprehensiveData.competitive_position; const competitivePosition = comprehensiveData.competitive_position;
const { isOpen, onOpen, onClose } = useDisclosure();
if (!competitivePosition) return null; if (!competitivePosition) return null;
// 缓存雷达图配置 // 缓存雷达图配置
@@ -160,56 +193,99 @@ const CompetitiveAnalysisCard: React.FC<CompetitiveAnalysisCardProps> = memo(
[competitivePosition.analysis?.main_competitors] [competitivePosition.analysis?.main_competitors]
); );
// 判断是否有行业排名数据可展示
const hasIndustryRankData = industryRankData && industryRankData.length > 0;
return ( return (
<Card {...CARD_STYLES}> <>
<CardHeader> <Card {...CARD_STYLES}>
<HStack> <CardHeader>
<Icon as={FaTrophy} color="yellow.500" /> <HStack>
<Heading size="sm" color="yellow.500"></Heading> <Icon as={FaTrophy} color="yellow.500" />
{competitivePosition.ranking && ( <Heading size="sm" color="yellow.500"></Heading>
<Badge {competitivePosition.ranking && (
ml={2} <Badge
bg="transparent" ml={2}
border="1px solid" bg="transparent"
borderColor="yellow.600" border="1px solid"
color="yellow.500" borderColor="yellow.600"
> color="yellow.500"
{competitivePosition.ranking.industry_rank}/ cursor={hasIndustryRankData ? 'pointer' : 'default'}
{competitivePosition.ranking.total_companies} onClick={hasIndustryRankData ? onOpen : undefined}
</Badge> _hover={hasIndustryRankData ? { bg: 'rgba(212, 175, 55, 0.1)' } : undefined}
)} >
</HStack> {competitivePosition.ranking.industry_rank}/
</CardHeader> {competitivePosition.ranking.total_companies}
<CardBody> </Badge>
{/* 主要竞争对手 */} )}
{/* {competitors.length > 0 && <CompetitorTags competitors={competitors} />} */} {hasIndustryRankData && (
<Button
size="xs"
variant="ghost"
color="yellow.500"
rightIcon={<Icon as={FaExternalLinkAlt} boxSize={3} />}
onClick={onOpen}
_hover={{ bg: 'rgba(212, 175, 55, 0.1)' }}
>
</Button>
)}
</HStack>
</CardHeader>
<CardBody>
{/* 主要竞争对手 */}
{/* {competitors.length > 0 && <CompetitorTags competitors={competitors} />} */}
{/* 评分和雷达图 */} {/* 评分和雷达图 */}
{/* <Grid templateColumns="repeat(2, 1fr)" gap={6}> {/* <Grid templateColumns="repeat(2, 1fr)" gap={6}>
<GridItem colSpan={GRID_COLSPAN}> <GridItem colSpan={GRID_COLSPAN}>
<ScoreSection scores={competitivePosition.scores} /> <ScoreSection scores={competitivePosition.scores} />
</GridItem> </GridItem>
<GridItem colSpan={GRID_COLSPAN}> <GridItem colSpan={GRID_COLSPAN}>
{radarOption && ( {radarOption && (
<ReactECharts <ReactECharts
option={radarOption} option={radarOption}
style={CHART_STYLE} style={CHART_STYLE}
theme="dark" theme="dark"
/>
)}
</GridItem>
</Grid> */}
{/* <Divider my={4} borderColor="yellow.600" /> */}
{/* 竞争优势和劣势 */}
<AdvantagesSection
advantages={competitivePosition.analysis?.competitive_advantages}
disadvantages={competitivePosition.analysis?.competitive_disadvantages}
/>
</CardBody>
</Card>
{/* 行业排名弹窗 - 黑金主题 */}
<Modal isOpen={isOpen} onClose={onClose} size="4xl" scrollBehavior="inside">
<ModalOverlay bg="blackAlpha.700" />
<ModalContent {...MODAL_STYLES.content}>
<ModalHeader {...MODAL_STYLES.header}>
<HStack>
<Icon as={FaTrophy} color="yellow.500" />
<Text></Text>
</HStack>
</ModalHeader>
<ModalCloseButton {...MODAL_STYLES.closeButton} />
<ModalBody py={4}>
{hasIndustryRankData && (
<IndustryRankingView
industryRank={industryRankData}
bgColor="gray.800"
borderColor="rgba(212, 175, 55, 0.3)"
/> />
)} )}
</GridItem> </ModalBody>
</Grid> */} </ModalContent>
</Modal>
{/* <Divider my={4} borderColor="yellow.600" /> */} </>
{/* 竞争优势和劣势 */}
<AdvantagesSection
advantages={competitivePosition.analysis?.competitive_advantages}
disadvantages={competitivePosition.analysis?.competitive_disadvantages}
/>
</CardBody>
</Card>
); );
} }
); );

View File

@@ -1,7 +1,7 @@
/** /**
* 战略分析 Tab * 战略分析 Tab
* *
* 包含:核心定位 + 战略分析 + 竞争地位分析 + 行业排名 * 包含:核心定位 + 战略分析 + 竞争地位分析(含行业排名弹窗)
*/ */
import React, { memo } from 'react'; import React, { memo } from 'react';
@@ -11,7 +11,6 @@ import {
StrategyAnalysisCard, StrategyAnalysisCard,
CompetitiveAnalysisCard, CompetitiveAnalysisCard,
} from '../components'; } from '../components';
import { IndustryRankingView } from '../../../FinancialPanorama/components';
import type { ComprehensiveData, IndustryRankData } from '../types'; import type { ComprehensiveData, IndustryRankData } from '../types';
export interface StrategyTabProps { export interface StrategyTabProps {
@@ -43,17 +42,11 @@ const StrategyTab: React.FC<StrategyTabProps> = memo(({
/> />
)} )}
{/* 竞争地位分析 */} {/* 竞争地位分析(包含行业排名弹窗) */}
{comprehensiveData?.competitive_position && ( {comprehensiveData?.competitive_position && (
<CompetitiveAnalysisCard comprehensiveData={comprehensiveData} /> <CompetitiveAnalysisCard
)} comprehensiveData={comprehensiveData}
industryRankData={industryRankData}
{/* 行业排名 */}
{industryRankData && industryRankData.length > 0 && (
<IndustryRankingView
industryRank={industryRankData}
bgColor="white"
borderColor="gray.200"
/> />
)} )}
</TabPanelContainer> </TabPanelContainer>