// src/components/EventDetailPanel/RelatedConceptsSection/index.js // 相关概念区组件 - 便当盒网格布局 import React, { useState, useEffect } from 'react'; import { Box, Flex, Heading, Center, Spinner, Text, Badge, SimpleGrid, HStack, Tooltip, useColorModeValue, } from '@chakra-ui/react'; import { useNavigate } from 'react-router-dom'; import { logger } from '@utils/logger'; import { getApiBase } from '@utils/apiConfig'; /** * 单个概念卡片组件(便当盒样式) */ const ConceptCard = ({ concept, onNavigate, isLocked, onLockedClick }) => { // 深色主题固定颜色 const cardBg = 'rgba(252, 129, 129, 0.15)'; // 浅红色背景 const cardHoverBg = 'rgba(252, 129, 129, 0.25)'; const borderColor = 'rgba(252, 129, 129, 0.3)'; const conceptColor = '#fc8181'; // 红色文字(与股票涨色一致) const handleClick = () => { if (isLocked && onLockedClick) { onLockedClick(); return; } onNavigate(concept); }; return ( {concept.concept} ); }; /** * 相关概念区组件 * @param {Object} props * @param {number} props.eventId - 事件ID(用于获取 related_concepts 表数据) * @param {string} props.eventTitle - 事件标题(备用) * @param {React.ReactNode} props.subscriptionBadge - 订阅徽章组件(可选) * @param {boolean} props.isLocked - 是否锁定(需要付费) * @param {Function} props.onLockedClick - 锁定时的点击回调 */ const RelatedConceptsSection = ({ eventId, eventTitle, subscriptionBadge = null, isLocked = false, onLockedClick = null, }) => { const [concepts, setConcepts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const navigate = useNavigate(); // 颜色配置 - 使用深色主题固定颜色 const sectionBg = 'transparent'; const headingColor = '#e2e8f0'; const textColor = '#a0aec0'; const countBadgeBg = '#3182ce'; const countBadgeColor = '#ffffff'; // 获取相关概念 useEffect(() => { const fetchConcepts = async () => { if (!eventId) { setLoading(false); return; } try { setLoading(true); setError(null); const apiUrl = `${getApiBase()}/api/events/${eventId}/concepts`; const response = await fetch(apiUrl, { method: 'GET', headers: { 'Content-Type': 'application/json' }, credentials: 'include' }); if (!response.ok) { if (response.status === 403) { setConcepts([]); setLoading(false); return; } throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); if (data.success && Array.isArray(data.data)) { setConcepts(data.data); } else { setConcepts([]); } } catch (err) { console.error('[RelatedConceptsSection] 获取概念失败', err); logger.error('RelatedConceptsSection', 'fetchConcepts', err); setError('加载概念数据失败'); setConcepts([]); } finally { setLoading(false); } }; fetchConcepts(); }, [eventId]); // 跳转到概念中心 const handleNavigate = (concept) => { navigate(`/concepts?q=${encodeURIComponent(concept.concept)}`); }; // 加载中状态 if (loading) { return (
加载相关概念中...
); } const hasNoConcepts = !concepts || concepts.length === 0; return ( {/* 标题栏 */} 相关概念 {!hasNoConcepts && ( {concepts.length} )} {subscriptionBadge} {/* 概念列表 - 便当盒网格布局 */} {hasNoConcepts ? ( {error ? ( {error} ) : ( 暂无相关概念数据 )} ) : ( {concepts.map((concept, index) => ( ))} )} ); }; export default RelatedConceptsSection;