feat: 添加合规

This commit is contained in:
zdl
2025-10-20 21:25:33 +08:00
parent d695f8ff7b
commit 6c96299b8f
42 changed files with 7118 additions and 289 deletions

View File

@@ -149,11 +149,34 @@ const HistoricalEvents = ({
return `${Math.floor(diffDays / 365)}年前`;
};
// 处理关联描述字段的辅助函数
const getRelationDesc = (relationDesc) => {
// 处理空值
if (!relationDesc) return '';
// 如果是字符串,直接返回
if (typeof relationDesc === 'string') {
return relationDesc;
}
// 如果是对象且包含data数组
if (typeof relationDesc === 'object' && relationDesc.data && Array.isArray(relationDesc.data)) {
const firstItem = relationDesc.data[0];
if (firstItem) {
// 优先使用 query_part,其次使用 sentences
return firstItem.query_part || firstItem.sentences || '';
}
}
// 其他情况返回空字符串
return '';
};
// 可展开的文本组件
const ExpandableText = ({ text, maxLength = 20 }) => {
const { isOpen, onToggle } = useDisclosure();
const [shouldTruncate, setShouldTruncate] = useState(false);
useEffect(() => {
if (text && text.length > maxLength) {
setShouldTruncate(true);
@@ -161,22 +184,22 @@ const HistoricalEvents = ({
setShouldTruncate(false);
}
}, [text, maxLength]);
if (!text) return <Text fontSize="xs">--</Text>;
const displayText = shouldTruncate && !isOpen
? text.substring(0, maxLength) + '...'
const displayText = shouldTruncate && !isOpen
? text.substring(0, maxLength) + '...'
: text;
return (
<VStack align="flex-start" spacing={1}>
<Text fontSize="xs" noOfLines={isOpen ? undefined : 2} maxW="300px">
{displayText}{text.includes('AI合成') ? '' : 'AI合成'}
</Text>
{shouldTruncate && (
<Button
size="xs"
variant="link"
<Button
size="xs"
variant="link"
color="blue.500"
onClick={onToggle}
height="auto"
@@ -444,13 +467,36 @@ const HistoricalEvents = ({
// 股票列表子组件
const StocksList = ({ stocks, eventTradingDate }) => {
const textSecondary = useColorModeValue('gray.600', 'gray.400');
// 处理股票代码,移除.SZ/.SH后缀
const formatStockCode = (stockCode) => {
if (!stockCode) return '';
return stockCode.replace(/\.(SZ|SH)$/i, '');
};
// 处理关联描述字段的辅助函数
const getRelationDesc = (relationDesc) => {
// 处理空值
if (!relationDesc) return '';
// 如果是字符串,直接返回
if (typeof relationDesc === 'string') {
return relationDesc;
}
// 如果是对象且包含data数组
if (typeof relationDesc === 'object' && relationDesc.data && Array.isArray(relationDesc.data)) {
const firstItem = relationDesc.data[0];
if (firstItem) {
// 优先使用 query_part,其次使用 sentences
return firstItem.query_part || firstItem.sentences || '';
}
}
// 其他情况返回空字符串
return '';
};
if (!stocks || stocks.length === 0) {
return (
<Box textAlign="center" py={8} color={textSecondary}>
@@ -527,7 +573,7 @@ const StocksList = ({ stocks, eventTradingDate }) => {
<Td>
<VStack align="flex-start" spacing={1}>
<Text fontSize="xs" noOfLines={2} maxW="300px">
{stock.relation_desc ? `${stock.relation_desc}AI合成` : '--'}
{getRelationDesc(stock.relation_desc) ? `${getRelationDesc(stock.relation_desc)}AI合成` : '--'}
</Text>
</VStack>
</Td>

View File

@@ -65,6 +65,7 @@ import ReactECharts from 'echarts-for-react';
import HomeNavbar from '../../../components/Navbars/HomeNavbar';
import { logger } from '../../../utils/logger';
import { getApiBase } from '../../../utils/apiConfig';
// 板块关联TOP10数据计算
function getSectorRelationTop10(sectorData) {
@@ -177,7 +178,7 @@ function getTop10Sectors(chartData) {
}
const isProduction = process.env.NODE_ENV === 'production';
const API_BASE = isProduction ? "" : process.env.REACT_APP_API_URL;
const API_BASE = getApiBase();
// const API_BASE = 'http://49.232.185.254:5001'; // 改回5001端口确保和后端一致
// 涨停分析服务

View File

@@ -206,6 +206,28 @@ const RelatedStocks = ({
return `股票${stock.stock_code.split('.')[0]}`;
};
const getRelationDesc = (relationDesc) => {
// 处理空值
if (!relationDesc) return '--';
// 如果是字符串,直接返回
if (typeof relationDesc === 'string') {
return relationDesc;
}
// 如果是对象且包含data数组
if (typeof relationDesc === 'object' && relationDesc.data && Array.isArray(relationDesc.data)) {
const firstItem = relationDesc.data[0];
if (firstItem) {
// 优先使用 query_part,其次使用 sentences
return firstItem.query_part || firstItem.sentences || '--';
}
}
// 其他情况返回默认值
return '--';
};
// ==================== 数据处理 ====================
const filteredAndSortedStocks = stocksData
.filter(stock => {
@@ -442,7 +464,7 @@ const RelatedStocks = ({
{/* 关联描述 */}
<Td maxW="200px">
<Text fontSize="xs" noOfLines={2}>
{stock.relation_desc || '--'}
{getRelationDesc(stock.relation_desc)}
</Text>
</Td>

View File

@@ -405,6 +405,14 @@ const TransmissionChainAnalysis = ({ eventId }) => {
const modalBgColor = useColorModeValue('white', 'gray.800');
const modalBorderColor = useColorModeValue('gray.200', 'gray.600');
// 关闭弹窗并清空状态
const handleCloseModal = () => {
setIsModalOpen(false);
setSelectedNode(null);
setNodeDetail(null);
setTransmissionPath([]);
};
// 延迟初始化图表确保DOM容器准备好
useEffect(() => {
const timer = setTimeout(() => {
@@ -771,9 +779,10 @@ const TransmissionChainAnalysis = ({ eventId }) => {
)}
{/* 节点详情弹窗 */}
<Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} size="xl">
<ModalOverlay />
<ModalContent maxH="80vh" bg={modalBgColor}>
{isModalOpen && (
<Modal isOpen={isModalOpen} onClose={handleCloseModal} size="xl">
<ModalOverlay />
<ModalContent maxH="80vh" bg={modalBgColor}>
<ModalHeader borderBottom="1px solid" borderColor={modalBorderColor}>
<HStack justify="space-between">
<Text>{selectedNode ? '节点详情' : '传导链分析'}</Text>
@@ -845,9 +854,9 @@ const TransmissionChainAnalysis = ({ eventId }) => {
{selectedNode.extra?.description && (
<Box>
<Text fontWeight="bold" mb={2} color="blue.600">描述</Text>
<Box
fontSize="sm"
color="gray.600"
<Box
fontSize="sm"
color="gray.600"
borderLeft="3px solid"
borderColor="blue.200"
pl={3}
@@ -856,7 +865,19 @@ const TransmissionChainAnalysis = ({ eventId }) => {
borderRadius="md"
fontStyle="italic"
>
{selectedNode.extra.description}AI合成
{selectedNode.extra.description?.data ? (
<>
<CitedContent
data={selectedNode.extra.description}
title=""
showAIBadge={false}
containerStyle={{ backgroundColor: 'transparent', padding: 0, display: 'inline-block', verticalAlign: 'baseline' }}
/>
AI合成
</>
) : (
`${selectedNode.extra.description}AI合成`
)}
</Box>
</Box>
)}
@@ -899,16 +920,17 @@ const TransmissionChainAnalysis = ({ eventId }) => {
<HStack justify="space-between" align="flex-start">
<VStack align="stretch" spacing={1} flex={1}>
<Text fontWeight="bold" fontSize="sm">{parent.name}</Text>
{parent.transmission_mechanism_citation?.data ? (
<Box fontSize="xs">
{parent.transmission_mechanism?.data ? (
<Text fontSize="xs">
<Text as="span" fontWeight="bold">机制: </Text>
<CitedContent
data={parent.transmission_mechanism_citation.data}
data={parent.transmission_mechanism}
title=""
showAIBadge={false}
containerStyle={{ backgroundColor: 'transparent', padding: 0, display: 'inline' }}
containerStyle={{ backgroundColor: 'transparent', padding: 0, display: 'inline-block', verticalAlign: 'baseline' }}
/>
</Box>
AI合成
</Text>
) : parent.transmission_mechanism ? (
<Text fontSize="xs" color="gray.600">
机制: {parent.transmission_mechanism}AI合成
@@ -950,15 +972,16 @@ const TransmissionChainAnalysis = ({ eventId }) => {
<VStack align="stretch" spacing={1} flex={1}>
<Text fontWeight="bold" fontSize="sm">{child.name}</Text>
{child.transmission_mechanism?.data ? (
<Box fontSize="xs">
<Text fontSize="xs">
<Text as="span" fontWeight="bold">机制: </Text>
<CitedContent
data={child.transmission_mechanism.data}
data={child.transmission_mechanism}
title=""
showAIBadge={false}
containerStyle={{ backgroundColor: 'transparent', padding: 0, display: 'inline' }}
containerStyle={{ backgroundColor: 'transparent', padding: 0, display: 'inline-block', verticalAlign: 'baseline' }}
/>
</Box>
AI合成
</Text>
) : child.transmission_mechanism ? (
<Text fontSize="xs" color="gray.600">
机制: {child.transmission_mechanism}AI合成
@@ -991,10 +1014,11 @@ const TransmissionChainAnalysis = ({ eventId }) => {
</ModalBody>
<ModalFooter borderTop="1px solid" borderColor={modalBorderColor}>
<Button onClick={() => setIsModalOpen(false)}>关闭</Button>
<Button onClick={handleCloseModal}>关闭</Button>
</ModalFooter>
</ModalContent>
</Modal>
)}
</Box>
);
};