概念板块重做
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// src/components/EventDetailPanel/RelatedConceptsSection/index.js
|
||||
// 相关概念区组件 - 概念中心风格卡片
|
||||
// 相关概念区组件 - 艺术风格卡片
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import {
|
||||
Box,
|
||||
@@ -16,28 +16,25 @@ import {
|
||||
Skeleton,
|
||||
Card,
|
||||
CardBody,
|
||||
Image,
|
||||
} from '@chakra-ui/react';
|
||||
import { keyframes } from '@emotion/react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Layers, ArrowUp, ArrowDown } from 'lucide-react';
|
||||
import { Layers, ArrowUp, ArrowDown, TrendingUp } from 'lucide-react';
|
||||
import { logger } from '@utils/logger';
|
||||
import { getApiBase } from '@utils/apiConfig';
|
||||
import { selectSubscriptionInfo } from '@store/slices/subscriptionSlice';
|
||||
|
||||
// 脉冲动画
|
||||
const pulseAnimation = keyframes`
|
||||
0% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.05); opacity: 0.9; }
|
||||
100% { transform: scale(1); opacity: 1; }
|
||||
// 呼吸光效动画
|
||||
const glowAnimation = keyframes`
|
||||
0%, 100% { opacity: 0.4; transform: scale(1); }
|
||||
50% { opacity: 0.6; transform: scale(1.02); }
|
||||
`;
|
||||
|
||||
// 毛玻璃效果
|
||||
const GLASS_BLUR = {
|
||||
sm: 'blur(4px)',
|
||||
md: 'blur(8px)',
|
||||
lg: 'blur(12px)',
|
||||
};
|
||||
// 微光流动动画
|
||||
const shimmerAnimation = keyframes`
|
||||
0% { background-position: -200% center; }
|
||||
100% { background-position: 200% center; }
|
||||
`;
|
||||
|
||||
/**
|
||||
* 格式化涨跌幅
|
||||
@@ -49,39 +46,51 @@ const formatChangePercent = (value) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取涨跌幅颜色
|
||||
* 获取涨跌幅颜色配置
|
||||
*/
|
||||
const getChangeColor = (value) => {
|
||||
if (value === null || value === undefined) return 'gray';
|
||||
if (value > 0) return 'red';
|
||||
if (value < 0) return 'green';
|
||||
return 'gray';
|
||||
const getChangeTheme = (value) => {
|
||||
if (value === null || value === undefined) {
|
||||
return {
|
||||
color: 'gray',
|
||||
gradient: 'linear(135deg, rgba(71, 85, 105, 0.3) 0%, rgba(51, 65, 85, 0.5) 100%)',
|
||||
glow: 'rgba(100, 116, 139, 0.3)',
|
||||
badge: 'rgba(100, 116, 139, 0.9)',
|
||||
};
|
||||
}
|
||||
if (value > 0) {
|
||||
return {
|
||||
color: 'red',
|
||||
gradient: 'linear(135deg, rgba(127, 29, 29, 0.4) 0%, rgba(185, 28, 28, 0.2) 100%)',
|
||||
glow: 'rgba(239, 68, 68, 0.2)',
|
||||
badge: 'rgba(220, 38, 38, 0.95)',
|
||||
};
|
||||
}
|
||||
if (value < 0) {
|
||||
return {
|
||||
color: 'green',
|
||||
gradient: 'linear(135deg, rgba(20, 83, 45, 0.4) 0%, rgba(22, 101, 52, 0.2) 100%)',
|
||||
glow: 'rgba(34, 197, 94, 0.2)',
|
||||
badge: 'rgba(22, 163, 74, 0.95)',
|
||||
};
|
||||
}
|
||||
return {
|
||||
color: 'gray',
|
||||
gradient: 'linear(135deg, rgba(71, 85, 105, 0.3) 0%, rgba(51, 65, 85, 0.5) 100%)',
|
||||
glow: 'rgba(100, 116, 139, 0.3)',
|
||||
badge: 'rgba(100, 116, 139, 0.9)',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 概念卡片组件 - 概念中心风格
|
||||
* 概念卡片组件 - 艺术风格
|
||||
*/
|
||||
const ConceptCard = ({ concept, onNavigate, isLocked, onLockedClick, index }) => {
|
||||
const changePercent = concept.price_info?.avg_change_pct;
|
||||
const changeColor = getChangeColor(changePercent);
|
||||
const theme = getChangeTheme(changePercent);
|
||||
const hasChange = changePercent !== null && changePercent !== undefined;
|
||||
const stockCount = concept.stock_count || 0;
|
||||
const stocks = concept.stocks || [];
|
||||
|
||||
// 生成随机涨幅数字背景
|
||||
const generateNumbersBackground = () => {
|
||||
const numbers = [];
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const isPositive = Math.random() > 0.5;
|
||||
const value = (Math.random() * 10).toFixed(2);
|
||||
const sign = isPositive ? '+' : '-';
|
||||
numbers.push(`${sign}${value}%`);
|
||||
}
|
||||
return numbers;
|
||||
};
|
||||
|
||||
const backgroundNumbers = generateNumbersBackground();
|
||||
|
||||
const handleClick = () => {
|
||||
if (isLocked && onLockedClick) {
|
||||
onLockedClick();
|
||||
@@ -94,173 +103,163 @@ const ConceptCard = ({ concept, onNavigate, isLocked, onLockedClick, index }) =>
|
||||
<Card
|
||||
cursor="pointer"
|
||||
onClick={handleClick}
|
||||
bg="rgba(15, 23, 42, 0.8)"
|
||||
backdropFilter={GLASS_BLUR.lg}
|
||||
bg="rgba(15, 23, 42, 0.85)"
|
||||
backdropFilter="blur(12px)"
|
||||
borderWidth="1px"
|
||||
borderColor="whiteAlpha.100"
|
||||
overflow="hidden"
|
||||
_hover={{
|
||||
transform: 'translateY(-4px)',
|
||||
boxShadow: '0 16px 32px rgba(139, 92, 246, 0.2)',
|
||||
borderColor: 'purple.500',
|
||||
transform: 'translateY(-4px) scale(1.01)',
|
||||
boxShadow: `0 20px 40px ${theme.glow}, 0 0 0 1px rgba(139, 92, 246, 0.3)`,
|
||||
borderColor: 'purple.400',
|
||||
'& .card-glow': {
|
||||
opacity: 0.8,
|
||||
},
|
||||
'& .concept-title': {
|
||||
textShadow: '0 0 30px rgba(255, 255, 255, 0.3)',
|
||||
},
|
||||
}}
|
||||
transition="all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
|
||||
transition="all 0.4s cubic-bezier(0.4, 0, 0.2, 1)"
|
||||
position="relative"
|
||||
boxShadow="0 4px 16px rgba(0, 0, 0, 0.3)"
|
||||
boxShadow="0 4px 20px rgba(0, 0, 0, 0.4)"
|
||||
borderRadius="xl"
|
||||
>
|
||||
{/* 排名徽章 */}
|
||||
{index < 3 && (
|
||||
<Badge
|
||||
position="absolute"
|
||||
top={2}
|
||||
left={2}
|
||||
top={3}
|
||||
left={3}
|
||||
zIndex={10}
|
||||
bg={index === 0 ? 'yellow.500' : index === 1 ? 'orange.400' : 'red.400'}
|
||||
bg={index === 0 ? 'linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%)' : index === 1 ? 'linear-gradient(135deg, #9ca3af 0%, #6b7280 100%)' : 'linear-gradient(135deg, #f97316 0%, #ea580c 100%)'}
|
||||
color="white"
|
||||
borderRadius="full"
|
||||
w="22px"
|
||||
h="22px"
|
||||
w="24px"
|
||||
h="24px"
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
fontSize="xs"
|
||||
fontWeight="bold"
|
||||
boxShadow="0 2px 8px rgba(0, 0, 0, 0.3)"
|
||||
boxShadow="0 2px 10px rgba(0, 0, 0, 0.3)"
|
||||
>
|
||||
{index + 1}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
{/* 毛玻璃涨幅数字背景 */}
|
||||
<Box position="relative" height="100px" overflow="hidden">
|
||||
{/* 渐变背景层 - 涨红跌绿 */}
|
||||
{/* 头部区域 - 艺术风格 */}
|
||||
<Box position="relative" height="90px" overflow="hidden">
|
||||
{/* 底层渐变 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
bgGradient={
|
||||
hasChange && changePercent > 0
|
||||
? "linear(135deg, rgba(153, 27, 27, 0.6) 0%, rgba(239, 68, 68, 0.4) 100%)"
|
||||
: hasChange && changePercent < 0
|
||||
? "linear(135deg, rgba(20, 83, 45, 0.6) 0%, rgba(34, 197, 94, 0.4) 100%)"
|
||||
: "linear(135deg, rgba(71, 85, 105, 0.6) 0%, rgba(100, 116, 139, 0.4) 100%)"
|
||||
}
|
||||
bgGradient={theme.gradient}
|
||||
/>
|
||||
|
||||
{/* 数字矩阵层 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
display="grid"
|
||||
gridTemplateColumns="repeat(4, 1fr)"
|
||||
gridTemplateRows="repeat(4, 1fr)"
|
||||
gap={1}
|
||||
p={2}
|
||||
opacity={0.08}
|
||||
>
|
||||
{backgroundNumbers.map((num, idx) => (
|
||||
<Flex
|
||||
key={idx}
|
||||
align="center"
|
||||
justify="center"
|
||||
fontSize="9px"
|
||||
fontWeight="bold"
|
||||
color="white"
|
||||
transform={`rotate(${Math.random() * 16 - 8}deg)`}
|
||||
>
|
||||
{num}
|
||||
</Flex>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
{/* Logo 水印 */}
|
||||
{/* 光效层 */}
|
||||
<Box
|
||||
className="card-glow"
|
||||
position="absolute"
|
||||
top="50%"
|
||||
left="50%"
|
||||
transform="translate(-50%, -50%)"
|
||||
width="50px"
|
||||
height="50px"
|
||||
opacity={0.08}
|
||||
>
|
||||
<Image
|
||||
src={`${process.env.PUBLIC_URL}/LOGO_badge.png`}
|
||||
alt="Logo"
|
||||
width="100%"
|
||||
height="100%"
|
||||
objectFit="contain"
|
||||
/>
|
||||
</Box>
|
||||
width="120%"
|
||||
height="120%"
|
||||
bgGradient={`radial(circle at center, ${theme.glow} 0%, transparent 70%)`}
|
||||
opacity={0.5}
|
||||
animation={`${glowAnimation} 3s ease-in-out infinite`}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
|
||||
{/* 高光效果 */}
|
||||
{/* 顶部微光条 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
height="50%"
|
||||
bg="linear-gradient(180deg, rgba(255,255,255,0.06) 0%, transparent 100%)"
|
||||
pointerEvents="none"
|
||||
height="2px"
|
||||
bg={`linear-gradient(90deg, transparent 0%, ${theme.glow} 50%, transparent 100%)`}
|
||||
backgroundSize="200% 100%"
|
||||
animation={`${shimmerAnimation} 3s linear infinite`}
|
||||
/>
|
||||
|
||||
{/* 涨跌幅 Badge */}
|
||||
{/* 概念标题 - 居中大字 */}
|
||||
<Flex
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
align="center"
|
||||
justify="center"
|
||||
px={4}
|
||||
>
|
||||
<Text
|
||||
className="concept-title"
|
||||
fontSize="xl"
|
||||
fontWeight="bold"
|
||||
color="white"
|
||||
textAlign="center"
|
||||
noOfLines={1}
|
||||
letterSpacing="0.05em"
|
||||
textShadow="0 2px 10px rgba(0, 0, 0, 0.5)"
|
||||
transition="text-shadow 0.3s ease"
|
||||
>
|
||||
{concept.concept}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
{/* 涨跌幅徽章 - 右上角 */}
|
||||
{hasChange && (
|
||||
<Badge
|
||||
position="absolute"
|
||||
top={2}
|
||||
right={2}
|
||||
bg={changeColor === 'red' ? 'rgba(239, 68, 68, 0.9)' : changeColor === 'green' ? 'rgba(34, 197, 94, 0.9)' : 'rgba(100, 116, 139, 0.9)'}
|
||||
top={3}
|
||||
right={3}
|
||||
bg={theme.badge}
|
||||
color="white"
|
||||
fontSize="sm"
|
||||
fontSize="xs"
|
||||
px={2}
|
||||
py={0.5}
|
||||
borderRadius="full"
|
||||
borderRadius="md"
|
||||
fontWeight="bold"
|
||||
boxShadow={`0 2px 8px rgba(${changeColor === 'red' ? '239, 68, 68' : changeColor === 'green' ? '34, 197, 94' : '100, 116, 139'}, 0.4)`}
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
gap={0.5}
|
||||
animation={Math.abs(changePercent) > 5 ? `${pulseAnimation} 2s infinite` : 'none'}
|
||||
boxShadow="0 2px 8px rgba(0, 0, 0, 0.3)"
|
||||
>
|
||||
<Icon
|
||||
as={changePercent > 0 ? ArrowUp : changePercent < 0 ? ArrowDown : null}
|
||||
as={changePercent > 0 ? ArrowUp : changePercent < 0 ? ArrowDown : TrendingUp}
|
||||
boxSize={3}
|
||||
/>
|
||||
{formatChangePercent(changePercent)}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
{/* 股票数量徽章 */}
|
||||
{/* 股票数量 - 右下角 */}
|
||||
<Badge
|
||||
position="absolute"
|
||||
bottom={2}
|
||||
right={2}
|
||||
bg="rgba(0, 0, 0, 0.5)"
|
||||
backdropFilter={GLASS_BLUR.sm}
|
||||
color="white"
|
||||
right={3}
|
||||
bg="blackAlpha.600"
|
||||
color="whiteAlpha.900"
|
||||
fontSize="xs"
|
||||
px={2}
|
||||
py={0.5}
|
||||
borderRadius="full"
|
||||
borderRadius="md"
|
||||
fontWeight="medium"
|
||||
border="1px solid"
|
||||
borderColor="whiteAlpha.200"
|
||||
>
|
||||
{stockCount} 只股票
|
||||
{stockCount} 股
|
||||
</Badge>
|
||||
</Box>
|
||||
|
||||
<CardBody p={3} bg="transparent">
|
||||
<VStack align="start" spacing={2}>
|
||||
{/* 概念名称 */}
|
||||
{/* 描述信息 */}
|
||||
<Tooltip
|
||||
label={concept.description || concept.reason || concept.concept}
|
||||
label={concept.description || concept.reason}
|
||||
placement="top"
|
||||
hasArrow
|
||||
bg="gray.800"
|
||||
@@ -269,86 +268,70 @@ const ConceptCard = ({ concept, onNavigate, isLocked, onLockedClick, index }) =>
|
||||
borderRadius="md"
|
||||
maxW="300px"
|
||||
fontSize="xs"
|
||||
isDisabled={!concept.description && !concept.reason}
|
||||
>
|
||||
<Heading
|
||||
size="sm"
|
||||
color="white"
|
||||
noOfLines={1}
|
||||
fontWeight="bold"
|
||||
letterSpacing="0.02em"
|
||||
>
|
||||
{concept.concept}
|
||||
</Heading>
|
||||
<Text color="whiteAlpha.700" fontSize="xs" noOfLines={2} minH="32px" lineHeight="tall">
|
||||
{concept.description || concept.reason || '暂无描述信息'}
|
||||
</Text>
|
||||
</Tooltip>
|
||||
|
||||
{/* 描述信息 */}
|
||||
<Text color="whiteAlpha.600" fontSize="xs" noOfLines={2} minH="32px">
|
||||
{concept.description || concept.reason || '暂无描述信息'}
|
||||
</Text>
|
||||
|
||||
{/* 关联股票预览 */}
|
||||
{stocks.length > 0 && (
|
||||
<Box
|
||||
width="100%"
|
||||
p={2}
|
||||
bg="whiteAlpha.50"
|
||||
backdropFilter={GLASS_BLUR.sm}
|
||||
borderRadius="lg"
|
||||
>
|
||||
<HStack spacing={1} flexWrap="wrap">
|
||||
{stocks.slice(0, 4).map((stock, idx) => (
|
||||
<Badge
|
||||
key={idx}
|
||||
bg="whiteAlpha.100"
|
||||
color="whiteAlpha.800"
|
||||
fontSize="10px"
|
||||
px={1.5}
|
||||
py={0.5}
|
||||
borderRadius="sm"
|
||||
fontWeight="normal"
|
||||
>
|
||||
{stock.name}
|
||||
</Badge>
|
||||
))}
|
||||
{stocks.length > 4 && (
|
||||
<Badge
|
||||
bg="purple.500"
|
||||
color="white"
|
||||
fontSize="10px"
|
||||
px={1.5}
|
||||
py={0.5}
|
||||
borderRadius="sm"
|
||||
>
|
||||
+{stocks.length - 4}
|
||||
</Badge>
|
||||
)}
|
||||
</HStack>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* 层级标签 */}
|
||||
{concept.hierarchy && (
|
||||
<HStack spacing={1} flexWrap="wrap">
|
||||
{concept.hierarchy.lv1 && (
|
||||
<HStack spacing={1} flexWrap="wrap" w="100%">
|
||||
{stocks.slice(0, 3).map((stock, idx) => (
|
||||
<Badge
|
||||
bg="whiteAlpha.200"
|
||||
key={idx}
|
||||
bg="whiteAlpha.100"
|
||||
color="whiteAlpha.800"
|
||||
fontSize="10px"
|
||||
px={1.5}
|
||||
py={0.5}
|
||||
borderRadius="sm"
|
||||
fontWeight="normal"
|
||||
>
|
||||
{stock.name}
|
||||
</Badge>
|
||||
))}
|
||||
{stockCount > 3 && (
|
||||
<Badge
|
||||
bg="purple.500"
|
||||
color="white"
|
||||
fontSize="10px"
|
||||
px={1.5}
|
||||
py={0.5}
|
||||
borderRadius="sm"
|
||||
>
|
||||
+{stockCount - 3}
|
||||
</Badge>
|
||||
)}
|
||||
</HStack>
|
||||
)}
|
||||
|
||||
{/* 层级标签 */}
|
||||
{concept.hierarchy && (concept.hierarchy.lv1 || concept.hierarchy.lv2) && (
|
||||
<HStack spacing={1} flexWrap="wrap">
|
||||
{concept.hierarchy.lv1 && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
colorScheme="purple"
|
||||
fontSize="9px"
|
||||
px={1.5}
|
||||
py={0}
|
||||
borderRadius="sm"
|
||||
opacity={0.8}
|
||||
>
|
||||
{concept.hierarchy.lv1}
|
||||
</Badge>
|
||||
)}
|
||||
{concept.hierarchy.lv2 && (
|
||||
<Badge
|
||||
bg="whiteAlpha.100"
|
||||
color="whiteAlpha.700"
|
||||
fontSize="10px"
|
||||
variant="outline"
|
||||
colorScheme="blue"
|
||||
fontSize="9px"
|
||||
px={1.5}
|
||||
py={0.5}
|
||||
py={0}
|
||||
borderRadius="sm"
|
||||
opacity={0.6}
|
||||
>
|
||||
{concept.hierarchy.lv2}
|
||||
</Badge>
|
||||
@@ -366,25 +349,24 @@ const ConceptCard = ({ concept, onNavigate, isLocked, onLockedClick, index }) =>
|
||||
*/
|
||||
const SkeletonCard = () => (
|
||||
<Card
|
||||
bg="rgba(15, 23, 42, 0.8)"
|
||||
bg="rgba(15, 23, 42, 0.85)"
|
||||
borderWidth="1px"
|
||||
borderColor="whiteAlpha.100"
|
||||
overflow="hidden"
|
||||
borderRadius="xl"
|
||||
>
|
||||
<Skeleton height="100px" startColor="whiteAlpha.100" endColor="whiteAlpha.200" />
|
||||
<Skeleton height="90px" startColor="whiteAlpha.100" endColor="whiteAlpha.200" />
|
||||
<CardBody p={3}>
|
||||
<VStack align="stretch" spacing={2}>
|
||||
<Skeleton height="18px" width="70%" startColor="whiteAlpha.100" endColor="whiteAlpha.200" />
|
||||
<Skeleton height="32px" startColor="whiteAlpha.100" endColor="whiteAlpha.200" />
|
||||
<Skeleton height="28px" startColor="whiteAlpha.100" endColor="whiteAlpha.200" />
|
||||
<Skeleton height="20px" width="60%" startColor="whiteAlpha.100" endColor="whiteAlpha.200" />
|
||||
</VStack>
|
||||
</CardBody>
|
||||
</Card>
|
||||
);
|
||||
|
||||
/**
|
||||
* 相关概念区组件 - 概念中心风格
|
||||
* 相关概念区组件
|
||||
*/
|
||||
const RelatedConceptsSection = ({
|
||||
eventId,
|
||||
@@ -400,14 +382,11 @@ const RelatedConceptsSection = ({
|
||||
const [error, setError] = useState(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
// 获取订阅信息
|
||||
const subscriptionInfo = useSelector(selectSubscriptionInfo);
|
||||
const isSubscriptionExpired = subscriptionInfo.type !== 'free' && !subscriptionInfo.is_active;
|
||||
|
||||
// 颜色配置
|
||||
const textColor = '#a0aec0';
|
||||
|
||||
// 获取相关概念
|
||||
useEffect(() => {
|
||||
const fetchConcepts = async () => {
|
||||
if (!eventId) {
|
||||
@@ -463,21 +442,17 @@ const RelatedConceptsSection = ({
|
||||
fetchConcepts();
|
||||
}, [eventId, isLocked, isSubscriptionExpired, onConceptsLoaded]);
|
||||
|
||||
// 跳转到概念中心
|
||||
const handleNavigate = (concept) => {
|
||||
navigate(`/concepts?q=${encodeURIComponent(concept.concept)}`);
|
||||
};
|
||||
|
||||
// 加载中状态
|
||||
if (loading) {
|
||||
return (
|
||||
<Box p={showHeader ? 3 : 0} borderRadius="md">
|
||||
{showHeader && (
|
||||
<Flex justify="space-between" align="center" mb={3}>
|
||||
<HStack spacing={2}>
|
||||
<Heading size="sm" color="#e2e8f0">
|
||||
相关概念
|
||||
</Heading>
|
||||
<Heading size="sm" color="#e2e8f0">相关概念</Heading>
|
||||
{subscriptionBadge}
|
||||
</HStack>
|
||||
</Flex>
|
||||
@@ -499,23 +474,13 @@ const RelatedConceptsSection = ({
|
||||
|
||||
return (
|
||||
<Box p={showHeader ? 3 : 0} borderRadius="md">
|
||||
{/* 标题栏 - 可选 */}
|
||||
{showHeader && (
|
||||
<Flex justify="space-between" align="center" mb={3}>
|
||||
<HStack spacing={2}>
|
||||
<Icon as={Layers} color="blue.400" boxSize={4} />
|
||||
<Heading size="sm" color="#e2e8f0">
|
||||
相关概念
|
||||
</Heading>
|
||||
<Heading size="sm" color="#e2e8f0">相关概念</Heading>
|
||||
{!hasNoConcepts && (
|
||||
<Badge
|
||||
bg="blue.500"
|
||||
color="white"
|
||||
fontSize="xs"
|
||||
px={2}
|
||||
py={0.5}
|
||||
borderRadius="full"
|
||||
>
|
||||
<Badge bg="blue.500" color="white" fontSize="xs" px={2} py={0.5} borderRadius="full">
|
||||
{concepts.length}
|
||||
</Badge>
|
||||
)}
|
||||
@@ -524,7 +489,6 @@ const RelatedConceptsSection = ({
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{/* 概念列表 - 网格布局 */}
|
||||
{hasNoConcepts ? (
|
||||
<Box py={2}>
|
||||
{error ? (
|
||||
|
||||
Reference in New Issue
Block a user