feat: 传导练UI调整

This commit is contained in:
zdl
2025-11-28 07:14:52 +08:00
parent 34338373cd
commit cf4fdf6a68

View File

@@ -28,9 +28,11 @@ import {
ModalCloseButton,
Icon,
useColorModeValue,
Tooltip
Tooltip,
Center
} from '@chakra-ui/react';
import { InfoIcon, ViewIcon } from '@chakra-ui/icons';
import { Share2, GitBranch, Inbox } from 'lucide-react';
import ReactECharts from 'echarts-for-react';
import { eventService } from '../../../services/eventService';
import CitedContent from '../../../components/Citation/CitedContent';
@@ -637,7 +639,7 @@ const TransmissionChainAnalysis = ({ eventId }) => {
};
return (
<Box p={6}>
<Box>
{/* 统计信息条 */}
<Box
mb={4}
@@ -647,56 +649,57 @@ const TransmissionChainAnalysis = ({ eventId }) => {
borderColor={PROFESSIONAL_COLORS.border.default}
bg={PROFESSIONAL_COLORS.background.secondary}
>
<HStack spacing={6} wrap="wrap">
<Stat>
<StatLabel color={PROFESSIONAL_COLORS.text.secondary}>总节点数</StatLabel>
<StatNumber color={PROFESSIONAL_COLORS.text.primary}>{stats.totalNodes}</StatNumber>
<Flex wrap="wrap" gap={{ base: 3, md: 6 }}>
<Stat minW="fit-content">
<StatLabel fontSize={{ base: "xs", md: "sm" }} color={PROFESSIONAL_COLORS.text.secondary} whiteSpace="nowrap">总节点数</StatLabel>
<StatNumber fontSize={{ base: "xl", md: "2xl" }} color={PROFESSIONAL_COLORS.text.primary}>{stats.totalNodes}</StatNumber>
</Stat>
<Stat>
<StatLabel color={PROFESSIONAL_COLORS.text.secondary}>涉及行业</StatLabel>
<StatNumber color={PROFESSIONAL_COLORS.text.primary}>{stats.involvedIndustries}</StatNumber>
<Stat minW="fit-content">
<StatLabel fontSize={{ base: "xs", md: "sm" }} color={PROFESSIONAL_COLORS.text.secondary} whiteSpace="nowrap">涉及行业</StatLabel>
<StatNumber fontSize={{ base: "xl", md: "2xl" }} color={PROFESSIONAL_COLORS.text.primary}>{stats.involvedIndustries}</StatNumber>
</Stat>
<Stat>
<StatLabel color={PROFESSIONAL_COLORS.text.secondary}>相关公司</StatLabel>
<StatNumber color={PROFESSIONAL_COLORS.text.primary}>{stats.relatedCompanies}</StatNumber>
<Stat minW="fit-content">
<StatLabel fontSize={{ base: "xs", md: "sm" }} color={PROFESSIONAL_COLORS.text.secondary} whiteSpace="nowrap">相关公司</StatLabel>
<StatNumber fontSize={{ base: "xl", md: "2xl" }} color={PROFESSIONAL_COLORS.text.primary}>{stats.relatedCompanies}</StatNumber>
</Stat>
<Stat>
<StatLabel color={PROFESSIONAL_COLORS.text.secondary}>正向影响</StatLabel>
<StatNumber color="#10B981">{stats.positiveImpact}</StatNumber>
<Stat minW="fit-content">
<StatLabel fontSize={{ base: "xs", md: "sm" }} color={PROFESSIONAL_COLORS.text.secondary} whiteSpace="nowrap">正向影响</StatLabel>
<StatNumber fontSize={{ base: "xl", md: "2xl" }} color="#10B981">{stats.positiveImpact}</StatNumber>
</Stat>
<Stat>
<StatLabel color={PROFESSIONAL_COLORS.text.secondary}>负向影响</StatLabel>
<StatNumber color="#EF4444">{stats.negativeImpact}</StatNumber>
<Stat minW="fit-content">
<StatLabel fontSize={{ base: "xs", md: "sm" }} color={PROFESSIONAL_COLORS.text.secondary} whiteSpace="nowrap">负向影响</StatLabel>
<StatNumber fontSize={{ base: "xl", md: "2xl" }} color="#EF4444">{stats.negativeImpact}</StatNumber>
</Stat>
<Stat>
<StatLabel color={PROFESSIONAL_COLORS.text.secondary}>循环效应</StatLabel>
<StatNumber color="#A855F7">{stats.circularEffect}</StatNumber>
<Stat minW="fit-content">
<StatLabel fontSize={{ base: "xs", md: "sm" }} color={PROFESSIONAL_COLORS.text.secondary} whiteSpace="nowrap">循环效应</StatLabel>
<StatNumber fontSize={{ base: "xl", md: "2xl" }} color="#A855F7">{stats.circularEffect}</StatNumber>
</Stat>
</HStack>
</Flex>
</Box>
{/* 自定义图例 */}
<Box mb={4}>
<HStack spacing={4} wrap="wrap">
{Object.entries(NODE_STYLES).map(([type, style]) => (
<Tag
key={type}
size="md"
bg={PROFESSIONAL_COLORS.background.secondary}
color={PROFESSIONAL_COLORS.text.primary}
borderWidth="1px"
borderColor={PROFESSIONAL_COLORS.border.default}
>
<Box w={3} h={3} bg={style.color} borderRadius="sm" mr={2} />
{NODE_TYPE_LABELS[type] || type}
</Tag>
))}
</HStack>
</Box>
<Flex mb={4} wrap="wrap" gap={2}>
{Object.entries(NODE_STYLES).map(([type, style]) => (
<Tag
key={type}
size="sm"
px={2}
py={1}
bg={PROFESSIONAL_COLORS.background.secondary}
color={PROFESSIONAL_COLORS.text.primary}
borderWidth="1px"
borderColor={PROFESSIONAL_COLORS.border.default}
>
<Box w={2.5} h={2.5} bg={style.color} borderRadius="sm" mr={1.5} />
{NODE_TYPE_LABELS[type] || type}
</Tag>
))}
</Flex>
{/* 视图切换按钮 */}
<Flex mb={4} gap={2}>
<Button
leftIcon={<Icon as={Share2} boxSize={4} />}
bg={viewMode === 'graph' ? PROFESSIONAL_COLORS.gold[500] : PROFESSIONAL_COLORS.background.secondary}
color={viewMode === 'graph' ? 'black' : PROFESSIONAL_COLORS.text.primary}
_hover={{
@@ -710,6 +713,7 @@ const TransmissionChainAnalysis = ({ eventId }) => {
力导向图
</Button>
<Button
leftIcon={<Icon as={GitBranch} boxSize={4} />}
bg={viewMode === 'sankey' ? PROFESSIONAL_COLORS.gold[500] : PROFESSIONAL_COLORS.background.secondary}
color={viewMode === 'sankey' ? 'black' : PROFESSIONAL_COLORS.text.primary}
_hover={{
@@ -722,7 +726,6 @@ const TransmissionChainAnalysis = ({ eventId }) => {
>
桑基图
</Button>
</Flex>
{loading && (
@@ -748,75 +751,97 @@ const TransmissionChainAnalysis = ({ eventId }) => {
{!loading && !error && (
<Box>
{/* 提示信息 */}
<Alert
status="info"
mb={4}
borderRadius="md"
bg="rgba(59, 130, 246, 0.1)"
color="#3B82F6"
borderWidth="1px"
borderColor="#3B82F6"
>
<AlertIcon />
<Text fontSize="sm" color={PROFESSIONAL_COLORS.text.secondary}>
<Icon as={ViewIcon} mr={2} />
点击图表中的节点可以查看详细信息
</Text>
</Alert>
{/* 图表容器 */}
{/* 图表容器 - 宽高比 2:1H5 自适应 */}
<Box
h={viewMode === 'sankey' ? "600px" : "700px"}
position="relative"
w="100%"
pb={{ base: "75%", md: "50%" }}
border="1px solid"
borderColor={PROFESSIONAL_COLORS.border.default}
borderRadius="lg"
boxShadow="0 4px 12px rgba(0, 0, 0, 0.3)"
bg={PROFESSIONAL_COLORS.background.card}
p={4}
ref={containerRef}
>
<Box
position="absolute"
top={0}
left={0}
right={0}
bottom={0}
p={4}
>
{/* 提示信息 - 固定在左上角 */}
<Text
position="absolute"
top={2}
left={3}
fontSize="xs"
color={PROFESSIONAL_COLORS.text.muted}
zIndex={1}
bg="rgba(0, 0, 0, 0.5)"
px={2}
py={1}
borderRadius="md"
>
<Icon as={ViewIcon} mr={1} boxSize={3} />
点击节点查看详情
</Text>
{chartReady && (
<>
{viewMode === 'graph' ? (
<ReactECharts
option={graphData ? getGraphOption(graphData) : {}}
style={{ height: '100%', width: '100%' }}
onEvents={{
click: handleGraphNodeClick
}}
opts={{
renderer: 'canvas',
devicePixelRatio: window.devicePixelRatio || 1
}}
lazyUpdate={true}
notMerge={false}
shouldSetOption={(prevProps, props) => {
// 减少不必要的重新渲染
return JSON.stringify(prevProps.option) !== JSON.stringify(props.option);
}}
/>
{/* 空状态提示 */}
{(viewMode === 'graph' && (!graphData || !graphData.nodes || graphData.nodes.length === 0)) ||
(viewMode === 'sankey' && (!sankeyData || !sankeyData.nodes || sankeyData.nodes.length === 0)) ? (
<Center h="100%" flexDirection="column">
<Icon as={Inbox} boxSize={12} color={PROFESSIONAL_COLORS.text.muted} />
<Text mt={4} color={PROFESSIONAL_COLORS.text.muted} fontSize="sm">
暂无传导链数据
</Text>
</Center>
) : (
<ReactECharts
option={sankeyData ? getSankeyOption(sankeyData) : {}}
style={{ height: '100%', width: '100%' }}
onEvents={{
click: handleSankeyNodeClick
}}
opts={{
renderer: 'canvas',
devicePixelRatio: window.devicePixelRatio || 1
}}
lazyUpdate={true}
notMerge={false}
shouldSetOption={(prevProps, props) => {
// 减少不必要的重新渲染
return JSON.stringify(prevProps.option) !== JSON.stringify(props.option);
}}
/>
<>
{viewMode === 'graph' ? (
<ReactECharts
option={graphData ? getGraphOption(graphData) : {}}
style={{ height: '100%', width: '100%' }}
onEvents={{
click: handleGraphNodeClick
}}
opts={{
renderer: 'canvas',
devicePixelRatio: window.devicePixelRatio || 1
}}
lazyUpdate={true}
notMerge={false}
shouldSetOption={(prevProps, props) => {
// 减少不必要的重新渲染
return JSON.stringify(prevProps.option) !== JSON.stringify(props.option);
}}
/>
) : (
<ReactECharts
option={sankeyData ? getSankeyOption(sankeyData) : {}}
style={{ height: '100%', width: '100%' }}
onEvents={{
click: handleSankeyNodeClick
}}
opts={{
renderer: 'canvas',
devicePixelRatio: window.devicePixelRatio || 1
}}
lazyUpdate={true}
notMerge={false}
shouldSetOption={(prevProps, props) => {
// 减少不必要的重新渲染
return JSON.stringify(prevProps.option) !== JSON.stringify(props.option);
}}
/>
)}
</>
)}
</>
)}
</Box>
</Box>
</Box>
)}