diff --git a/src/components/Navbars/components/NavStockSearch/index.js b/src/components/Navbars/components/NavStockSearch/index.js new file mode 100644 index 00000000..eea7ad28 --- /dev/null +++ b/src/components/Navbars/components/NavStockSearch/index.js @@ -0,0 +1,213 @@ +// src/components/Navbars/components/NavStockSearch/index.js +// 导航栏股票搜索组件 - 支持代码、名称、拼音缩写搜索 + +import React, { useRef, useEffect, useCallback, memo } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + Box, + Input, + InputGroup, + InputLeftElement, + InputRightElement, + IconButton, + Text, + VStack, + HStack, + Spinner, + Tag, + Center, + List, + ListItem, + Flex, + Portal, +} from '@chakra-ui/react'; +import { Search, X } from 'lucide-react'; +import { useStockSearch } from '@hooks/useStockSearch'; + +/** + * 导航栏股票搜索组件 + * 浅色主题,适配导航栏使用 + */ +const NavStockSearch = memo(() => { + const navigate = useNavigate(); + const containerRef = useRef(null); + const inputRef = useRef(null); + + // 浅色主题颜色配置 + const searchIconColor = 'gray.400'; + const inputBg = 'gray.50'; + const dropdownBg = 'white'; + const borderColor = 'gray.200'; + const hoverBg = 'gray.50'; + const textColor = 'gray.800'; + const subTextColor = 'gray.500'; + const accentColor = 'blue.500'; + + // 使用搜索 Hook + const { + searchQuery, + searchResults, + isSearching, + showResults, + handleSearch, + clearSearch, + setShowResults, + } = useStockSearch({ limit: 8, debounceMs: 300 }); + + // 点击外部关闭下拉 + useEffect(() => { + const handleClickOutside = (event) => { + if (containerRef.current && !containerRef.current.contains(event.target)) { + setShowResults(false); + } + }; + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, [setShowResults]); + + // 选择股票 - 跳转到详情页 + const handleSelectStock = useCallback((stock) => { + clearSearch(); + navigate(`/company/${stock.stock_code}`); + }, [navigate, clearSearch]); + + // 处理键盘事件 + const handleKeyDown = useCallback((e) => { + if (e.key === 'Enter' && searchResults.length > 0) { + handleSelectStock(searchResults[0]); + } else if (e.key === 'Escape') { + setShowResults(false); + inputRef.current?.blur(); + } + }, [searchResults, handleSelectStock, setShowResults]); + + // 计算下拉框位置 + const getDropdownPosition = () => { + if (!containerRef.current) return {}; + const rect = containerRef.current.getBoundingClientRect(); + return { + position: 'fixed', + top: `${rect.bottom + 8}px`, + left: `${rect.left}px`, + width: '300px', + }; + }; + + return ( + + + + + + handleSearch(e.target.value)} + onKeyDown={handleKeyDown} + onFocus={() => searchQuery && searchResults.length > 0 && setShowResults(true)} + borderColor={borderColor} + borderRadius="md" + _hover={{ borderColor: 'gray.300' }} + _focus={{ + borderColor: accentColor, + boxShadow: '0 0 0 1px var(--chakra-colors-blue-500)', + bg: 'white' + }} + _placeholder={{ color: 'gray.400' }} + /> + {(searchQuery || isSearching) && ( + + {isSearching ? ( + + ) : ( + } + onClick={clearSearch} + aria-label="清除搜索" + color="gray.400" + _hover={{ bg: 'transparent', color: 'gray.600' }} + /> + )} + + )} + + + {/* 搜索结果下拉 - 使用 Portal 避免被导航栏裁剪 */} + {showResults && ( + + + {searchResults.length > 0 ? ( + + {searchResults.map((stock, index) => ( + handleSelectStock(stock)} + borderBottomWidth={index < searchResults.length - 1 ? '1px' : '0'} + borderColor="gray.100" + > + + + + {stock.stock_name} + + + + {stock.stock_code} + + {stock.pinyin_abbr && ( + + {stock.pinyin_abbr.toUpperCase()} + + )} + + + {stock.exchange && ( + + {stock.exchange === 'SH' ? '沪' : stock.exchange === 'SZ' ? '深' : stock.exchange} + + )} + + + ))} + + ) : ( +
+ + {searchQuery ? '未找到相关股票' : '输入代码/名称/拼音搜索'} + +
+ )} +
+
+ )} +
+ ); +}); + +NavStockSearch.displayName = 'NavStockSearch'; + +export default NavStockSearch; diff --git a/src/components/Navbars/components/NavbarActions/index.js b/src/components/Navbars/components/NavbarActions/index.js index b4394575..a67e6093 100644 --- a/src/components/Navbars/components/NavbarActions/index.js +++ b/src/components/Navbars/components/NavbarActions/index.js @@ -8,6 +8,7 @@ import LoginButton from '../LoginButton'; // import CalendarButton from '../CalendarButton'; // 暂时注释 import { DesktopUserMenu, TabletUserMenu } from '../UserMenu'; import { MySpaceButton, MoreMenu } from '../Navigation'; +import NavStockSearch from '../NavStockSearch'; /** * Navbar 右侧功能区组件 @@ -50,9 +51,10 @@ const NavbarActions = memo(({ {/* 投资日历 - 暂时注释 */} {/* {isDesktop && } */} - {/* 桌面端布局:[我的空间] | [头像][用户名] */} + {/* 桌面端布局:[搜索框] [我的空间] | [头像][用户名] */} {isDesktop ? ( <> + { {/* 主内容区域 - padding 由 MainLayout 统一设置 */} {/* ⚡ 顶部说明面板(懒加载):产品介绍 + 沪深指数 + 热门概念词云 */} - - - - }> - - + {/* 📱 移动端隐藏:HeroPanel 在小屏幕上显示效果不佳,仅在 md 及以上尺寸显示 */} + + + + + }> + + + {/* 实时要闻·动态追踪 - 横向滚动 */} onStockClick?.(stock)} - role="group" + trigger="hover" + placement="left" + openDelay={300} + closeDelay={100} + isLazy > - {/* 第一行:股票名称 + 价格/涨跌幅 + 取消关注按钮 */} - - - - {stock.stock_name || stock.stock_code} - - - {stock.stock_code} - - - - - - {quote?.current_price?.toFixed(2) || stock.current_price || '--'} - - - {changePercent !== undefined && changePercent !== null - ? `${isUp ? '+' : ''}${Number(changePercent).toFixed(2)}%` - : '--'} - - - e.stopPropagation()}> - handleUnwatch(stock.stock_code)} - size="sm" - colorScheme="gold" - showTooltip={true} - /> - - - - {/* 第二行:日均、周涨 Badge */} - {(dailyChg || weeklyChg) && ( - - {dailyChg && ( - - 日均 {dailyChg.text} - + + onStockClick?.(stock)} + role="group" + > + {/* 第一行:股票名称 + 价格/涨跌幅 + 取消关注按钮 */} + + + + {stock.stock_name || stock.stock_code} + + + {stock.stock_code} + + + + + + {quote?.current_price?.toFixed(2) || stock.current_price || '--'} + + + {changePercent !== undefined && changePercent !== null + ? `${isUp ? '+' : ''}${Number(changePercent).toFixed(2)}%` + : '--'} + + + e.stopPropagation()}> + handleUnwatch(stock.stock_code)} + size="sm" + colorScheme="gold" + showTooltip={true} + /> + + + + {/* 第二行:日均、周涨 Badge */} + {(dailyChg || weeklyChg) && ( + + {dailyChg && ( + + 日均 {dailyChg.text} + + )} + {weeklyChg && ( + + 周涨 {weeklyChg.text} + + )} + )} - {weeklyChg && ( - + + + + + {/* 股票信息头部 */} + + + + {stock.stock_name} + + + {stock.stock_code} + + + + + {quote?.current_price?.toFixed(2) || stock.current_price || '--'} + + + {changePercent !== undefined && changePercent !== null + ? `${isUp ? '+' : ''}${Number(changePercent).toFixed(2)}%` + : '--'} + + + + {/* 分时图 */} + - 周涨 {weeklyChg.text} - - )} - - )} - + + + {/* 提示文字 */} + + 点击查看详情 + + + + + ); })}