feat(Concept): 统一图表容器与共享导航状态
- 使用 ChartContainer 包裹矩形树图和层级图 - 添加 chartDrillPath 共享状态,Tab 切换时导航保持 - 矩形树图/层级图使用不同 contentTopPadding 配置 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import { useSearchParams, useNavigate } from 'react-router-dom';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { getConceptHtmlUrl } from '../../utils/textUtils';
|
||||
@@ -116,6 +116,7 @@ import {
|
||||
Network,
|
||||
TrendingUp,
|
||||
Zap,
|
||||
RefreshCw,
|
||||
} from 'lucide-react';
|
||||
import { keyframes } from '@emotion/react';
|
||||
import ConceptTimelineModal from './ConceptTimelineModal';
|
||||
@@ -123,6 +124,7 @@ import ConceptStatsPanel from './components/ConceptStatsPanel';
|
||||
import HierarchyView from './components/HierarchyView';
|
||||
import ForceGraphView from './components/ForceGraphView';
|
||||
import BreadcrumbNav from './components/BreadcrumbNav';
|
||||
import ChartContainer from './components/ChartContainer';
|
||||
import ConceptStocksModal from '@components/ConceptStocksModal';
|
||||
import TradeDatePicker from '@components/TradeDatePicker';
|
||||
// 导航栏已由 MainLayout 提供,无需在此导入
|
||||
@@ -367,6 +369,9 @@ const ConceptCenter = () => {
|
||||
const [totalPages, setTotalPages] = useState(1);
|
||||
const [viewMode, setViewMode] = useState('list'); // 默认列表视图
|
||||
|
||||
// 图表共享的钻取路径状态(tab 切换时保持)
|
||||
const [chartDrillPath, setChartDrillPath] = useState(null);
|
||||
|
||||
// 层级筛选状态
|
||||
const [hierarchyFilter, setHierarchyFilter] = useState({ lv1: null, lv2: null, lv3: null });
|
||||
|
||||
@@ -590,6 +595,41 @@ const ConceptCenter = () => {
|
||||
fetchConcepts(searchQuery, 1, selectedDate, sortBy, filter);
|
||||
}, [searchQuery, selectedDate, sortBy, updateUrlParams, fetchConcepts]);
|
||||
|
||||
// 图表面包屑导航项(从 chartDrillPath 派生)
|
||||
const chartBreadcrumbItems = useMemo(() => {
|
||||
const items = [{ label: '全部分类', path: null }];
|
||||
|
||||
if (chartDrillPath?.lv1) {
|
||||
items.push({ label: chartDrillPath.lv1, path: { lv1: chartDrillPath.lv1 } });
|
||||
}
|
||||
if (chartDrillPath?.lv2) {
|
||||
items.push({ label: chartDrillPath.lv2, path: { lv1: chartDrillPath.lv1, lv2: chartDrillPath.lv2 } });
|
||||
}
|
||||
if (chartDrillPath?.lv3) {
|
||||
items.push({ label: chartDrillPath.lv3, path: { lv1: chartDrillPath.lv1, lv2: chartDrillPath.lv2, lv3: chartDrillPath.lv3 } });
|
||||
}
|
||||
|
||||
return items;
|
||||
}, [chartDrillPath]);
|
||||
|
||||
// 图表面包屑导航回调
|
||||
const handleChartNavigate = useCallback((path) => {
|
||||
setChartDrillPath(path);
|
||||
}, []);
|
||||
|
||||
// 图表返回上一层
|
||||
const handleChartGoBack = useCallback(() => {
|
||||
if (!chartDrillPath) return;
|
||||
|
||||
if (chartDrillPath.lv3) {
|
||||
setChartDrillPath({ lv1: chartDrillPath.lv1, lv2: chartDrillPath.lv2 });
|
||||
} else if (chartDrillPath.lv2) {
|
||||
setChartDrillPath({ lv1: chartDrillPath.lv1 });
|
||||
} else if (chartDrillPath.lv1) {
|
||||
setChartDrillPath(null);
|
||||
}
|
||||
}, [chartDrillPath]);
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = () => {
|
||||
setCurrentPage(1);
|
||||
@@ -1437,8 +1477,8 @@ const ConceptCenter = () => {
|
||||
</Card>
|
||||
);
|
||||
|
||||
// 日期选择组件 - 深色主题
|
||||
const DateSelector = () => (
|
||||
// 统一控制栏组件 - 日期 + 排序 + 视图切换(单行布局)
|
||||
const ControlBar = () => (
|
||||
<Box
|
||||
p={4}
|
||||
bg="rgba(15, 23, 42, 0.8)"
|
||||
@@ -1448,12 +1488,46 @@ const ConceptCenter = () => {
|
||||
borderColor="whiteAlpha.100"
|
||||
boxShadow="0 4px 20px rgba(0, 0, 0, 0.3)"
|
||||
>
|
||||
<Flex
|
||||
direction={{ base: 'column', lg: 'row' }}
|
||||
align={{ base: 'stretch', lg: 'center' }}
|
||||
gap={4}
|
||||
<Flex justify="space-between" align="center" wrap="wrap" gap={4}>
|
||||
{/* 左侧:日期选择区 */}
|
||||
<HStack spacing={3} flexWrap="wrap">
|
||||
<ButtonGroup size="sm" spacing={1}>
|
||||
{[
|
||||
{ label: '今天', days: 0 },
|
||||
{ label: '昨天', days: 1 },
|
||||
{ label: '一周前', days: 7 },
|
||||
{ label: '一月前', days: 30 },
|
||||
].map(({ label, days }) => {
|
||||
// 判断是否选中当前按钮
|
||||
const targetDate = new Date();
|
||||
targetDate.setDate(targetDate.getDate() - days);
|
||||
const isSelected = selectedDate &&
|
||||
selectedDate.toDateString() === targetDate.toDateString();
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={days}
|
||||
onClick={() => handleQuickDateSelect(days)}
|
||||
bg={isSelected ? 'purple.500' : 'whiteAlpha.100'}
|
||||
color="white"
|
||||
borderRadius="full"
|
||||
border="1px solid"
|
||||
borderColor={isSelected ? 'purple.500' : 'whiteAlpha.200'}
|
||||
px={3}
|
||||
size="sm"
|
||||
boxShadow={isSelected ? '0 0 12px rgba(139, 92, 246, 0.4)' : 'none'}
|
||||
_hover={{
|
||||
bg: 'purple.500',
|
||||
borderColor: 'purple.500',
|
||||
boxShadow: '0 0 12px rgba(139, 92, 246, 0.4)',
|
||||
}}
|
||||
transition="all 0.2s"
|
||||
>
|
||||
{/* 使用通用日期选择器组件 - 不显示最新日期提示,由下方单独渲染 */}
|
||||
{label}
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</ButtonGroup>
|
||||
<TradeDatePicker
|
||||
value={selectedDate}
|
||||
onChange={(date) => {
|
||||
@@ -1466,101 +1540,135 @@ const ConceptCenter = () => {
|
||||
fetchConcepts(searchQuery, 1, date, sortBy);
|
||||
}}
|
||||
latestTradeDate={latestTradeDate}
|
||||
label="交易日期"
|
||||
label="更多"
|
||||
showIcon={false}
|
||||
showLatestTradeDateTip={false}
|
||||
inputWidth="130px"
|
||||
size="sm"
|
||||
labelColor="white"
|
||||
/>
|
||||
|
||||
{/* 快捷按钮 - 紧跟日期选择器 */}
|
||||
<ButtonGroup size="sm" flexWrap="wrap" spacing={2}>
|
||||
<Button
|
||||
onClick={() => handleQuickDateSelect(0)}
|
||||
bg="whiteAlpha.100"
|
||||
color="white"
|
||||
borderRadius="full"
|
||||
border="1px solid"
|
||||
borderColor="whiteAlpha.200"
|
||||
px={4}
|
||||
_hover={{
|
||||
bg: 'purple.500',
|
||||
borderColor: 'purple.500',
|
||||
boxShadow: '0 0 12px rgba(139, 92, 246, 0.4)',
|
||||
}}
|
||||
transition="all 0.2s"
|
||||
>
|
||||
今天
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleQuickDateSelect(1)}
|
||||
bg="whiteAlpha.100"
|
||||
color="white"
|
||||
borderRadius="full"
|
||||
border="1px solid"
|
||||
borderColor="whiteAlpha.200"
|
||||
px={4}
|
||||
_hover={{
|
||||
bg: 'purple.500',
|
||||
borderColor: 'purple.500',
|
||||
boxShadow: '0 0 12px rgba(139, 92, 246, 0.4)',
|
||||
}}
|
||||
transition="all 0.2s"
|
||||
>
|
||||
昨天
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleQuickDateSelect(7)}
|
||||
bg="whiteAlpha.100"
|
||||
color="white"
|
||||
borderRadius="full"
|
||||
border="1px solid"
|
||||
borderColor="whiteAlpha.200"
|
||||
px={4}
|
||||
_hover={{
|
||||
bg: 'purple.500',
|
||||
borderColor: 'purple.500',
|
||||
boxShadow: '0 0 12px rgba(139, 92, 246, 0.4)',
|
||||
}}
|
||||
transition="all 0.2s"
|
||||
>
|
||||
一周前
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleQuickDateSelect(30)}
|
||||
bg="whiteAlpha.100"
|
||||
color="white"
|
||||
borderRadius="full"
|
||||
border="1px solid"
|
||||
borderColor="whiteAlpha.200"
|
||||
px={4}
|
||||
_hover={{
|
||||
bg: 'purple.500',
|
||||
borderColor: 'purple.500',
|
||||
boxShadow: '0 0 12px rgba(139, 92, 246, 0.4)',
|
||||
}}
|
||||
transition="all 0.2s"
|
||||
>
|
||||
一月前
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
||||
{/* 最新交易日期提示 - 靠右显示 */}
|
||||
{/* 数据更新提示 */}
|
||||
{latestTradeDate && (
|
||||
<Tooltip label="数据库中最新的交易日期">
|
||||
<HStack
|
||||
spacing={1.5}
|
||||
ml="auto"
|
||||
px={2}
|
||||
py={1}
|
||||
opacity={0.7}
|
||||
_hover={{ opacity: 1 }}
|
||||
transition="opacity 0.2s"
|
||||
>
|
||||
<Icon as={Info} color="blue.300" boxSize={3} />
|
||||
<Text fontSize="xs" color="blue.200">
|
||||
数据更新至 {latestTradeDate.toLocaleDateString('zh-CN')}
|
||||
<Text fontSize="xs" color="red.400" whiteSpace="nowrap">
|
||||
更新至 {latestTradeDate.toLocaleDateString('zh-CN')}
|
||||
</Text>
|
||||
</HStack>
|
||||
</Tooltip>
|
||||
)}
|
||||
</HStack>
|
||||
|
||||
{/* 右侧:刷新 + 排序 + 视图切换 */}
|
||||
<HStack spacing={4} align="center" flexWrap="wrap">
|
||||
{/* 刷新按钮 */}
|
||||
<Tooltip label="刷新数据" placement="top">
|
||||
<IconButton
|
||||
size="sm"
|
||||
icon={<RefreshCw size={16} />}
|
||||
onClick={() => fetchConcepts(searchQuery, currentPage, selectedDate, sortBy)}
|
||||
bg="whiteAlpha.100"
|
||||
color="white"
|
||||
border="1px solid"
|
||||
borderColor="whiteAlpha.200"
|
||||
borderRadius="full"
|
||||
_hover={{
|
||||
bg: 'whiteAlpha.200',
|
||||
borderColor: 'whiteAlpha.300',
|
||||
}}
|
||||
transition="all 0.2s"
|
||||
aria-label="刷新"
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
{/* 排序下拉框 - 仅列表视图显示 */}
|
||||
{viewMode === 'list' && (
|
||||
<HStack spacing={2}>
|
||||
<Text fontSize="sm" color="whiteAlpha.600" whiteSpace="nowrap">排序</Text>
|
||||
<Select
|
||||
value={sortBy}
|
||||
onChange={(e) => handleSortChange(e.target.value)}
|
||||
width="120px"
|
||||
size="sm"
|
||||
focusBorderColor="purple.400"
|
||||
borderColor="whiteAlpha.300"
|
||||
borderRadius="lg"
|
||||
fontWeight="medium"
|
||||
color="white"
|
||||
bg="whiteAlpha.50"
|
||||
_hover={{ borderColor: 'purple.400' }}
|
||||
sx={{
|
||||
option: {
|
||||
bg: 'gray.800',
|
||||
color: 'white',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<option value="change_pct">涨跌幅</option>
|
||||
<option value="_score">相关度</option>
|
||||
<option value="stock_count">股票数量</option>
|
||||
<option value="outbreak_date">爆发日期</option>
|
||||
</Select>
|
||||
</HStack>
|
||||
)}
|
||||
|
||||
{/* 视图切换按钮组 */}
|
||||
<ButtonGroup size="sm" isAttached variant="outline">
|
||||
<Tooltip label="矩形树图" placement="top">
|
||||
<IconButton
|
||||
icon={<BoxIcon size={16} />}
|
||||
onClick={() => {
|
||||
if (viewMode !== 'force3d') {
|
||||
trackViewModeChanged('force3d', viewMode);
|
||||
setViewMode('force3d');
|
||||
}
|
||||
}}
|
||||
bg={viewMode === 'force3d' ? 'purple.500' : 'transparent'}
|
||||
color={viewMode === 'force3d' ? 'white' : 'whiteAlpha.700'}
|
||||
borderColor="whiteAlpha.300"
|
||||
_hover={{
|
||||
bg: viewMode === 'force3d' ? 'purple.400' : 'whiteAlpha.100',
|
||||
boxShadow: viewMode === 'force3d' ? '0 0 10px rgba(139, 92, 246, 0.4)' : 'none',
|
||||
}}
|
||||
aria-label="矩形树图"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip label="层级图" placement="top">
|
||||
<IconButton
|
||||
icon={<Network />}
|
||||
onClick={() => {
|
||||
if (viewMode !== 'hierarchy') {
|
||||
trackViewModeChanged('hierarchy', viewMode);
|
||||
setViewMode('hierarchy');
|
||||
}
|
||||
}}
|
||||
bg={viewMode === 'hierarchy' ? 'purple.500' : 'transparent'}
|
||||
color={viewMode === 'hierarchy' ? 'white' : 'whiteAlpha.700'}
|
||||
borderColor="whiteAlpha.300"
|
||||
_hover={{
|
||||
bg: viewMode === 'hierarchy' ? 'purple.400' : 'whiteAlpha.100',
|
||||
boxShadow: viewMode === 'hierarchy' ? '0 0 10px rgba(139, 92, 246, 0.4)' : 'none',
|
||||
}}
|
||||
aria-label="层级图"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip label="列表视图" placement="top">
|
||||
<IconButton
|
||||
icon={<List />}
|
||||
onClick={() => {
|
||||
if (viewMode !== 'list') {
|
||||
trackViewModeChanged('list', viewMode);
|
||||
setViewMode('list');
|
||||
}
|
||||
}}
|
||||
bg={viewMode === 'list' ? 'purple.500' : 'transparent'}
|
||||
color={viewMode === 'list' ? 'white' : 'whiteAlpha.700'}
|
||||
borderColor="whiteAlpha.300"
|
||||
_hover={{
|
||||
bg: viewMode === 'list' ? 'purple.400' : 'whiteAlpha.100',
|
||||
boxShadow: viewMode === 'list' ? '0 0 10px rgba(139, 92, 246, 0.4)' : 'none',
|
||||
}}
|
||||
aria-label="列表视图"
|
||||
/>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
</HStack>
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
@@ -1626,144 +1734,18 @@ const ConceptCenter = () => {
|
||||
}
|
||||
/>
|
||||
|
||||
{/* 主内容区域 - padding 由 MainLayout 统一设置 */}
|
||||
<Box py={10} position="relative" zIndex={1}>
|
||||
{/* 主内容区域 - 左右两栏布局 */}
|
||||
<Box pt={0} pb={10} position="relative" zIndex={1}>
|
||||
<Flex gap={6} direction={{ base: 'column', xl: 'row' }} align="flex-start">
|
||||
{/* 左侧:控制栏 + 概念内容 */}
|
||||
<Box flex="1" minW={0}>
|
||||
{/* 控制栏 */}
|
||||
<Box mb={6}>
|
||||
<DateSelector />
|
||||
<ControlBar />
|
||||
</Box>
|
||||
|
||||
{/* 双栏布局:左侧概念卡片,右侧统计面板 */}
|
||||
<Flex gap={8} direction={{ base: 'column', xl: 'row' }}>
|
||||
{/* 左侧概念卡片区域 */}
|
||||
<Box flex={1}>
|
||||
|
||||
<Card
|
||||
mb={8}
|
||||
bg="rgba(15, 23, 42, 0.8)"
|
||||
backdropFilter={GLASS_BLUR.lg}
|
||||
border="1px solid"
|
||||
borderColor="whiteAlpha.100"
|
||||
boxShadow="0 4px 20px rgba(0, 0, 0, 0.3)"
|
||||
borderRadius="2xl"
|
||||
>
|
||||
<CardBody>
|
||||
<Flex
|
||||
direction={{ base: 'column', md: 'row' }}
|
||||
justify="space-between"
|
||||
align={{ base: 'stretch', md: 'center' }}
|
||||
gap={4}
|
||||
>
|
||||
{/* 排序方式 - 仅在列表视图显示 */}
|
||||
{viewMode === 'list' && (
|
||||
<HStack spacing={4} align="center">
|
||||
<Icon as={Tags} boxSize={4} color="purple.300" />
|
||||
<Text fontWeight="bold" color="white">排序方式:</Text>
|
||||
<Select
|
||||
value={sortBy}
|
||||
onChange={(e) => handleSortChange(e.target.value)}
|
||||
width="200px"
|
||||
focusBorderColor="purple.400"
|
||||
borderColor="whiteAlpha.300"
|
||||
borderRadius="lg"
|
||||
fontWeight="medium"
|
||||
color="white"
|
||||
bg="whiteAlpha.50"
|
||||
_hover={{ borderColor: 'purple.400' }}
|
||||
sx={{
|
||||
option: {
|
||||
bg: 'gray.800',
|
||||
color: 'white',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<option value="change_pct">涨跌幅</option>
|
||||
<option value="_score">相关度</option>
|
||||
<option value="stock_count">股票数量</option>
|
||||
<option value="outbreak_date">爆发日期</option>
|
||||
</Select>
|
||||
{searchQuery && sortBy === '_score' && (
|
||||
<Tooltip label="搜索时自动切换到相关度排序,以显示最匹配的结果。您也可以手动切换其他排序方式。">
|
||||
<HStack
|
||||
spacing={1}
|
||||
bg="blue.500"
|
||||
px={3}
|
||||
py={1}
|
||||
borderRadius="full"
|
||||
boxShadow="0 0 10px rgba(59, 130, 246, 0.4)"
|
||||
>
|
||||
<Icon as={Info} color="white" boxSize={3} />
|
||||
<Text fontSize="xs" color="white" fontWeight="medium">
|
||||
智能排序
|
||||
</Text>
|
||||
</HStack>
|
||||
</Tooltip>
|
||||
)}
|
||||
</HStack>
|
||||
)}
|
||||
|
||||
<ButtonGroup size="sm" isAttached variant="outline" ml={viewMode !== 'list' ? 'auto' : undefined}>
|
||||
<Tooltip label="概念矩形树图" placement="top">
|
||||
<IconButton
|
||||
icon={<BoxIcon size={16} />}
|
||||
onClick={() => {
|
||||
if (viewMode !== 'force3d') {
|
||||
trackViewModeChanged('force3d', viewMode);
|
||||
setViewMode('force3d');
|
||||
}
|
||||
}}
|
||||
bg={viewMode === 'force3d' ? 'purple.500' : 'transparent'}
|
||||
color={viewMode === 'force3d' ? 'white' : 'whiteAlpha.700'}
|
||||
borderColor="whiteAlpha.300"
|
||||
_hover={{
|
||||
bg: viewMode === 'force3d' ? 'purple.400' : 'whiteAlpha.100',
|
||||
boxShadow: viewMode === 'force3d' ? '0 0 10px rgba(139, 92, 246, 0.4)' : 'none',
|
||||
}}
|
||||
aria-label="概念矩形树图"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip label="层级图" placement="top">
|
||||
<IconButton
|
||||
icon={<Network />}
|
||||
onClick={() => {
|
||||
if (viewMode !== 'hierarchy') {
|
||||
trackViewModeChanged('hierarchy', viewMode);
|
||||
setViewMode('hierarchy');
|
||||
}
|
||||
}}
|
||||
bg={viewMode === 'hierarchy' ? 'purple.500' : 'transparent'}
|
||||
color={viewMode === 'hierarchy' ? 'white' : 'whiteAlpha.700'}
|
||||
borderColor="whiteAlpha.300"
|
||||
_hover={{
|
||||
bg: viewMode === 'hierarchy' ? 'purple.400' : 'whiteAlpha.100',
|
||||
boxShadow: viewMode === 'hierarchy' ? '0 0 10px rgba(139, 92, 246, 0.4)' : 'none',
|
||||
}}
|
||||
aria-label="层级图"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip label="列表视图" placement="top">
|
||||
<IconButton
|
||||
icon={<List />}
|
||||
onClick={() => {
|
||||
if (viewMode !== 'list') {
|
||||
trackViewModeChanged('list', viewMode);
|
||||
setViewMode('list');
|
||||
}
|
||||
}}
|
||||
bg={viewMode === 'list' ? 'purple.500' : 'transparent'}
|
||||
color={viewMode === 'list' ? 'white' : 'whiteAlpha.700'}
|
||||
borderColor="whiteAlpha.300"
|
||||
_hover={{
|
||||
bg: viewMode === 'list' ? 'purple.400' : 'whiteAlpha.100',
|
||||
boxShadow: viewMode === 'list' ? '0 0 10px rgba(139, 92, 246, 0.4)' : 'none',
|
||||
}}
|
||||
aria-label="列表视图"
|
||||
/>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
{/* 概念内容区域 */}
|
||||
<Box>
|
||||
{/* 面包屑导航 - 显示当前层级筛选 */}
|
||||
<BreadcrumbNav
|
||||
filter={hierarchyFilter}
|
||||
@@ -1772,32 +1754,36 @@ const ConceptCenter = () => {
|
||||
isDarkMode={true}
|
||||
/>
|
||||
|
||||
{selectedDate && viewMode !== 'hierarchy' && viewMode !== 'force3d' && (
|
||||
<Box mb={4} p={3} bg="rgba(59, 130, 246, 0.2)" borderRadius="xl" borderLeft="4px solid" borderColor="blue.400">
|
||||
<HStack>
|
||||
<Icon as={Info} color="blue.300" />
|
||||
<Text fontSize="sm" color="whiteAlpha.800">
|
||||
当前显示 <Text as="strong" color="cyan.300">{selectedDate.toLocaleDateString('zh-CN')}</Text> 的概念涨跌幅数据
|
||||
{searchQuery && <span>,搜索词:<Text as="strong" color="cyan.300">"{searchQuery}"</Text></span>}
|
||||
</Text>
|
||||
</HStack>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* 3D 力导向图视图 */}
|
||||
{/* 矩形树图 / 层级图视图 - 使用统一容器 */}
|
||||
{(viewMode === 'force3d' || viewMode === 'hierarchy') ? (
|
||||
<ChartContainer
|
||||
breadcrumbItems={chartBreadcrumbItems}
|
||||
onNavigate={handleChartNavigate}
|
||||
onGoBack={handleChartGoBack}
|
||||
showBackButton={!!chartDrillPath}
|
||||
showLegend={viewMode === 'hierarchy'}
|
||||
contentTopPadding={viewMode === 'force3d' ? 0 : 14}
|
||||
>
|
||||
{viewMode === 'force3d' ? (
|
||||
<ForceGraphView
|
||||
apiBaseUrl={API_BASE_URL}
|
||||
onSelectCategory={handleHierarchySelect}
|
||||
selectedDate={selectedDate}
|
||||
externalDrillPath={chartDrillPath}
|
||||
onDrillPathChange={setChartDrillPath}
|
||||
hideNavigation={true}
|
||||
/>
|
||||
) : /* 层级图视图 */
|
||||
viewMode === 'hierarchy' ? (
|
||||
) : (
|
||||
<HierarchyView
|
||||
apiBaseUrl={API_BASE_URL}
|
||||
onSelectCategory={handleHierarchySelect}
|
||||
selectedDate={selectedDate}
|
||||
externalDrillPath={chartDrillPath}
|
||||
onDrillPathChange={setChartDrillPath}
|
||||
hideNavigation={true}
|
||||
/>
|
||||
)}
|
||||
</ChartContainer>
|
||||
) : loading ? (
|
||||
<SimpleGrid columns={{ base: 2, md: 2, lg: 3 }} spacing={{ base: 3, md: 6 }}>
|
||||
{[...Array(12)].map((_, i) => (
|
||||
@@ -1934,10 +1920,10 @@ const ConceptCenter = () => {
|
||||
</Center>
|
||||
) : null}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* 右侧统计面板 */}
|
||||
{/* 右侧:概念统计中心 */}
|
||||
<Box w={{ base: '100%', xl: '400px' }} flexShrink={0}>
|
||||
<Box position="sticky" top={6}>
|
||||
{hasFeatureAccess('concept_stats_panel') ? (
|
||||
<ConceptStatsPanel
|
||||
apiBaseUrl={API_BASE_URL}
|
||||
@@ -1952,35 +1938,17 @@ const ConceptCenter = () => {
|
||||
borderColor="whiteAlpha.100"
|
||||
borderRadius="2xl"
|
||||
>
|
||||
<CardBody p={6}>
|
||||
<VStack spacing={4} textAlign="center">
|
||||
<Icon as={LineChart} boxSize={12} color="whiteAlpha.300" />
|
||||
<VStack spacing={2}>
|
||||
<Heading size="md" color="white">
|
||||
概念统计中心
|
||||
</Heading>
|
||||
<CardBody p={4}>
|
||||
<HStack spacing={3}>
|
||||
<Icon as={LineChart} boxSize={6} color="whiteAlpha.300" />
|
||||
<Text fontSize="sm" color="whiteAlpha.600">
|
||||
此功能需要Pro版订阅才能使用
|
||||
概念统计中心需要Pro版订阅
|
||||
</Text>
|
||||
</VStack>
|
||||
<Button
|
||||
bg="blue.500"
|
||||
color="white"
|
||||
leftIcon={<Icon as={Rocket} />}
|
||||
onClick={() => {
|
||||
setUpgradeFeature('pro');
|
||||
setUpgradeModalOpen(true);
|
||||
}}
|
||||
_hover={{ bg: 'blue.400', boxShadow: '0 0 15px rgba(59, 130, 246, 0.5)' }}
|
||||
>
|
||||
升级到Pro版
|
||||
</Button>
|
||||
</VStack>
|
||||
</HStack>
|
||||
</CardBody>
|
||||
</Card>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user