refactor(icons): 迁移 views/Concept 目录图标到 lucide-react

- @chakra-ui/icons → lucide-react
- react-icons → lucide-react
- 处理 Box/BoxIcon 命名冲突
- 涉及 6 个组件文件

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-25 12:24:38 +08:00
parent be6e080710
commit d6cf776530
6 changed files with 228 additions and 193 deletions

View File

@@ -16,8 +16,7 @@ import {
Flex,
Tooltip,
} from '@chakra-ui/react';
import { ChevronRightIcon } from '@chakra-ui/icons';
import { FaFilter, FaTimes, FaHome } from 'react-icons/fa';
import { ChevronRight, Filter, X, Home } from 'lucide-react';
const BreadcrumbNav = ({
filter,
@@ -139,7 +138,7 @@ const BreadcrumbNav = ({
py={1}
borderRadius="full"
>
<Icon as={FaFilter} boxSize={3} />
<Icon as={Filter} boxSize={3} />
<Text fontSize="sm" fontWeight="bold">
层级筛选
</Text>
@@ -161,7 +160,7 @@ const BreadcrumbNav = ({
alignItems="center"
gap={1}
>
<Icon as={FaHome} boxSize={3} />
<Icon as={Home} boxSize={3} />
全部
</Badge>
</Tooltip>
@@ -169,7 +168,7 @@ const BreadcrumbNav = ({
{/* 面包屑路径 */}
{breadcrumbs.map((crumb, index) => (
<React.Fragment key={crumb.level}>
<Icon as={ChevronRightIcon} color={styles.chevron.color} boxSize={4} />
<Icon as={ChevronRight} color={styles.chevron.color} boxSize={4} />
<Badge
{...(isDarkMode ? {} : { colorScheme: styles.crumbBadge(crumb.level, index === breadcrumbs.length - 1).colorScheme })}
bg={isDarkMode ? styles.crumbBadge(crumb.level, index === breadcrumbs.length - 1).bg : undefined}
@@ -196,7 +195,7 @@ const BreadcrumbNav = ({
<Tooltip label="清除筛选" placement="top">
<IconButton
size="sm"
icon={<FaTimes />}
icon={<X />}
{...(isDarkMode ? {} : { colorScheme: 'red' })}
color={isDarkMode ? styles.clearBtn.color : undefined}
variant="ghost"

View File

@@ -37,14 +37,16 @@ import {
useToast,
} from '@chakra-ui/react';
import {
FaArrowUp,
FaArrowDown,
FaChartLine,
FaNewspaper,
FaRocket,
FaCrown,
} from 'react-icons/fa';
import { BsLightningFill, BsGraphUp, BsGraphDown } from 'react-icons/bs';
ArrowUp,
ArrowDown,
LineChart,
Newspaper,
Rocket,
Crown,
Zap,
TrendingUp,
TrendingDown,
} from 'lucide-react';
const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
// 获取正确的API基础URL
@@ -274,7 +276,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
const tabsData = [
{
label: '涨幅榜',
icon: FaArrowUp,
icon: ArrowUp,
color: 'red',
data: statsData.hot_concepts,
renderItem: (item, index) => (
@@ -314,7 +316,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
</Badge>
{index === 0 && (
<Icon
as={FaCrown}
as={Crown}
position="absolute"
top="-8px"
right="-8px"
@@ -329,12 +331,12 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
</Text>
<HStack spacing={2} fontSize="xs" color="whiteAlpha.600">
<HStack spacing={1}>
<Icon as={FaChartLine} boxSize={2.5} />
<Icon as={LineChart} boxSize={2.5} />
<Text>{item.stock_count}</Text>
</HStack>
<Text>·</Text>
<HStack spacing={1}>
<Icon as={FaNewspaper} boxSize={2.5} />
<Icon as={Newspaper} boxSize={2.5} />
<Text>{item.news_count}</Text>
</HStack>
</HStack>
@@ -349,7 +351,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
px={2}
py={1}
>
<Icon as={FaArrowUp} boxSize={2} mr={1} />
<Icon as={ArrowUp} boxSize={2} mr={1} />
{formatChange(item.change_pct)}
</Badge>
</Flex>
@@ -357,7 +359,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
},
{
label: '跌幅榜',
icon: FaArrowDown,
icon: ArrowDown,
color: 'green',
data: statsData.cold_concepts,
renderItem: (item, index) => (
@@ -400,12 +402,12 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
</Text>
<HStack spacing={2} fontSize="xs" color="whiteAlpha.600">
<HStack spacing={1}>
<Icon as={FaChartLine} boxSize={2.5} />
<Icon as={LineChart} boxSize={2.5} />
<Text>{item.stock_count}</Text>
</HStack>
<Text>·</Text>
<HStack spacing={1}>
<Icon as={FaNewspaper} boxSize={2.5} />
<Icon as={Newspaper} boxSize={2.5} />
<Text>{item.news_count}</Text>
</HStack>
</HStack>
@@ -420,7 +422,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
px={2}
py={1}
>
<Icon as={FaArrowDown} boxSize={2} mr={1} />
<Icon as={ArrowDown} boxSize={2} mr={1} />
{formatChange(item.change_pct)}
</Badge>
</Flex>
@@ -428,7 +430,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
},
{
label: '波动榜',
icon: BsLightningFill,
icon: Zap,
color: 'purple',
data: statsData.volatile_concepts,
renderItem: (item, index) => (
@@ -468,7 +470,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
</Badge>
{index === 0 && (
<Icon
as={BsLightningFill}
as={Zap}
position="absolute"
top="-8px"
right="-8px"
@@ -495,7 +497,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
px={2}
py={1}
>
<Icon as={BsLightningFill} boxSize={2} mr={1} />
<Icon as={Zap} boxSize={2} mr={1} />
{item.volatility?.toFixed(1)}%
</Badge>
</Flex>
@@ -503,7 +505,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
},
{
label: '连涨榜',
icon: FaRocket,
icon: Rocket,
color: 'cyan',
data: statsData.momentum_concepts,
renderItem: (item, index) => (
@@ -543,7 +545,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
</Badge>
{index === 0 && (
<Icon
as={FaRocket}
as={Rocket}
position="absolute"
top="-8px"
right="-8px"
@@ -570,7 +572,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
px={2}
py={1}
>
<Icon as={FaRocket} boxSize={2} mr={1} />
<Icon as={Rocket} boxSize={2} mr={1} />
{item.consecutive_days}
</Badge>
</Flex>
@@ -620,7 +622,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
<Flex justify="space-between" align="center" w="full">
<HStack spacing={2}>
<Box p={2} bg="whiteAlpha.100" borderRadius="lg" border="1px solid" borderColor="cyan.500">
<Icon as={FaChartLine} color="cyan.400" boxSize={4} />
<Icon as={LineChart} color="cyan.400" boxSize={4} />
</Box>
<VStack align="start" spacing={0}>
<Heading size="sm" color="white" fontWeight="bold">

View File

@@ -31,18 +31,18 @@ import {
} from '@chakra-ui/react';
import { keyframes } from '@emotion/react';
import {
FaLayerGroup,
FaSync,
FaExpand,
FaCompress,
FaHome,
FaArrowUp,
FaArrowDown,
FaCircle,
FaTh,
FaChevronRight,
FaArrowLeft,
} from 'react-icons/fa';
Layers,
RefreshCw,
Maximize2,
Minimize2,
Home,
ArrowUp,
ArrowDown,
Circle,
Grid3x3,
ChevronRight,
ArrowLeft,
} from 'lucide-react';
import { logger } from '../../../utils/logger';
// 极光动画 - 黑金色主题
@@ -1052,7 +1052,7 @@ const ForceGraphView = ({
borderColor="whiteAlpha.100"
>
<VStack spacing={4}>
<Icon as={FaLayerGroup} boxSize={16} color="gray.600" />
<Icon as={Layers} boxSize={16} color="gray.600" />
<Text color="gray.400">加载失败{error}</Text>
<Button
colorScheme="purple"
@@ -1078,7 +1078,7 @@ const ForceGraphView = ({
<Tooltip label="返回上一层" placement="bottom">
<IconButton
size="sm"
icon={<FaArrowLeft />}
icon={<ArrowLeft />}
onClick={handleGoBack}
bg="whiteAlpha.100"
color="white"
@@ -1104,7 +1104,7 @@ const ForceGraphView = ({
border="1px solid"
borderColor="whiteAlpha.200"
>
<Icon as={FaTh} color="purple.300" />
<Icon as={Grid3x3} color="purple.300" />
<Text color="white" fontWeight="bold" fontSize="sm">
概念矩形树图
</Text>
@@ -1123,7 +1123,7 @@ const ForceGraphView = ({
{breadcrumbItems.map((item, index) => (
<HStack key={index} spacing={2}>
{index > 0 && (
<Icon as={FaChevronRight} color="whiteAlpha.400" boxSize={3} />
<Icon as={ChevronRight} color="whiteAlpha.400" boxSize={3} />
)}
<Text
color={index === breadcrumbItems.length - 1 ? 'purple.300' : 'whiteAlpha.700'}
@@ -1153,7 +1153,7 @@ const ForceGraphView = ({
<Tooltip label="返回全部" placement="left">
<IconButton
size="sm"
icon={<FaHome />}
icon={<Home />}
onClick={handleGoHome}
bg="whiteAlpha.100"
color="white"
@@ -1174,7 +1174,7 @@ const ForceGraphView = ({
<Tooltip label="刷新数据" placement="left">
<IconButton
size="sm"
icon={<FaSync />}
icon={<RefreshCw />}
onClick={handleRefresh}
isLoading={priceLoading}
bg="whiteAlpha.100"
@@ -1195,7 +1195,7 @@ const ForceGraphView = ({
<Tooltip label={isFullscreen ? '退出全屏' : '全屏'} placement="left">
<IconButton
size="sm"
icon={isFullscreen ? <FaCompress /> : <FaExpand />}
icon={isFullscreen ? <Minimize2 /> : <Maximize2 />}
onClick={toggleFullscreen}
bg="whiteAlpha.100"
color="white"

View File

@@ -27,87 +27,87 @@ import {
} from '@chakra-ui/react';
import { keyframes } from '@emotion/react';
import {
FaLayerGroup,
FaExpand,
FaCompress,
FaSync,
FaHome,
FaChevronRight,
FaBrain,
FaMicrochip,
FaRobot,
FaMobileAlt,
FaCar,
FaBolt,
FaRocket,
FaShieldAlt,
FaGlobe,
FaIndustry,
FaShoppingCart,
FaCoins,
FaHeartbeat,
FaAtom,
FaArrowUp,
FaArrowDown,
FaCubes,
FaServer,
FaCode,
FaMagic,
FaEye,
FaPlane,
FaSatellite,
FaBatteryFull,
FaSolarPanel,
FaTags,
FaExternalLinkAlt,
} from 'react-icons/fa';
Layers,
Maximize2,
Minimize2,
RefreshCw,
Home,
ChevronRight,
Brain,
Cpu,
Bot,
Smartphone,
Car,
Zap,
Rocket,
Shield,
Globe,
Factory,
ShoppingCart,
Coins,
Heart,
Atom,
ArrowUp,
ArrowDown,
Box as BoxIcon,
Server,
Code,
Sparkles,
Eye,
Plane,
Satellite,
BatteryFull,
Sun,
Tags,
ExternalLink,
} from 'lucide-react';
import { logger } from '../../../utils/logger';
// 一级分类图标映射
const LV1_ICONS = {
'人工智能': FaBrain,
'半导体': FaMicrochip,
'机器人': FaRobot,
'消费电子': FaMobileAlt,
'智能驾驶与汽车': FaCar,
'新能源与电力': FaBolt,
'空天经济': FaRocket,
'国防军工': FaShieldAlt,
'政策与主题': FaGlobe,
'周期与材料': FaIndustry,
'大消费': FaShoppingCart,
'数字经济与金融科技': FaCoins,
'全球宏观与贸易': FaGlobe,
'医药健康': FaHeartbeat,
'前沿科技': FaAtom,
'人工智能': Brain,
'半导体': Cpu,
'机器人': Bot,
'消费电子': Smartphone,
'智能驾驶与汽车': Car,
'新能源与电力': Zap,
'空天经济': Rocket,
'国防军工': Shield,
'政策与主题': Globe,
'周期与材料': Factory,
'大消费': ShoppingCart,
'数字经济与金融科技': Coins,
'全球宏观与贸易': Globe,
'医药健康': Heart,
'前沿科技': Atom,
};
// 二级分类图标映射
const LV2_ICONS = {
'AI基础设施': FaServer,
'AI模型与软件': FaCode,
'AI应用': FaMagic,
'半导体设备': FaCubes,
'半导体材料': FaAtom,
'芯片设计与制造': FaMicrochip,
'先进封装': FaCubes,
'人形机器人整机': FaRobot,
'机器人核心零部件': FaCubes,
'其他类型机器人': FaRobot,
'智能终端': FaMobileAlt,
'XR与空间计算': FaEye,
'华为产业链': FaMobileAlt,
'自动驾驶解决方案': FaCar,
'智能汽车产业链': FaCar,
'车路协同': FaCar,
'新型电池技术': FaBatteryFull,
'电力设备与电网': FaBolt,
'清洁能源': FaSolarPanel,
'低空经济': FaPlane,
'商业航天': FaSatellite,
'无人作战与信息化': FaShieldAlt,
'海军装备': FaShieldAlt,
'军贸出海': FaGlobe,
'AI基础设施': Server,
'AI模型与软件': Code,
'AI应用': Sparkles,
'半导体设备': BoxIcon,
'半导体材料': Atom,
'芯片设计与制造': Cpu,
'先进封装': BoxIcon,
'人形机器人整机': Bot,
'机器人核心零部件': BoxIcon,
'其他类型机器人': Bot,
'智能终端': Smartphone,
'XR与空间计算': Eye,
'华为产业链': Smartphone,
'自动驾驶解决方案': Car,
'智能汽车产业链': Car,
'车路协同': Car,
'新型电池技术': BatteryFull,
'电力设备与电网': Zap,
'清洁能源': Sun,
'低空经济': Plane,
'商业航天': Satellite,
'无人作战与信息化': Shield,
'海军装备': Shield,
'军贸出海': Globe,
};
// 根据涨跌幅获取背景渐变色(涨红跌绿)
@@ -151,10 +151,10 @@ const formatChangePercent = (value) => {
// 获取图标
const getIcon = (name, level) => {
if (level === 'lv1') return LV1_ICONS[name] || FaLayerGroup;
if (level === 'lv2') return LV2_ICONS[name] || FaCubes;
if (level === 'lv3') return FaCubes;
return FaTags;
if (level === 'lv1') return LV1_ICONS[name] || Layers;
if (level === 'lv2') return LV2_ICONS[name] || Box;
if (level === 'lv3') return Box;
return Tags;
};
// 从 API 返回的名称中提取纯名称
@@ -352,7 +352,7 @@ const GlassCard = ({ item, onClick, size = 'normal' }) => {
{/* 外链图标 */}
{isLeafConcept && (
<Icon
as={FaExternalLinkAlt}
as={ExternalLink}
boxSize={3}
color="whiteAlpha.500"
/>
@@ -372,7 +372,7 @@ const GlassCard = ({ item, onClick, size = 'normal' }) => {
<HStack spacing={1} align="center">
{hasChange && (isPositive || isNegative) && (
<Icon
as={isPositive ? FaArrowUp : FaArrowDown}
as={isPositive ? ArrowUp : ArrowDown}
boxSize={3}
color={getChangeTextColor(item.avg_change_pct)}
/>
@@ -766,7 +766,7 @@ const HierarchyView = ({
<Center h="400px" position="relative">
<AuroraBackground />
<VStack spacing={4} position="relative" zIndex={1}>
<Icon as={FaLayerGroup} boxSize={16} color="gray.600" />
<Icon as={Layers} boxSize={16} color="gray.600" />
<Text color="gray.400">加载失败{error}</Text>
<Button
colorScheme="purple"
@@ -787,7 +787,7 @@ const HierarchyView = ({
<Center h="400px" position="relative">
<AuroraBackground />
<VStack spacing={4} position="relative" zIndex={1}>
<Icon as={FaLayerGroup} boxSize={16} color="gray.600" />
<Icon as={Layers} boxSize={16} color="gray.600" />
<Text color="gray.400">暂无层级数据</Text>
</VStack>
</Center>
@@ -832,14 +832,14 @@ const HierarchyView = ({
{breadcrumbs.map((crumb, index) => (
<React.Fragment key={index}>
{index > 0 && (
<Icon as={FaChevronRight} color="whiteAlpha.400" boxSize={3} mx={1} />
<Icon as={ChevronRight} color="whiteAlpha.400" boxSize={3} mx={1} />
)}
<Button
size="sm"
variant="ghost"
bg={index === breadcrumbs.length - 1 ? 'purple.500' : 'transparent'}
color={index === breadcrumbs.length - 1 ? 'white' : 'whiteAlpha.700'}
leftIcon={index === 0 ? <FaHome /> : undefined}
leftIcon={index === 0 ? <Home /> : undefined}
onClick={() => handleBreadcrumbClick(crumb, index)}
isDisabled={index === breadcrumbs.length - 1}
fontWeight={index === breadcrumbs.length - 1 ? 'bold' : 'medium'}
@@ -861,7 +861,7 @@ const HierarchyView = ({
<Tooltip label="刷新涨跌幅" placement="top">
<IconButton
size="sm"
icon={<FaSync />}
icon={<RefreshCw />}
onClick={handleRefreshPrice}
isLoading={priceLoading}
bg="whiteAlpha.100"
@@ -876,7 +876,7 @@ const HierarchyView = ({
<Tooltip label={isFullscreen ? '退出全屏' : '全屏'} placement="top">
<IconButton
size="sm"
icon={isFullscreen ? <FaCompress /> : <FaExpand />}
icon={isFullscreen ? <Minimize2 /> : <Maximize2 />}
onClick={toggleFullscreen}
bg="whiteAlpha.100"
color="white"