// 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;