diff --git a/src/views/Company/components/CompanyOverview/DeepAnalysisTab/components/CompetitiveAnalysisCard.tsx b/src/views/Company/components/CompanyOverview/DeepAnalysisTab/components/CompetitiveAnalysisCard.tsx index 480c03e4..d82307ed 100644 --- a/src/views/Company/components/CompanyOverview/DeepAnalysisTab/components/CompetitiveAnalysisCard.tsx +++ b/src/views/Company/components/CompanyOverview/DeepAnalysisTab/components/CompetitiveAnalysisCard.tsx @@ -4,7 +4,7 @@ * 显示竞争力评分、雷达图和竞争分析 */ -import React from 'react'; +import React, { memo, useMemo } from 'react'; import { Card, CardBody, @@ -35,148 +35,187 @@ import { FaUsers, } from 'react-icons/fa'; import ReactECharts from 'echarts-for-react'; -import { DisclaimerBox, ScoreBar } from '../atoms'; +import { ScoreBar } from '../atoms'; import { getRadarChartOption } from '../utils/chartOptions'; -import type { ComprehensiveData } from '../types'; +import type { ComprehensiveData, CompetitivePosition } from '../types'; + +// 样式常量 - 避免每次渲染创建新对象 +const CARD_STYLES = { + bg: 'transparent', + border: '1px solid', + borderColor: 'yellow.600', + shadow: 'md', +} as const; + +const CONTENT_BOX_STYLES = { + p: 4, + border: '1px solid', + borderColor: 'yellow.600', + borderRadius: 'md', +} as const; + +const GRID_COLSPAN = { base: 2, lg: 1 } as const; +const CHART_STYLE = { height: '320px' } as const; interface CompetitiveAnalysisCardProps { comprehensiveData: ComprehensiveData; - cardBg?: string; } -const CompetitiveAnalysisCard: React.FC = ({ - comprehensiveData, - cardBg, -}) => { - const competitivePosition = comprehensiveData.competitive_position; - if (!competitivePosition) return null; +// 竞争对手标签组件 +interface CompetitorTagsProps { + competitors: string[]; +} - const radarOption = getRadarChartOption(comprehensiveData); +const CompetitorTags = memo(({ competitors }) => ( + + + 主要竞争对手 + + + {competitors.map((competitor, idx) => ( + + + {competitor} + + ))} + + +)); - return ( - - - - - 竞争地位分析 - {competitivePosition.ranking && ( - - 行业排名 {competitivePosition.ranking.industry_rank}/ - {competitivePosition.ranking.total_companies} - - )} - - - - +CompetitorTags.displayName = 'CompetitorTags'; - {/* 主要竞争对手 */} - {competitivePosition.analysis?.main_competitors && ( - - - 主要竞争对手 - - - {competitivePosition.analysis.main_competitors - .split(',') - .map((competitor, idx) => ( - - - {competitor.trim()} - - ))} - - - )} +// 评分区域组件 +interface ScoreSectionProps { + scores: CompetitivePosition['scores']; +} - {/* 评分和雷达图 */} - - - - - - - - - - - - - +const ScoreSection = memo(({ scores }) => ( + + + + + + + + + + +)); - - {radarOption && ( - +ScoreSection.displayName = 'ScoreSection'; + +// 竞争优劣势组件 +interface AdvantagesSectionProps { + advantages?: string; + disadvantages?: string; +} + +const AdvantagesSection = memo( + ({ advantages, disadvantages }) => ( + + + + 竞争优势 + + + {advantages || '暂无数据'} + + + + + 竞争劣势 + + + {disadvantages || '暂无数据'} + + + + ) +); + +AdvantagesSection.displayName = 'AdvantagesSection'; + +const CompetitiveAnalysisCard: React.FC = memo( + ({ comprehensiveData }) => { + const competitivePosition = comprehensiveData.competitive_position; + if (!competitivePosition) return null; + + // 缓存雷达图配置 + const radarOption = useMemo( + () => getRadarChartOption(comprehensiveData), + [comprehensiveData] + ); + + // 缓存竞争对手列表 + const competitors = useMemo( + () => + competitivePosition.analysis?.main_competitors + ?.split(',') + .map((c) => c.trim()) || [], + [competitivePosition.analysis?.main_competitors] + ); + + return ( + + + + + 竞争地位分析 + {competitivePosition.ranking && ( + + 行业排名 {competitivePosition.ranking.industry_rank}/ + {competitivePosition.ranking.total_companies} + )} - - + + + + {/* 主要竞争对手 */} + {competitors.length > 0 && } - + {/* 评分和雷达图 */} + + + + - {/* 竞争优势和劣势 */} - - - - 竞争优势 - - - {competitivePosition.analysis?.competitive_advantages || '暂无数据'} - - - - - 竞争劣势 - - - {competitivePosition.analysis?.competitive_disadvantages || '暂无数据'} - - - - - - ); -}; + + {radarOption && ( + + )} + + + + + + {/* 竞争优势和劣势 */} + + + + ); + } +); + +CompetitiveAnalysisCard.displayName = 'CompetitiveAnalysisCard'; export default CompetitiveAnalysisCard;