diff --git a/src/views/Concept/components/HierarchyView.js b/src/views/Concept/components/HierarchyView.js index cae3bb51..21b65489 100644 --- a/src/views/Concept/components/HierarchyView.js +++ b/src/views/Concept/components/HierarchyView.js @@ -1,11 +1,12 @@ /** - * HierarchyView - 概念层级思维导图视图 + * HierarchyView - 概念层级热力图视图 * - * 使用 ECharts Tree 图表实现思维导图效果 + * 使用 ECharts Treemap 实现热力图效果 * 特性: - * 1. 默认只展示前两层(根节点 + lv1),点击节点展开下层 - * 2. 集成 /hierarchy/price 接口获取实时涨跌幅 - * 3. 支持径向布局和正交布局切换 + * 1. 炫酷的矩形树图/热力图展示 + * 2. 点击 lv1 进入 lv2,点击 lv2 进入 lv3,层层钻取 + * 3. 集成 /hierarchy/price 接口获取实时涨跌幅 + * 4. 支持面包屑导航返回上级 */ import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import { @@ -18,7 +19,6 @@ import { Spinner, Center, Flex, - ButtonGroup, Button, useBreakpointValue, Tooltip, @@ -27,203 +27,63 @@ import { import ReactECharts from 'echarts-for-react'; import { FaLayerGroup, - FaSitemap, - FaProjectDiagram, FaExpand, FaCompress, - FaRedo, FaSync, + FaHome, + FaChevronRight, } from 'react-icons/fa'; import { logger } from '../../../utils/logger'; // 一级分类颜色映射 const LV1_COLORS = { - '人工智能': '#8B5CF6', - '半导体': '#3B82F6', - '机器人': '#10B981', - '消费电子': '#EC4899', - '智能驾驶与汽车': '#F97316', - '新能源与电力': '#22C55E', - '空天经济': '#06B6D4', - '国防军工': '#EF4444', - '政策与主题': '#F59E0B', - '周期与材料': '#6B7280', - '大消费': '#F472B6', - '数字经济与金融科技': '#6366F1', - '全球宏观与贸易': '#14B8A6', - '医药健康': '#84CC16', - '前沿科技': '#A855F7', + '人工智能': ['#8B5CF6', '#A78BFA', '#C4B5FD'], + '半导体': ['#3B82F6', '#60A5FA', '#93C5FD'], + '机器人': ['#10B981', '#34D399', '#6EE7B7'], + '消费电子': ['#EC4899', '#F472B6', '#F9A8D4'], + '智能驾驶与汽车': ['#F97316', '#FB923C', '#FDBA74'], + '新能源与电力': ['#22C55E', '#4ADE80', '#86EFAC'], + '空天经济': ['#06B6D4', '#22D3EE', '#67E8F9'], + '国防军工': ['#EF4444', '#F87171', '#FCA5A5'], + '政策与主题': ['#F59E0B', '#FBBF24', '#FCD34D'], + '周期与材料': ['#6B7280', '#9CA3AF', '#D1D5DB'], + '大消费': ['#F472B6', '#F9A8D4', '#FBCFE8'], + '数字经济与金融科技': ['#6366F1', '#818CF8', '#A5B4FC'], + '全球宏观与贸易': ['#14B8A6', '#2DD4BF', '#5EEAD4'], + '医药健康': ['#84CC16', '#A3E635', '#BEF264'], + '前沿科技': ['#A855F7', '#C084FC', '#D8B4FE'], }; -// 获取涨跌幅颜色 +// 获取涨跌幅颜色(红涨绿跌) const getChangeColor = (value) => { if (value === null || value === undefined) return '#9CA3AF'; - return value > 0 ? '#EF4444' : value < 0 ? '#22C55E' : '#9CA3AF'; + if (value > 3) return '#DC2626'; + if (value > 1) return '#EF4444'; + if (value > 0) return '#F87171'; + if (value < -3) return '#15803D'; + if (value < -1) return '#22C55E'; + if (value < 0) return '#4ADE80'; + return '#9CA3AF'; }; // 格式化涨跌幅 const formatChangePercent = (value) => { - if (value === null || value === undefined) return ''; + if (value === null || value === undefined) return '--'; const formatted = Math.abs(value).toFixed(2); return value > 0 ? `+${formatted}%` : value < 0 ? `-${formatted}%` : '0.00%'; }; -/** - * 将后端层级数据转换为 ECharts tree 格式 - * @param {Array} hierarchy - 层级结构数据 - * @param {Object} priceData - 涨跌幅数据 { lv1Map, lv2Map, lv3Map } - * @param {number} initialDepth - 初始展开深度 (1 = 只展示lv1, 2 = 展示到lv2) - */ -const transformToEChartsData = (hierarchy, priceData, initialDepth = 1) => { - if (!hierarchy || hierarchy.length === 0) return null; - - const { lv1Map = {}, lv2Map = {}, lv3Map = {} } = priceData || {}; - - // 根节点 - const root = { - name: '概念中心', - itemStyle: { - color: '#8B5CF6', - borderColor: '#7C3AED', - borderWidth: 3, - }, - label: { - fontSize: 14, - fontWeight: 'bold', - color: '#1F2937', - }, - children: hierarchy.map(lv1 => { - const lv1Price = lv1Map[lv1.name] || {}; - const lv1Color = LV1_COLORS[lv1.name] || '#8B5CF6'; - const changeColor = getChangeColor(lv1Price.avg_change_pct); - const changeStr = formatChangePercent(lv1Price.avg_change_pct); - - return { - name: lv1.name, - value: lv1.concept_count, - collapsed: initialDepth < 2, // 初始是否折叠 - data: { - type: 'lv1', - id: lv1.id, - name: lv1.name, - concept_count: lv1.concept_count, - avg_change_pct: lv1Price.avg_change_pct, - stock_count: lv1Price.stock_count, - }, - itemStyle: { - color: lv1Color, - borderColor: lv1Color, - borderWidth: 2, - }, - label: { - fontSize: 12, - fontWeight: 'bold', - color: '#1F2937', - formatter: () => { - let text = `${lv1.name}\n{count|${lv1.concept_count}个}`; - if (changeStr) { - text += `\n{change|${changeStr}}`; - } - return text; - }, - rich: { - count: { - fontSize: 10, - color: '#6B7280', - lineHeight: 16, - }, - change: { - fontSize: 11, - color: changeColor, - fontWeight: 'bold', - lineHeight: 16, - }, - }, - }, - children: lv1.children?.map(lv2 => { - const lv2Price = lv2Map[lv2.name] || {}; - const lv2ChangeColor = getChangeColor(lv2Price.avg_change_pct); - const lv2ChangeStr = formatChangePercent(lv2Price.avg_change_pct); - const hasLv3 = lv2.children && lv2.children.length > 0; - - return { - name: lv2.name, - value: lv2.concept_count, - collapsed: true, // lv2 默认折叠 - data: { - type: 'lv2', - id: lv2.id, - name: lv2.name, - parentLv1: lv1.name, - concept_count: lv2.concept_count, - avg_change_pct: lv2Price.avg_change_pct, - stock_count: lv2Price.stock_count, - }, - itemStyle: { - color: lv1Color, - opacity: 0.8, - }, - label: { - fontSize: 11, - color: '#374151', - formatter: () => { - let text = `${lv2.name}\n{count|(${lv2.concept_count})}`; - if (lv2ChangeStr) { - text += ` {change|${lv2ChangeStr}}`; - } - return text; - }, - rich: { - count: { - fontSize: 9, - color: '#9CA3AF', - }, - change: { - fontSize: 10, - color: lv2ChangeColor, - fontWeight: 'bold', - }, - }, - }, - children: hasLv3 ? lv2.children.map(lv3 => { - const lv3Price = lv3Map[lv3.name] || {}; - const lv3ChangeStr = formatChangePercent(lv3Price.avg_change_pct); - - return { - name: lv3.name, - value: lv3.concept_count, - collapsed: true, - data: { - type: 'lv3', - id: lv3.id, - name: lv3.name, - parentLv1: lv1.name, - parentLv2: lv2.name, - concept_count: lv3.concept_count, - concepts: lv3.concepts, - }, - itemStyle: { - color: lv1Color, - opacity: 0.6, - }, - label: { - fontSize: 10, - color: '#4B5563', - formatter: `${lv3.name} (${lv3.concept_count})${lv3ChangeStr ? ' ' + lv3ChangeStr : ''}`, - }, - }; - }) : undefined, - }; - }) || [], - }; - }), - }; - - return root; +// 获取 lv1 的颜色 +const getLv1Color = (name, index = 0) => { + const colors = LV1_COLORS[name]; + if (colors) return colors[Math.min(index, colors.length - 1)]; + // 默认颜色 + const defaultColors = ['#8B5CF6', '#A78BFA', '#C4B5FD']; + return defaultColors[Math.min(index, defaultColors.length - 1)]; }; /** - * 主组件:层级视图 + * 主组件:层级热力图视图 */ const HierarchyView = ({ apiBaseUrl, @@ -236,9 +96,14 @@ const HierarchyView = ({ const [priceData, setPriceData] = useState({ lv1Map: {}, lv2Map: {}, lv3Map: {} }); const [priceLoading, setPriceLoading] = useState(false); const [tradeDate, setTradeDate] = useState(null); - const [layout, setLayout] = useState('radial'); // 'radial' | 'orthogonal' const [isFullscreen, setIsFullscreen] = useState(false); + // 钻取状态 + const [currentLevel, setCurrentLevel] = useState('lv1'); // 'lv1' | 'lv2' | 'lv3' + const [currentLv1, setCurrentLv1] = useState(null); + const [currentLv2, setCurrentLv2] = useState(null); + const [breadcrumbs, setBreadcrumbs] = useState([{ label: '全部分类', level: 'root' }]); + const chartRef = useRef(null); const containerRef = useRef(null); const isMobile = useBreakpointValue({ base: true, md: false }); @@ -327,39 +192,145 @@ const HierarchyView = ({ } }, [hierarchy, fetchHierarchyPrice]); + // 根据当前层级获取显示数据 + const currentData = useMemo(() => { + const { lv1Map, lv2Map, lv3Map } = priceData; + + if (currentLevel === 'lv1') { + // 显示所有 lv1 + return hierarchy.map((lv1, index) => { + const price = lv1Map[lv1.name] || {}; + return { + name: lv1.name, + value: lv1.concept_count || 10, + id: lv1.id, + level: 'lv1', + concept_count: lv1.concept_count, + stock_count: price.stock_count, + avg_change_pct: price.avg_change_pct, + children: lv1.children, + itemStyle: { + color: getLv1Color(lv1.name, 0), + borderColor: '#fff', + borderWidth: 2, + }, + }; + }); + } + + if (currentLevel === 'lv2' && currentLv1) { + // 显示选中 lv1 下的 lv2 + const lv1Data = hierarchy.find(h => h.name === currentLv1.name); + if (!lv1Data || !lv1Data.children) return []; + + return lv1Data.children.map((lv2, index) => { + const price = lv2Map[lv2.name] || {}; + return { + name: lv2.name, + value: lv2.concept_count || 5, + id: lv2.id, + level: 'lv2', + parentLv1: currentLv1.name, + concept_count: lv2.concept_count, + stock_count: price.stock_count, + avg_change_pct: price.avg_change_pct, + children: lv2.children, + concepts: lv2.concepts, + itemStyle: { + color: getLv1Color(currentLv1.name, index % 3), + borderColor: '#fff', + borderWidth: 2, + }, + }; + }); + } + + if (currentLevel === 'lv3' && currentLv1 && currentLv2) { + // 显示选中 lv2 下的 lv3 或概念 + const lv1Data = hierarchy.find(h => h.name === currentLv1.name); + if (!lv1Data || !lv1Data.children) return []; + + const lv2Data = lv1Data.children.find(h => h.name === currentLv2.name); + if (!lv2Data) return []; + + // 如果有 lv3 子级 + if (lv2Data.children && lv2Data.children.length > 0) { + return lv2Data.children.map((lv3, index) => { + const price = lv3Map[lv3.name] || {}; + return { + name: lv3.name, + value: lv3.concept_count || 3, + id: lv3.id, + level: 'lv3', + parentLv1: currentLv1.name, + parentLv2: currentLv2.name, + concept_count: lv3.concept_count, + stock_count: price.stock_count, + avg_change_pct: price.avg_change_pct, + concepts: lv3.concepts, + itemStyle: { + color: getLv1Color(currentLv1.name, index % 3), + borderColor: '#fff', + borderWidth: 2, + }, + }; + }); + } + + // 如果直接是概念列表 + if (lv2Data.concepts && lv2Data.concepts.length > 0) { + return lv2Data.concepts.map((concept, index) => ({ + name: concept, + value: 1, + level: 'concept', + parentLv1: currentLv1.name, + parentLv2: currentLv2.name, + itemStyle: { + color: getLv1Color(currentLv1.name, index % 3), + borderColor: '#fff', + borderWidth: 1, + }, + })); + } + + return []; + } + + return []; + }, [hierarchy, priceData, currentLevel, currentLv1, currentLv2]); + // ECharts 配置 const chartOption = useMemo(() => { - const treeData = transformToEChartsData(hierarchy, priceData, 1); - if (!treeData) return null; - - const isRadial = layout === 'radial'; + if (!currentData || currentData.length === 0) return null; return { tooltip: { trigger: 'item', - triggerOn: 'mousemove', formatter: (params) => { - const data = params.data?.data || {}; - let content = `
${params.name}
`; + const data = params.data || {}; + let content = `
${data.name}
`; + + if (data.avg_change_pct !== undefined && data.avg_change_pct !== null) { + const color = getChangeColor(data.avg_change_pct); + content += `
平均涨跌: ${formatChangePercent(data.avg_change_pct)}
`; + } if (data.concept_count !== undefined) { - content += `
概念数量: ${data.concept_count}
`; + content += `
概念数量: ${data.concept_count}
`; } + if (data.stock_count !== undefined) { - content += `
成分股数: ${data.stock_count}
`; + content += `
成分股数: ${data.stock_count}
`; } - if (data.avg_change_pct !== undefined) { - const color = getChangeColor(data.avg_change_pct); - content += `
平均涨跌: ${formatChangePercent(data.avg_change_pct)}
`; - } - if (data.type) { - const typeMap = { lv1: '一级分类', lv2: '二级分类', lv3: '三级分类' }; - content += `
${typeMap[data.type] || ''}
`; + + const levelMap = { lv1: '一级分类', lv2: '二级分类', lv3: '三级分类', concept: '概念' }; + if (data.level) { + content += `
${levelMap[data.level] || ''}
`; } // 提示可点击 - if (data.type && data.type !== 'concept') { - content += `
点击筛选该分类
`; + if (data.level && data.level !== 'concept' && data.children) { + content += `
👆 点击查看下级分类
`; } return content; @@ -367,100 +338,146 @@ const HierarchyView = ({ backgroundColor: 'rgba(255, 255, 255, 0.98)', borderColor: '#E5E7EB', borderWidth: 1, - padding: [10, 14], + padding: [12, 16], textStyle: { color: '#1F2937', fontSize: 12, }, - extraCssText: 'box-shadow: 0 4px 12px rgba(0,0,0,0.1);', + extraCssText: 'box-shadow: 0 4px 20px rgba(0,0,0,0.15); border-radius: 8px;', }, series: [ { - type: 'tree', - data: [treeData], - layout: isRadial ? 'radial' : 'orthogonal', - orient: isRadial ? undefined : 'LR', - symbol: 'circle', - symbolSize: (value, params) => { - const data = params.data?.data || {}; - if (data.type === 'lv1') return 20; - if (data.type === 'lv2') return 14; - if (data.type === 'lv3') return 10; - return 26; // 根节点 + type: 'treemap', + data: currentData, + width: '100%', + height: '100%', + roam: false, + nodeClick: false, // 禁用默认钻取,使用自定义 + breadcrumb: { + show: false, // 使用自定义面包屑 }, - initialTreeDepth: 2, // 初始展开到第2层(根 + lv1) - animationDuration: 400, - animationDurationUpdate: 500, - roam: true, - zoom: isMobile ? 0.7 : 0.9, - center: isRadial ? ['50%', '50%'] : ['40%', '50%'], label: { - position: isRadial ? 'radial' : 'right', - verticalAlign: 'middle', - align: isRadial ? undefined : 'left', - fontSize: 11, - distance: 8, - }, - leaves: { - label: { - position: isRadial ? 'radial' : 'right', - verticalAlign: 'middle', - align: isRadial ? undefined : 'left', + show: true, + formatter: (params) => { + const data = params.data || {}; + const name = data.name || ''; + const changeStr = data.avg_change_pct !== undefined && data.avg_change_pct !== null + ? formatChangePercent(data.avg_change_pct) + : ''; + + // 根据区块大小决定显示内容 + if (params.value < 3) { + return name.length > 4 ? name.slice(0, 4) + '...' : name; + } + + if (changeStr) { + return `{name|${name}}\n{change|${changeStr}}`; + } + return `{name|${name}}`; }, + rich: { + name: { + fontSize: isMobile ? 12 : 14, + fontWeight: 'bold', + color: '#fff', + textShadowColor: 'rgba(0,0,0,0.3)', + textShadowBlur: 2, + lineHeight: isMobile ? 18 : 22, + }, + change: { + fontSize: isMobile ? 14 : 18, + fontWeight: 'bold', + color: '#fff', + textShadowColor: 'rgba(0,0,0,0.5)', + textShadowBlur: 3, + lineHeight: isMobile ? 20 : 26, + }, + }, + position: 'inside', + verticalAlign: 'middle', + align: 'center', + }, + upperLabel: { + show: false, + }, + itemStyle: { + borderColor: '#fff', + borderWidth: 3, + gapWidth: 3, + borderRadius: 4, }, emphasis: { - focus: 'ancestor', itemStyle: { - shadowBlur: 10, + shadowBlur: 20, shadowColor: 'rgba(0, 0, 0, 0.3)', }, + label: { + fontSize: isMobile ? 14 : 16, + }, }, - expandAndCollapse: true, - lineStyle: { - color: '#CBD5E1', - width: 1.5, - curveness: 0.5, - }, + levels: [ + { + itemStyle: { + borderColor: '#fff', + borderWidth: 4, + gapWidth: 4, + }, + }, + ], + animationDuration: 500, + animationEasing: 'cubicOut', }, ], }; - }, [hierarchy, priceData, layout, isMobile]); + }, [currentData, isMobile]); - // 处理节点点击 - 区分展开/收起和筛选 + // 处理点击事件 - 钻取 const handleChartClick = useCallback((params) => { - const data = params.data?.data; - if (!data || !data.type) return; + const data = params.data; + if (!data) return; - // 只有在节点已展开且点击的情况下才触发筛选 - // ECharts tree 会自动处理展开/收起 - logger.info('HierarchyView', '节点点击', data); + logger.info('HierarchyView', '热力图点击', { level: data.level, name: data.name }); - let filter = { lv1: null, lv2: null, lv3: null }; - - switch (data.type) { - case 'lv1': - filter = { lv1: data.name, lv2: null, lv3: null }; - break; - case 'lv2': - filter = { lv1: data.parentLv1, lv2: data.name, lv3: null }; - break; - case 'lv3': - filter = { lv1: data.parentLv1, lv2: data.parentLv2, lv3: data.name }; - break; - default: - return; + if (data.level === 'lv1' && data.children && data.children.length > 0) { + // 进入 lv2 + setCurrentLevel('lv2'); + setCurrentLv1(data); + setBreadcrumbs([ + { label: '全部分类', level: 'root' }, + { label: data.name, level: 'lv1', data }, + ]); + } else if (data.level === 'lv2') { + // 检查是否有 lv3 或概念 + if ((data.children && data.children.length > 0) || (data.concepts && data.concepts.length > 0)) { + setCurrentLevel('lv3'); + setCurrentLv2(data); + setBreadcrumbs([ + { label: '全部分类', level: 'root' }, + { label: currentLv1.name, level: 'lv1', data: currentLv1 }, + { label: data.name, level: 'lv2', data }, + ]); + } + } else if (data.level === 'lv3' || data.level === 'concept') { + // 最底层,可以触发筛选或者其他操作 + // 这里可以选择不做任何操作,或者提示用户 + logger.info('HierarchyView', '已到达最底层', { name: data.name }); } + }, [currentLv1]); - onSelectCategory && onSelectCategory(filter); - }, [onSelectCategory]); - - // 重置图表视图 - const handleResetView = useCallback(() => { - if (chartRef.current) { - const chart = chartRef.current.getEchartsInstance(); - chart.dispatchAction({ type: 'restore' }); + // 面包屑导航 - 返回上级 + const handleBreadcrumbClick = useCallback((crumb, index) => { + if (crumb.level === 'root') { + setCurrentLevel('lv1'); + setCurrentLv1(null); + setCurrentLv2(null); + setBreadcrumbs([{ label: '全部分类', level: 'root' }]); + } else if (crumb.level === 'lv1') { + setCurrentLevel('lv2'); + setCurrentLv1(crumb.data); + setCurrentLv2(null); + setBreadcrumbs(breadcrumbs.slice(0, index + 1)); } - }, []); + }, [breadcrumbs]); // 刷新涨跌幅数据 const handleRefreshPrice = useCallback(() => { @@ -477,6 +494,14 @@ const HierarchyView = ({ click: handleChartClick, }), [handleChartClick]); + // 获取当前层级标题 + const getCurrentTitle = () => { + if (currentLevel === 'lv1') return '一级分类概览'; + if (currentLevel === 'lv2' && currentLv1) return `${currentLv1.name} - 二级分类`; + if (currentLevel === 'lv3' && currentLv2) return `${currentLv2.name} - 三级分类`; + return '概念分类'; + }; + if (loading) { return (
@@ -536,7 +561,7 @@ const HierarchyView = ({ - 概念层级导图 + {getCurrentTitle()} {tradeDate && ( @@ -562,48 +587,6 @@ const HierarchyView = ({ /> - {/* 布局切换 */} - - - } - onClick={() => setLayout('radial')} - bg={layout === 'radial' ? 'purple.500' : 'transparent'} - color={layout === 'radial' ? 'white' : 'purple.500'} - borderColor="purple.500" - _hover={{ - bg: layout === 'radial' ? 'purple.600' : 'purple.50', - }} - aria-label="径向布局" - /> - - - } - onClick={() => setLayout('orthogonal')} - bg={layout === 'orthogonal' ? 'purple.500' : 'transparent'} - color={layout === 'orthogonal' ? 'white' : 'purple.500'} - borderColor="purple.500" - _hover={{ - bg: layout === 'orthogonal' ? 'purple.600' : 'purple.50', - }} - aria-label="树形布局" - /> - - - - {/* 重置视图 */} - - } - onClick={handleResetView} - variant="outline" - colorScheme="gray" - aria-label="重置视图" - /> - - {/* 全屏切换 */} + {/* 面包屑导航 */} + + {breadcrumbs.map((crumb, index) => ( + + {index > 0 && ( + + )} + + + ))} + + {/* 操作提示 */} - 🖱️ 滚轮缩放 | 拖拽平移 | 点击节点展开/收起 | 双击筛选该分类 + 👆 点击色块 查看下级分类 | + 区块大小表示概念数量 | + 红涨 + 绿跌 - {/* ECharts 图表 */} + {/* ECharts 热力图 */} - {hierarchy.length} 个一级分类 - - - {hierarchy.reduce((acc, h) => acc + (h.children?.length || 0), 0)} 个二级分类 - - - {hierarchy.reduce((acc, h) => acc + h.concept_count, 0)} 个概念 + 当前显示 {currentData.length} 个{currentLevel === 'lv1' ? '一级' : currentLevel === 'lv2' ? '二级' : '三级'}分类 + {currentLevel === 'lv1' && ( + <> + + 共 {hierarchy.reduce((acc, h) => acc + (h.children?.length || 0), 0)} 个二级分类 + + + 共 {hierarchy.reduce((acc, h) => acc + h.concept_count, 0)} 个概念 + + + )} ); diff --git a/src/views/Concept/index.js b/src/views/Concept/index.js index 85b10efb..500505ab 100644 --- a/src/views/Concept/index.js +++ b/src/views/Concept/index.js @@ -344,13 +344,14 @@ const ConceptCenter = () => { fetchConcepts('', 1, selectedDate, 'change_pct', { lv1: null, lv2: null, lv3: null }); }; - // 处理层级筛选选择(从 HierarchyView 点击分类) + // 处理层级筛选选择(从 HierarchyView 点击分类 - 仅在需要时使用) + // 注意:热力图视图现在是独立的钻取交互,不再自动切换到列表视图 const handleHierarchySelect = useCallback((filter) => { logger.info('ConceptCenter', '层级筛选选择', filter); setHierarchyFilter(filter); setCurrentPage(1); - setViewMode('list'); // 切换到列表视图 + // 不再自动切换视图,热力图内部自己处理钻取 // 更新 URL 参数 updateUrlParams({ @@ -360,21 +361,9 @@ const ConceptCenter = () => { page: 1 }); - // 重新获取数据 + // 重新获取数据(用于其他视图) fetchConcepts(searchQuery, 1, selectedDate, sortBy, filter); - - // 滚动到页面顶部,让用户看到筛选结果 - window.scrollTo({ top: 0, behavior: 'smooth' }); - - // 显示提示 - toast({ - title: '已应用筛选', - description: `正在显示「${[filter.lv1, filter.lv2, filter.lv3].filter(Boolean).join(' > ')}」分类下的概念`, - status: 'info', - duration: 2000, - isClosable: true, - }); - }, [searchQuery, selectedDate, sortBy, updateUrlParams, fetchConcepts, toast]); + }, [searchQuery, selectedDate, sortBy, updateUrlParams, fetchConcepts]); // 清除层级筛选 const handleClearHierarchyFilter = useCallback(() => {