update ui

This commit is contained in:
2025-11-14 08:03:33 +08:00
parent 8c93606769
commit 98d063bcfe

View File

@@ -35,6 +35,7 @@ import ReactECharts from 'echarts-for-react';
import { eventService } from '../../../services/eventService'; import { eventService } from '../../../services/eventService';
import CitedContent from '../../../components/Citation/CitedContent'; import CitedContent from '../../../components/Citation/CitedContent';
import { logger } from '../../../utils/logger'; import { logger } from '../../../utils/logger';
import { PROFESSIONAL_COLORS } from '../../../constants/professionalTheme';
// 节点样式配置 - 完全复刻Flask版本 // 节点样式配置 - 完全复刻Flask版本
const NODE_STYLES = { const NODE_STYLES = {
@@ -402,8 +403,8 @@ const TransmissionChainAnalysis = ({ eventId }) => {
}); });
const containerRef = useRef(null); const containerRef = useRef(null);
const modalBgColor = useColorModeValue('white', 'gray.800'); const modalBgColor = PROFESSIONAL_COLORS.background.card;
const modalBorderColor = useColorModeValue('gray.200', 'gray.600'); const modalBorderColor = PROFESSIONAL_COLORS.border.default;
// 关闭弹窗并清空状态 // 关闭弹窗并清空状态
const handleCloseModal = () => { const handleCloseModal = () => {
@@ -638,31 +639,38 @@ const TransmissionChainAnalysis = ({ eventId }) => {
return ( return (
<Box p={6}> <Box p={6}>
{/* 统计信息条 */} {/* 统计信息条 */}
<Box mb={4}> <Box
mb={4}
p={4}
borderRadius="lg"
border="1px solid"
borderColor={PROFESSIONAL_COLORS.border.default}
bg={PROFESSIONAL_COLORS.background.secondary}
>
<HStack spacing={6} wrap="wrap"> <HStack spacing={6} wrap="wrap">
<Stat> <Stat>
<StatLabel>总节点数</StatLabel> <StatLabel color={PROFESSIONAL_COLORS.text.secondary}>总节点数</StatLabel>
<StatNumber>{stats.totalNodes}</StatNumber> <StatNumber color={PROFESSIONAL_COLORS.text.primary}>{stats.totalNodes}</StatNumber>
</Stat> </Stat>
<Stat> <Stat>
<StatLabel>涉及行业</StatLabel> <StatLabel color={PROFESSIONAL_COLORS.text.secondary}>涉及行业</StatLabel>
<StatNumber>{stats.involvedIndustries}</StatNumber> <StatNumber color={PROFESSIONAL_COLORS.text.primary}>{stats.involvedIndustries}</StatNumber>
</Stat> </Stat>
<Stat> <Stat>
<StatLabel>相关公司</StatLabel> <StatLabel color={PROFESSIONAL_COLORS.text.secondary}>相关公司</StatLabel>
<StatNumber>{stats.relatedCompanies}</StatNumber> <StatNumber color={PROFESSIONAL_COLORS.text.primary}>{stats.relatedCompanies}</StatNumber>
</Stat> </Stat>
<Stat> <Stat>
<StatLabel>正向影响</StatLabel> <StatLabel color={PROFESSIONAL_COLORS.text.secondary}>正向影响</StatLabel>
<StatNumber color="green.500">{stats.positiveImpact}</StatNumber> <StatNumber color="#10B981">{stats.positiveImpact}</StatNumber>
</Stat> </Stat>
<Stat> <Stat>
<StatLabel>负向影响</StatLabel> <StatLabel color={PROFESSIONAL_COLORS.text.secondary}>负向影响</StatLabel>
<StatNumber color="red.500">{stats.negativeImpact}</StatNumber> <StatNumber color="#EF4444">{stats.negativeImpact}</StatNumber>
</Stat> </Stat>
<Stat> <Stat>
<StatLabel>循环效应</StatLabel> <StatLabel color={PROFESSIONAL_COLORS.text.secondary}>循环效应</StatLabel>
<StatNumber color="purple.500">{stats.circularEffect}</StatNumber> <StatNumber color="#A855F7">{stats.circularEffect}</StatNumber>
</Stat> </Stat>
</HStack> </HStack>
</Box> </Box>
@@ -671,7 +679,14 @@ const TransmissionChainAnalysis = ({ eventId }) => {
<Box mb={4}> <Box mb={4}>
<HStack spacing={4} wrap="wrap"> <HStack spacing={4} wrap="wrap">
{Object.entries(NODE_STYLES).map(([type, style]) => ( {Object.entries(NODE_STYLES).map(([type, style]) => (
<Tag key={type} size="md"> <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} /> <Box w={3} h={3} bg={style.color} borderRadius="sm" mr={2} />
{NODE_TYPE_LABELS[type] || type} {NODE_TYPE_LABELS[type] || type}
</Tag> </Tag>
@@ -682,16 +697,28 @@ const TransmissionChainAnalysis = ({ eventId }) => {
{/* 视图切换按钮 */} {/* 视图切换按钮 */}
<Flex mb={4} gap={2}> <Flex mb={4} gap={2}>
<Button <Button
colorScheme={viewMode === 'graph' ? 'blue' : 'gray'} bg={viewMode === 'graph' ? PROFESSIONAL_COLORS.gold[500] : PROFESSIONAL_COLORS.background.secondary}
color={viewMode === 'graph' ? 'black' : PROFESSIONAL_COLORS.text.primary}
_hover={{
bg: viewMode === 'graph' ? PROFESSIONAL_COLORS.gold[600] : PROFESSIONAL_COLORS.background.cardHover,
}}
onClick={() => setViewMode('graph')} onClick={() => setViewMode('graph')}
size="sm" size="sm"
borderWidth="1px"
borderColor={PROFESSIONAL_COLORS.border.default}
> >
力导向图 力导向图
</Button> </Button>
<Button <Button
colorScheme={viewMode === 'sankey' ? 'blue' : 'gray'} bg={viewMode === 'sankey' ? PROFESSIONAL_COLORS.gold[500] : PROFESSIONAL_COLORS.background.secondary}
color={viewMode === 'sankey' ? 'black' : PROFESSIONAL_COLORS.text.primary}
_hover={{
bg: viewMode === 'sankey' ? PROFESSIONAL_COLORS.gold[600] : PROFESSIONAL_COLORS.background.cardHover,
}}
onClick={() => setViewMode('sankey')} onClick={() => setViewMode('sankey')}
size="sm" size="sm"
borderWidth="1px"
borderColor={PROFESSIONAL_COLORS.border.default}
> >
桑基图 桑基图
</Button> </Button>
@@ -705,7 +732,15 @@ const TransmissionChainAnalysis = ({ eventId }) => {
)} )}
{error && ( {error && (
<Alert status="error" mb={4}> <Alert
status="error"
mb={4}
bg="rgba(239, 68, 68, 0.1)"
color="#EF4444"
borderWidth="1px"
borderColor="#EF4444"
borderRadius="md"
>
<AlertIcon /> <AlertIcon />
{error} {error}
</Alert> </Alert>
@@ -714,22 +749,30 @@ const TransmissionChainAnalysis = ({ eventId }) => {
{!loading && !error && ( {!loading && !error && (
<Box> <Box>
{/* 提示信息 */} {/* 提示信息 */}
<Alert status="info" mb={4} borderRadius="md"> <Alert
status="info"
mb={4}
borderRadius="md"
bg="rgba(59, 130, 246, 0.1)"
color="#3B82F6"
borderWidth="1px"
borderColor="#3B82F6"
>
<AlertIcon /> <AlertIcon />
<Text fontSize="sm"> <Text fontSize="sm" color={PROFESSIONAL_COLORS.text.secondary}>
<Icon as={ViewIcon} mr={2} /> <Icon as={ViewIcon} mr={2} />
点击图表中的节点可以查看详细信息 点击图表中的节点可以查看详细信息
</Text> </Text>
</Alert> </Alert>
{/* 图表容器 */} {/* 图表容器 */}
<Box <Box
h={viewMode === 'sankey' ? "600px" : "700px"} h={viewMode === 'sankey' ? "600px" : "700px"}
border="1px solid" border="1px solid"
borderColor="gray.300" borderColor={PROFESSIONAL_COLORS.border.default}
borderRadius="lg" borderRadius="lg"
boxShadow="md" boxShadow="0 4px 12px rgba(0, 0, 0, 0.3)"
bg="white" bg={PROFESSIONAL_COLORS.background.card}
p={4} p={4}
ref={containerRef} ref={containerRef}
> >
@@ -785,9 +828,16 @@ const TransmissionChainAnalysis = ({ eventId }) => {
<ModalContent maxH="80vh" bg={modalBgColor}> <ModalContent maxH="80vh" bg={modalBgColor}>
<ModalHeader borderBottom="1px solid" borderColor={modalBorderColor}> <ModalHeader borderBottom="1px solid" borderColor={modalBorderColor}>
<HStack justify="space-between"> <HStack justify="space-between">
<Text>{selectedNode ? '节点详情' : '传导链分析'}</Text> <Text color={PROFESSIONAL_COLORS.text.primary}>{selectedNode ? '节点详情' : '传导链分析'}</Text>
{selectedNode && ( {selectedNode && (
<Badge colorScheme="blue">{NODE_TYPE_LABELS[selectedNode.extra?.node_type] || selectedNode.extra?.node_type}</Badge> <Badge
bg="rgba(59, 130, 246, 0.15)"
color="#3B82F6"
borderWidth="1px"
borderColor="#3B82F6"
>
{NODE_TYPE_LABELS[selectedNode.extra?.node_type] || selectedNode.extra?.node_type}
</Badge>
)} )}
</HStack> </HStack>
</ModalHeader> </ModalHeader>
@@ -798,50 +848,103 @@ const TransmissionChainAnalysis = ({ eventId }) => {
<VStack align="stretch" spacing={4}> <VStack align="stretch" spacing={4}>
{/* 节点基本信息 */} {/* 节点基本信息 */}
<Box> <Box>
<Text fontWeight="bold" mb={2} color="blue.600">基本信息</Text> <Text fontWeight="bold" mb={2} color={PROFESSIONAL_COLORS.gold[500]}>基本信息</Text>
<VStack align="stretch" spacing={2}> <VStack align="stretch" spacing={2}>
<HStack align="center"> <HStack align="center">
<Text fontSize="sm"><strong>名称:</strong> {selectedNode.name}</Text> <Text fontSize="sm" color={PROFESSIONAL_COLORS.text.primary}>
<strong>名称:</strong> {selectedNode.name}
</Text>
{selectedNode.extra?.is_main_event && ( {selectedNode.extra?.is_main_event && (
<Badge colorScheme="red" variant="solid" size="sm">主事件</Badge> <Badge
bg="rgba(239, 68, 68, 0.15)"
color="#EF4444"
borderWidth="1px"
borderColor="#EF4444"
size="sm"
>
主事件
</Badge>
)} )}
</HStack> </HStack>
<Text fontSize="sm"><strong>类型:</strong> {NODE_TYPE_LABELS[selectedNode.extra?.node_type] || selectedNode.extra?.node_type}</Text> <Text fontSize="sm" color={PROFESSIONAL_COLORS.text.primary}>
<Text fontSize="sm"><strong>重要性评分:</strong> {selectedNode.extra?.importance_score || 'N/A'}</Text> <strong>类型:</strong> {NODE_TYPE_LABELS[selectedNode.extra?.node_type] || selectedNode.extra?.node_type}
</Text>
<Text fontSize="sm" color={PROFESSIONAL_COLORS.text.primary}>
<strong>重要性评分:</strong> {selectedNode.extra?.importance_score || 'N/A'}
</Text>
{selectedNode.extra?.stock_code && ( {selectedNode.extra?.stock_code && (
<Text fontSize="sm"><strong>股票代码:</strong> <Text fontSize="sm" color={PROFESSIONAL_COLORS.text.primary}>
<Badge colorScheme="cyan" ml={2}>{selectedNode.extra.stock_code}</Badge> <strong>股票代码:</strong>
<Badge
bg="rgba(6, 182, 212, 0.15)"
color="#06B6D4"
borderWidth="1px"
borderColor="#06B6D4"
ml={2}
>
{selectedNode.extra.stock_code}
</Badge>
</Text> </Text>
)} )}
{nodeDetail ? ( {nodeDetail ? (
<HStack spacing={4}> <HStack spacing={4}>
<Badge colorScheme="gray"> <Badge
bg={PROFESSIONAL_COLORS.background.secondary}
color={PROFESSIONAL_COLORS.text.primary}
borderWidth="1px"
borderColor={PROFESSIONAL_COLORS.border.default}
>
总连接: {nodeDetail.node.total_connections || 0} 总连接: {nodeDetail.node.total_connections || 0}
</Badge> </Badge>
<Badge colorScheme="green"> <Badge
bg="rgba(16, 185, 129, 0.15)"
color="#10B981"
borderWidth="1px"
borderColor="#10B981"
>
来源: {nodeDetail.node.incoming_connections || 0} 来源: {nodeDetail.node.incoming_connections || 0}
</Badge> </Badge>
<Badge colorScheme="orange"> <Badge
bg="rgba(251, 146, 60, 0.15)"
color="#FB923C"
borderWidth="1px"
borderColor="#FB923C"
>
目标: {nodeDetail.node.outgoing_connections || 0} 目标: {nodeDetail.node.outgoing_connections || 0}
</Badge> </Badge>
</HStack> </HStack>
) : ( ) : (
<HStack spacing={4}> <HStack spacing={4}>
<Badge colorScheme="gray"> <Badge
总连接: {graphData ? graphData.edges.filter(e => bg={PROFESSIONAL_COLORS.background.secondary}
color={PROFESSIONAL_COLORS.text.primary}
borderWidth="1px"
borderColor={PROFESSIONAL_COLORS.border.default}
>
总连接: {graphData ? graphData.edges.filter(e =>
String(e.source) === String(selectedNode.id) || String(e.target) === String(selectedNode.id) String(e.source) === String(selectedNode.id) || String(e.target) === String(selectedNode.id)
).length : 0} ).length : 0}
</Badge> </Badge>
<Badge colorScheme="green"> <Badge
来源: {graphData ? graphData.edges.filter(e => bg="rgba(16, 185, 129, 0.15)"
color="#10B981"
borderWidth="1px"
borderColor="#10B981"
>
来源: {graphData ? graphData.edges.filter(e =>
String(e.target) === String(selectedNode.id) String(e.target) === String(selectedNode.id)
).length : 0} ).length : 0}
</Badge> </Badge>
<Badge colorScheme="orange"> <Badge
目标: {graphData ? graphData.edges.filter(e => bg="rgba(251, 146, 60, 0.15)"
color="#FB923C"
borderWidth="1px"
borderColor="#FB923C"
>
目标: {graphData ? graphData.edges.filter(e =>
String(e.source) === String(selectedNode.id) String(e.source) === String(selectedNode.id)
).length : 0} ).length : 0}
</Badge> </Badge>
@@ -853,14 +956,14 @@ const TransmissionChainAnalysis = ({ eventId }) => {
{/* 节点描述 */} {/* 节点描述 */}
{selectedNode.extra?.description && ( {selectedNode.extra?.description && (
<Box> <Box>
<Text fontWeight="bold" mb={2} color="blue.600">描述</Text> <Text fontWeight="bold" mb={2} color={PROFESSIONAL_COLORS.gold[500]}>描述</Text>
<Box <Box
fontSize="sm" fontSize="sm"
color="gray.600" color={PROFESSIONAL_COLORS.text.secondary}
borderLeft="3px solid" borderLeft="3px solid"
borderColor="blue.200" borderColor="#3B82F6"
pl={3} pl={3}
bg="gray.50" bg={PROFESSIONAL_COLORS.background.secondary}
p={2} p={2}
borderRadius="md" borderRadius="md"
fontStyle="italic" fontStyle="italic"
@@ -880,11 +983,17 @@ const TransmissionChainAnalysis = ({ eventId }) => {
{/* 传导路径 */} {/* 传导路径 */}
{transmissionPath && transmissionPath.length > 0 && ( {transmissionPath && transmissionPath.length > 0 && (
<Box> <Box>
<Text fontWeight="bold" mb={2} color="blue.600">传导路径</Text> <Text fontWeight="bold" mb={2} color={PROFESSIONAL_COLORS.gold[500]}>传导路径</Text>
<Box bg="gray.50" p={3} borderRadius="md" borderLeft="4px solid" borderColor="blue.500"> <Box
bg={PROFESSIONAL_COLORS.background.secondary}
p={3}
borderRadius="md"
borderLeft="4px solid"
borderColor="#3B82F6"
>
<List spacing={1}> <List spacing={1}>
{transmissionPath.map((node, index) => ( {transmissionPath.map((node, index) => (
<ListItem key={index} fontSize="sm"> <ListItem key={index} fontSize="sm" color={PROFESSIONAL_COLORS.text.primary}>
{index === 0 && '🚀 '} {index === 0 && '🚀 '}
{index === transmissionPath.length - 1 && '🎯 '} {index === transmissionPath.length - 1 && '🎯 '}
{index > 0 && index < transmissionPath.length - 1 && '➡️ '} {index > 0 && index < transmissionPath.length - 1 && '➡️ '}
@@ -906,19 +1015,38 @@ const TransmissionChainAnalysis = ({ eventId }) => {
if (sourcesFromAPI) { if (sourcesFromAPI) {
return ( return (
<Box> <Box>
<Text fontWeight="bold" mb={2} color="blue.600"> <Text fontWeight="bold" mb={2} color={PROFESSIONAL_COLORS.gold[500]}>
影响来源 ({nodeDetail.parents.length})AI合成 影响来源 ({nodeDetail.parents.length})AI合成
</Text> </Text>
<List spacing={2}> <List spacing={2}>
{nodeDetail.parents.map((parent, index) => ( {nodeDetail.parents.map((parent, index) => (
<ListItem key={index} p={2} bg="gray.50" borderRadius="md" borderLeft="3px solid" borderColor="green.300" position="relative"> <ListItem
key={index}
p={2}
bg={PROFESSIONAL_COLORS.background.secondary}
borderRadius="md"
borderLeft="3px solid"
borderColor="#10B981"
position="relative"
>
<HStack position="absolute" top={2} right={2} spacing={2} zIndex={1}> <HStack position="absolute" top={2} right={2} spacing={2} zIndex={1}>
{parent.direction && ( {parent.direction && (
<Badge <Badge
colorScheme={ bg={
parent.direction === 'positive' ? 'green' : parent.direction === 'positive' ? 'rgba(16, 185, 129, 0.15)' :
parent.direction === 'negative' ? 'red' : parent.direction === 'negative' ? 'rgba(239, 68, 68, 0.15)' :
'gray' 'rgba(107, 114, 128, 0.15)'
}
color={
parent.direction === 'positive' ? '#10B981' :
parent.direction === 'negative' ? '#EF4444' :
'#6B7280'
}
borderWidth="1px"
borderColor={
parent.direction === 'positive' ? '#10B981' :
parent.direction === 'negative' ? '#EF4444' :
'#6B7280'
} }
size="sm" size="sm"
> >
@@ -928,11 +1056,26 @@ const TransmissionChainAnalysis = ({ eventId }) => {
</Badge> </Badge>
)} )}
{parent.is_circular && ( {parent.is_circular && (
<Badge colorScheme="purple" size="sm">🔄 循环</Badge> <Badge
bg="rgba(168, 85, 247, 0.15)"
color="#A855F7"
borderWidth="1px"
borderColor="#A855F7"
size="sm"
>
🔄 循环
</Badge>
)} )}
</HStack> </HStack>
<VStack align="stretch" spacing={1}> <VStack align="stretch" spacing={1}>
<Text fontWeight="bold" fontSize="sm" pr={parent.direction || parent.is_circular ? 20 : 0}>{parent.name}</Text> <Text
fontWeight="bold"
fontSize="sm"
color={PROFESSIONAL_COLORS.text.primary}
pr={parent.direction || parent.is_circular ? 20 : 0}
>
{parent.name}
</Text>
{parent.transmission_mechanism?.data ? ( {parent.transmission_mechanism?.data ? (
<CitedContent <CitedContent
data={parent.transmission_mechanism} data={parent.transmission_mechanism}