refactor(HierarchyView): 支持外部状态控制
- 重构状态管理,统一使用 drillPath 格式 - 支持外部 drillPath 控制(externalDrillPath, onDrillPathChange) - 添加 hideNavigation 模式支持 - 面包屑、currentLevel 等从 drillPath 派生 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -30,8 +30,7 @@ import {
|
|||||||
Layers,
|
Layers,
|
||||||
Maximize2,
|
Maximize2,
|
||||||
Minimize2,
|
Minimize2,
|
||||||
RefreshCw,
|
ArrowLeft,
|
||||||
Home,
|
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
Brain,
|
Brain,
|
||||||
Cpu,
|
Cpu,
|
||||||
@@ -350,15 +349,6 @@ const GlassCard = ({ item, onClick, size = 'normal' }) => {
|
|||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</VStack>
|
</VStack>
|
||||||
|
|
||||||
{/* 外链图标 */}
|
|
||||||
{isLeafConcept && (
|
|
||||||
<Icon
|
|
||||||
as={ExternalLink}
|
|
||||||
boxSize={3}
|
|
||||||
color="whiteAlpha.500"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|
||||||
{/* 底部:涨跌幅 */}
|
{/* 底部:涨跌幅 */}
|
||||||
@@ -392,23 +382,44 @@ const GlassCard = ({ item, onClick, size = 'normal' }) => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{/* 展开标识 */}
|
{/* 展开标识 - 使用箭头图标 */}
|
||||||
{canDrillDown && (
|
{canDrillDown && (
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
top={2}
|
||||||
|
right={2}
|
||||||
|
p={1.5}
|
||||||
|
bg="whiteAlpha.200"
|
||||||
|
borderRadius="md"
|
||||||
|
backdropFilter={GLASS_BLUR.sm}
|
||||||
|
border="1px solid"
|
||||||
|
borderColor="whiteAlpha.100"
|
||||||
|
_hover={{ bg: 'whiteAlpha.300' }}
|
||||||
|
transition="all 0.2s"
|
||||||
|
>
|
||||||
|
<Icon as={Maximize2} boxSize={4} color="whiteAlpha.800" />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 详情按钮 - 仅在 concept 层显示 */}
|
||||||
|
{isLeafConcept && (
|
||||||
<Badge
|
<Badge
|
||||||
position="absolute"
|
position="absolute"
|
||||||
top={2}
|
top={2}
|
||||||
right={2}
|
right={2}
|
||||||
bg="whiteAlpha.200"
|
bg="rgba(139, 92, 246, 0.3)"
|
||||||
color="whiteAlpha.800"
|
color="purple.200"
|
||||||
fontSize="xs"
|
fontSize="xs"
|
||||||
borderRadius="full"
|
borderRadius="md"
|
||||||
px={2}
|
px={2}
|
||||||
py={0.5}
|
py={1}
|
||||||
backdropFilter={GLASS_BLUR.sm}
|
backdropFilter={GLASS_BLUR.sm}
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor="whiteAlpha.100"
|
borderColor="purple.400"
|
||||||
|
_hover={{ bg: 'rgba(139, 92, 246, 0.5)' }}
|
||||||
|
transition="all 0.2s"
|
||||||
>
|
>
|
||||||
展开
|
详情 >
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
@@ -422,6 +433,11 @@ const HierarchyView = ({
|
|||||||
apiBaseUrl,
|
apiBaseUrl,
|
||||||
onSelectCategory,
|
onSelectCategory,
|
||||||
selectedDate,
|
selectedDate,
|
||||||
|
// 外部控制的 drillPath(可选,用于状态共享)
|
||||||
|
externalDrillPath,
|
||||||
|
onDrillPathChange,
|
||||||
|
// 是否隐藏导航和背景(由外部 ChartContainer 提供时设为 true)
|
||||||
|
hideNavigation = false,
|
||||||
}) => {
|
}) => {
|
||||||
const [hierarchy, setHierarchy] = useState([]);
|
const [hierarchy, setHierarchy] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@@ -431,12 +447,40 @@ const HierarchyView = ({
|
|||||||
const [tradeDate, setTradeDate] = useState(null);
|
const [tradeDate, setTradeDate] = useState(null);
|
||||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||||
|
|
||||||
// 钻取状态
|
// 内部钻取状态
|
||||||
const [currentLevel, setCurrentLevel] = useState('lv1');
|
const [internalDrillPath, setInternalDrillPath] = useState(null);
|
||||||
const [currentLv1, setCurrentLv1] = useState(null);
|
|
||||||
const [currentLv2, setCurrentLv2] = useState(null);
|
// 支持受控和非受控模式
|
||||||
const [currentLv3, setCurrentLv3] = useState(null);
|
const drillPath = externalDrillPath !== undefined ? externalDrillPath : internalDrillPath;
|
||||||
const [breadcrumbs, setBreadcrumbs] = useState([{ label: '全部分类', level: 'root' }]);
|
const setDrillPath = onDrillPathChange || setInternalDrillPath;
|
||||||
|
|
||||||
|
// 从 drillPath 派生当前层级和选中项
|
||||||
|
const currentLevel = useMemo(() => {
|
||||||
|
if (!drillPath) return 'lv1';
|
||||||
|
if (drillPath.lv3) return 'concept';
|
||||||
|
if (drillPath.lv2) return 'lv3';
|
||||||
|
if (drillPath.lv1) return 'lv2';
|
||||||
|
return 'lv1';
|
||||||
|
}, [drillPath]);
|
||||||
|
|
||||||
|
const currentLv1 = useMemo(() => drillPath?.lv1 ? { name: drillPath.lv1 } : null, [drillPath]);
|
||||||
|
const currentLv2 = useMemo(() => drillPath?.lv2 ? { name: drillPath.lv2 } : null, [drillPath]);
|
||||||
|
const currentLv3 = useMemo(() => drillPath?.lv3 ? { name: drillPath.lv3 } : null, [drillPath]);
|
||||||
|
|
||||||
|
// 面包屑从 drillPath 派生
|
||||||
|
const breadcrumbs = useMemo(() => {
|
||||||
|
const items = [{ label: '全部分类', level: 'root' }];
|
||||||
|
if (drillPath?.lv1) {
|
||||||
|
items.push({ label: drillPath.lv1, level: 'lv1', data: { name: drillPath.lv1 } });
|
||||||
|
}
|
||||||
|
if (drillPath?.lv2) {
|
||||||
|
items.push({ label: drillPath.lv2, level: 'lv2', data: { name: drillPath.lv2 } });
|
||||||
|
}
|
||||||
|
if (drillPath?.lv3) {
|
||||||
|
items.push({ label: drillPath.lv3, level: 'lv3', data: { name: drillPath.lv3 } });
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}, [drillPath]);
|
||||||
|
|
||||||
const isMobile = useBreakpointValue({ base: true, md: false });
|
const isMobile = useBreakpointValue({ base: true, md: false });
|
||||||
|
|
||||||
@@ -651,67 +695,43 @@ const HierarchyView = ({
|
|||||||
logger.info('HierarchyView', '热力图点击', { level: item.level, name: item.name });
|
logger.info('HierarchyView', '热力图点击', { level: item.level, name: item.name });
|
||||||
|
|
||||||
if (item.level === 'lv1' && item.children && item.children.length > 0) {
|
if (item.level === 'lv1' && item.children && item.children.length > 0) {
|
||||||
setCurrentLevel('lv2');
|
setDrillPath({ lv1: item.name });
|
||||||
setCurrentLv1(item);
|
|
||||||
setBreadcrumbs([
|
|
||||||
{ label: '全部分类', level: 'root' },
|
|
||||||
{ label: item.name, level: 'lv1', data: item },
|
|
||||||
]);
|
|
||||||
} else if (item.level === 'lv2') {
|
} else if (item.level === 'lv2') {
|
||||||
if (item.children && item.children.length > 0) {
|
if ((item.children && item.children.length > 0) || (item.concepts && item.concepts.length > 0)) {
|
||||||
setCurrentLevel('lv3');
|
setDrillPath({ lv1: drillPath?.lv1, lv2: item.name });
|
||||||
setCurrentLv2(item);
|
|
||||||
setBreadcrumbs([
|
|
||||||
{ label: '全部分类', level: 'root' },
|
|
||||||
{ label: currentLv1.name, level: 'lv1', data: currentLv1 },
|
|
||||||
{ label: item.name, level: 'lv2', data: item },
|
|
||||||
]);
|
|
||||||
} else if (item.concepts && item.concepts.length > 0) {
|
|
||||||
setCurrentLevel('lv3');
|
|
||||||
setCurrentLv2(item);
|
|
||||||
setBreadcrumbs([
|
|
||||||
{ label: '全部分类', level: 'root' },
|
|
||||||
{ label: currentLv1.name, level: 'lv1', data: currentLv1 },
|
|
||||||
{ label: item.name, level: 'lv2', data: item },
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
} else if (item.level === 'lv3' && item.concepts && item.concepts.length > 0) {
|
} else if (item.level === 'lv3' && item.concepts && item.concepts.length > 0) {
|
||||||
setCurrentLevel('concept');
|
setDrillPath({ lv1: drillPath?.lv1, lv2: drillPath?.lv2, lv3: item.name });
|
||||||
setCurrentLv3(item);
|
|
||||||
setBreadcrumbs([
|
|
||||||
{ label: '全部分类', level: 'root' },
|
|
||||||
{ label: currentLv1.name, level: 'lv1', data: currentLv1 },
|
|
||||||
{ label: currentLv2.name, level: 'lv2', data: currentLv2 },
|
|
||||||
{ label: item.name, level: 'lv3', data: item },
|
|
||||||
]);
|
|
||||||
} else if (item.level === 'concept') {
|
} else if (item.level === 'concept') {
|
||||||
// 跳转到概念详情页
|
// 跳转到概念详情页
|
||||||
const htmlPath = getConceptHtmlUrl(item.name);
|
const htmlPath = getConceptHtmlUrl(item.name);
|
||||||
window.open(htmlPath, '_blank');
|
window.open(htmlPath, '_blank');
|
||||||
}
|
}
|
||||||
}, [currentLv1, currentLv2]);
|
}, [drillPath, setDrillPath]);
|
||||||
|
|
||||||
// 面包屑导航
|
// 面包屑导航
|
||||||
const handleBreadcrumbClick = useCallback((crumb, index) => {
|
const handleBreadcrumbClick = useCallback((crumb, index) => {
|
||||||
if (crumb.level === 'root') {
|
if (crumb.level === 'root') {
|
||||||
setCurrentLevel('lv1');
|
setDrillPath(null);
|
||||||
setCurrentLv1(null);
|
|
||||||
setCurrentLv2(null);
|
|
||||||
setCurrentLv3(null);
|
|
||||||
setBreadcrumbs([{ label: '全部分类', level: 'root' }]);
|
|
||||||
} else if (crumb.level === 'lv1') {
|
} else if (crumb.level === 'lv1') {
|
||||||
setCurrentLevel('lv2');
|
setDrillPath({ lv1: crumb.data.name });
|
||||||
setCurrentLv1(crumb.data);
|
|
||||||
setCurrentLv2(null);
|
|
||||||
setCurrentLv3(null);
|
|
||||||
setBreadcrumbs(breadcrumbs.slice(0, index + 1));
|
|
||||||
} else if (crumb.level === 'lv2') {
|
} else if (crumb.level === 'lv2') {
|
||||||
setCurrentLevel('lv3');
|
setDrillPath({ lv1: drillPath?.lv1, lv2: crumb.data.name });
|
||||||
setCurrentLv2(crumb.data);
|
|
||||||
setCurrentLv3(null);
|
|
||||||
setBreadcrumbs(breadcrumbs.slice(0, index + 1));
|
|
||||||
}
|
}
|
||||||
}, [breadcrumbs]);
|
}, [drillPath, setDrillPath]);
|
||||||
|
|
||||||
|
// 返回上一层
|
||||||
|
const handleGoBack = useCallback(() => {
|
||||||
|
if (!drillPath) return;
|
||||||
|
|
||||||
|
if (drillPath.lv3) {
|
||||||
|
setDrillPath({ lv1: drillPath.lv1, lv2: drillPath.lv2 });
|
||||||
|
} else if (drillPath.lv2) {
|
||||||
|
setDrillPath({ lv1: drillPath.lv1 });
|
||||||
|
} else if (drillPath.lv1) {
|
||||||
|
setDrillPath(null);
|
||||||
|
}
|
||||||
|
}, [drillPath, setDrillPath]);
|
||||||
|
|
||||||
// 刷新
|
// 刷新
|
||||||
const handleRefreshPrice = useCallback(() => {
|
const handleRefreshPrice = useCallback(() => {
|
||||||
@@ -796,6 +816,22 @@ const HierarchyView = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 当 hideNavigation 为 true 时,只渲染纯卡片网格内容
|
||||||
|
if (hideNavigation) {
|
||||||
|
return (
|
||||||
|
<SimpleGrid columns={getGridColumns()} spacing={{ base: 3, md: 4 }}>
|
||||||
|
{currentData.map((item) => (
|
||||||
|
<GlassCard
|
||||||
|
key={item.id || item.name}
|
||||||
|
item={item}
|
||||||
|
onClick={handleBlockClick}
|
||||||
|
size={currentLevel === 'lv1' ? 'large' : currentLevel === 'concept' ? 'small' : 'normal'}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</SimpleGrid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
position={isFullscreen ? 'fixed' : 'relative'}
|
position={isFullscreen ? 'fixed' : 'relative'}
|
||||||
@@ -817,105 +853,92 @@ const HierarchyView = ({
|
|||||||
{/* 内容层 */}
|
{/* 内容层 */}
|
||||||
<Box position="relative" zIndex={1}>
|
<Box position="relative" zIndex={1}>
|
||||||
{/* 面包屑导航 + 工具栏(同一行) */}
|
{/* 面包屑导航 + 工具栏(同一行) */}
|
||||||
<Flex
|
<HStack
|
||||||
align="center"
|
|
||||||
justify="space-between"
|
|
||||||
mb={5}
|
mb={5}
|
||||||
p={3}
|
spacing={2}
|
||||||
bg="whiteAlpha.50"
|
justify="space-between"
|
||||||
backdropFilter={GLASS_BLUR.lg}
|
|
||||||
borderRadius="2xl"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor="whiteAlpha.100"
|
|
||||||
gap={2}
|
|
||||||
>
|
>
|
||||||
{/* 左侧:面包屑导航 */}
|
{/* 左侧:返回按钮 + 面包屑导航 */}
|
||||||
<Flex align="center" flexWrap="wrap" gap={1} flex={1}>
|
<HStack spacing={2}>
|
||||||
{breadcrumbs.map((crumb, index) => (
|
{drillPath && (
|
||||||
<React.Fragment key={index}>
|
<Tooltip label="返回上一层">
|
||||||
{index > 0 && (
|
<IconButton
|
||||||
<Icon as={ChevronRight} color="whiteAlpha.400" boxSize={3} mx={1} />
|
icon={<ArrowLeft size={16} />}
|
||||||
)}
|
|
||||||
<Button
|
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
bg={index === breadcrumbs.length - 1 ? 'purple.500' : 'transparent'}
|
onClick={handleGoBack}
|
||||||
color={index === breadcrumbs.length - 1 ? 'white' : 'whiteAlpha.700'}
|
bg="rgba(255, 255, 255, 0.08)"
|
||||||
leftIcon={index === 0 ? <Home /> : undefined}
|
backdropFilter={GLASS_BLUR.lg}
|
||||||
onClick={() => handleBreadcrumbClick(crumb, index)}
|
border="1px solid"
|
||||||
isDisabled={index === breadcrumbs.length - 1}
|
borderColor="whiteAlpha.100"
|
||||||
fontWeight={index === breadcrumbs.length - 1 ? 'bold' : 'medium'}
|
color="whiteAlpha.800"
|
||||||
borderRadius="xl"
|
borderRadius="full"
|
||||||
_hover={index !== breadcrumbs.length - 1 ? { bg: 'whiteAlpha.100' } : {}}
|
_hover={{
|
||||||
boxShadow={index === breadcrumbs.length - 1 ? '0 0 20px rgba(139, 92, 246, 0.5)' : 'none'}
|
bg: 'rgba(255, 255, 255, 0.15)',
|
||||||
>
|
transform: 'scale(1.05)',
|
||||||
{crumb.label}
|
}}
|
||||||
</Button>
|
transition="all 0.2s"
|
||||||
</React.Fragment>
|
/>
|
||||||
))}
|
</Tooltip>
|
||||||
</Flex>
|
|
||||||
|
|
||||||
{/* 右侧:工具栏按钮 */}
|
|
||||||
<HStack spacing={2} flexShrink={0}>
|
|
||||||
{priceLoading && (
|
|
||||||
<Spinner size="sm" color="purple.300" />
|
|
||||||
)}
|
)}
|
||||||
<Tooltip label="刷新涨跌幅" placement="top">
|
<HStack
|
||||||
<IconButton
|
bg="rgba(255, 255, 255, 0.08)"
|
||||||
size="sm"
|
backdropFilter={GLASS_BLUR.lg}
|
||||||
icon={<RefreshCw />}
|
px={3}
|
||||||
onClick={handleRefreshPrice}
|
py={1.5}
|
||||||
isLoading={priceLoading}
|
borderRadius="full"
|
||||||
bg="whiteAlpha.100"
|
border="1px solid"
|
||||||
color="white"
|
borderColor="whiteAlpha.100"
|
||||||
border="1px solid"
|
spacing={1}
|
||||||
borderColor="whiteAlpha.200"
|
boxShadow="0 4px 16px rgba(0, 0, 0, 0.2)"
|
||||||
_hover={{ bg: 'whiteAlpha.200' }}
|
>
|
||||||
aria-label="刷新涨跌幅"
|
{breadcrumbs.map((crumb, index) => (
|
||||||
/>
|
<React.Fragment key={index}>
|
||||||
</Tooltip>
|
{index > 0 && (
|
||||||
|
<Icon as={ChevronRight} boxSize={3} color="whiteAlpha.400" />
|
||||||
|
)}
|
||||||
|
<Text
|
||||||
|
fontSize="xs"
|
||||||
|
color={index === breadcrumbs.length - 1 ? 'purple.300' : 'whiteAlpha.700'}
|
||||||
|
fontWeight={index === breadcrumbs.length - 1 ? 'bold' : 'normal'}
|
||||||
|
cursor={index < breadcrumbs.length - 1 ? 'pointer' : 'default'}
|
||||||
|
_hover={index < breadcrumbs.length - 1 ? { color: 'purple.300' } : {}}
|
||||||
|
onClick={() => {
|
||||||
|
if (index < breadcrumbs.length - 1) {
|
||||||
|
handleBreadcrumbClick(crumb, index);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
transition="color 0.2s"
|
||||||
|
>
|
||||||
|
{crumb.label}
|
||||||
|
</Text>
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</HStack>
|
||||||
|
</HStack>
|
||||||
|
|
||||||
<Tooltip label={isFullscreen ? '退出全屏' : '全屏'} placement="top">
|
{/* 右侧:全屏按钮 */}
|
||||||
<IconButton
|
<Tooltip label={isFullscreen ? '退出全屏' : '全屏查看'}>
|
||||||
size="sm"
|
<IconButton
|
||||||
icon={isFullscreen ? <Minimize2 /> : <Maximize2 />}
|
icon={isFullscreen ? <Minimize2 size={16} /> : <Maximize2 size={16} />}
|
||||||
onClick={toggleFullscreen}
|
size="sm"
|
||||||
bg="whiteAlpha.100"
|
variant="ghost"
|
||||||
color="white"
|
onClick={toggleFullscreen}
|
||||||
border="1px solid"
|
bg="rgba(255, 255, 255, 0.08)"
|
||||||
borderColor="whiteAlpha.200"
|
backdropFilter={GLASS_BLUR.lg}
|
||||||
_hover={{ bg: 'whiteAlpha.200' }}
|
border="1px solid"
|
||||||
aria-label={isFullscreen ? '退出全屏' : '全屏'}
|
borderColor="whiteAlpha.100"
|
||||||
/>
|
color="whiteAlpha.800"
|
||||||
</Tooltip>
|
borderRadius="full"
|
||||||
</HStack>
|
_hover={{
|
||||||
</Flex>
|
bg: 'rgba(255, 255, 255, 0.15)',
|
||||||
|
transform: 'scale(1.05)',
|
||||||
{/* 图例说明 */}
|
}}
|
||||||
<Flex
|
transition="all 0.2s"
|
||||||
justify="center"
|
aria-label={isFullscreen ? '退出全屏' : '全屏'}
|
||||||
mb={5}
|
/>
|
||||||
gap={4}
|
</Tooltip>
|
||||||
flexWrap="wrap"
|
</HStack>
|
||||||
fontSize="xs"
|
|
||||||
>
|
|
||||||
<HStack spacing={2}>
|
|
||||||
<Box w={3} h={3} borderRadius="sm" bg="linear-gradient(135deg, rgba(239, 68, 68, 0.7) 0%, rgba(248, 113, 113, 0.5) 100%)" />
|
|
||||||
<Text color="whiteAlpha.600">涨</Text>
|
|
||||||
</HStack>
|
|
||||||
<HStack spacing={2}>
|
|
||||||
<Box w={3} h={3} borderRadius="sm" bg="linear-gradient(135deg, rgba(22, 163, 74, 0.7) 0%, rgba(74, 222, 128, 0.5) 100%)" />
|
|
||||||
<Text color="whiteAlpha.600">跌</Text>
|
|
||||||
</HStack>
|
|
||||||
<HStack spacing={2}>
|
|
||||||
<Box w={3} h={3} borderRadius="sm" bg="linear-gradient(135deg, rgba(71, 85, 105, 0.6) 0%, rgba(100, 116, 139, 0.4) 100%)" />
|
|
||||||
<Text color="whiteAlpha.600">平/无数据</Text>
|
|
||||||
</HStack>
|
|
||||||
<Text color="whiteAlpha.300">|</Text>
|
|
||||||
<Text color="whiteAlpha.500">
|
|
||||||
{currentLevel !== 'concept' ? '点击色块查看下级' : '点击查看概念详情'}
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
{/* 热力图网格 */}
|
{/* 热力图网格 */}
|
||||||
<SimpleGrid columns={getGridColumns()} spacing={{ base: 3, md: 4 }}>
|
<SimpleGrid columns={getGridColumns()} spacing={{ base: 3, md: 4 }}>
|
||||||
@@ -929,6 +952,47 @@ const HierarchyView = ({
|
|||||||
))}
|
))}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
|
|
||||||
|
{/* 底部图例 */}
|
||||||
|
<Flex
|
||||||
|
mt={5}
|
||||||
|
justify="center"
|
||||||
|
gap={4}
|
||||||
|
>
|
||||||
|
<HStack
|
||||||
|
bg="rgba(255, 255, 255, 0.08)"
|
||||||
|
backdropFilter={GLASS_BLUR.lg}
|
||||||
|
px={4}
|
||||||
|
py={2}
|
||||||
|
borderRadius="full"
|
||||||
|
border="1px solid"
|
||||||
|
borderColor="whiteAlpha.100"
|
||||||
|
spacing={3}
|
||||||
|
boxShadow="0 4px 16px rgba(0, 0, 0, 0.2)"
|
||||||
|
>
|
||||||
|
<HStack spacing={2}>
|
||||||
|
<Box
|
||||||
|
w={3}
|
||||||
|
h={3}
|
||||||
|
borderRadius="full"
|
||||||
|
bg="linear-gradient(135deg, #EF4444, #DC2626)"
|
||||||
|
boxShadow="0 0 8px rgba(239, 68, 68, 0.5)"
|
||||||
|
/>
|
||||||
|
<Text color="whiteAlpha.800" fontSize="xs">涨</Text>
|
||||||
|
</HStack>
|
||||||
|
<Box w="1px" h={4} bg="whiteAlpha.200" />
|
||||||
|
<HStack spacing={2}>
|
||||||
|
<Box
|
||||||
|
w={3}
|
||||||
|
h={3}
|
||||||
|
borderRadius="full"
|
||||||
|
bg="linear-gradient(135deg, #22C55E, #16A34A)"
|
||||||
|
boxShadow="0 0 8px rgba(34, 197, 94, 0.5)"
|
||||||
|
/>
|
||||||
|
<Text color="whiteAlpha.800" fontSize="xs">跌</Text>
|
||||||
|
</HStack>
|
||||||
|
</HStack>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user