diff --git a/src/views/Company/components/CompanyHeader/SearchBar.js b/src/views/Company/components/CompanyHeader/SearchBar.js index fad22ced..8cb59706 100644 --- a/src/views/Company/components/CompanyHeader/SearchBar.js +++ b/src/views/Company/components/CompanyHeader/SearchBar.js @@ -1,68 +1,173 @@ // src/views/Company/components/CompanyHeader/SearchBar.js -// 股票搜索栏组件 - 金色主题 +// 股票搜索栏组件 - 金色主题 + 模糊搜索下拉 -import React from 'react'; +import React, { useState, useEffect, useRef } from 'react'; +import { useSelector } from 'react-redux'; import { + Box, HStack, Input, Button, InputGroup, InputLeftElement, + Text, + VStack, } from '@chakra-ui/react'; import { SearchIcon } from '@chakra-ui/icons'; /** - * 股票搜索栏组件 + * 股票搜索栏组件(带模糊搜索下拉) * * @param {Object} props * @param {string} props.inputCode - 输入框当前值 * @param {Function} props.onInputChange - 输入变化回调 * @param {Function} props.onSearch - 搜索按钮点击回调 - * @param {Function} props.onKeyPress - 键盘事件回调 + * @param {Function} props.onKeyDown - 键盘事件回调 */ const SearchBar = ({ inputCode, onInputChange, onSearch, - onKeyPress, + onKeyDown, }) => { + // 下拉状态 + const [showDropdown, setShowDropdown] = useState(false); + const [filteredStocks, setFilteredStocks] = useState([]); + const containerRef = useRef(null); + + // 从 Redux 获取全部股票列表 + const allStocks = useSelector(state => state.stock.allStocks); + + // 模糊搜索过滤 + useEffect(() => { + if (inputCode && inputCode.trim()) { + const searchTerm = inputCode.trim().toLowerCase(); + const filtered = allStocks.filter(stock => + stock.code.toLowerCase().includes(searchTerm) || + stock.name.includes(inputCode.trim()) + ).slice(0, 10); // 限制显示10条 + setFilteredStocks(filtered); + setShowDropdown(filtered.length > 0); + } else { + setFilteredStocks([]); + setShowDropdown(false); + } + }, [inputCode, allStocks]); + + // 点击外部关闭下拉 + useEffect(() => { + const handleClickOutside = (event) => { + if (containerRef.current && !containerRef.current.contains(event.target)) { + setShowDropdown(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, []); + + // 选择股票 + const handleSelectStock = (stock) => { + onInputChange(stock.code); + setShowDropdown(false); + // 自动触发搜索 + setTimeout(() => onSearch(), 0); + }; + + // 处理键盘事件 + const handleKeyDownWrapper = (e) => { + if (e.key === 'Enter') { + setShowDropdown(false); + } + onKeyDown?.(e); + }; + return ( - - - - - - onInputChange(e.target.value)} - onKeyPress={onKeyPress} - borderRadius="md" - color="white" + + + + + + + onInputChange(e.target.value)} + onKeyDown={handleKeyDownWrapper} + onFocus={() => inputCode && filteredStocks.length > 0 && setShowDropdown(true)} + borderRadius="md" + color="white" + borderColor="#C9A961" + _placeholder={{ color: '#C9A961' }} + _focus={{ + borderColor: '#F4D03F', + boxShadow: '0 0 0 1px #F4D03F', + }} + _hover={{ + borderColor: '#F4D03F', + }} + /> + + - + _hover={{ bg: '#1a1a1a', borderColor: '#F4D03F', color: '#F4D03F' }} + > + 查询 + + + + {/* 模糊搜索下拉列表 */} + {showDropdown && ( + + + {filteredStocks.map((stock) => ( + handleSelectStock(stock)} + borderBottom="1px solid" + borderColor="whiteAlpha.100" + _last={{ borderBottom: 'none' }} + > + + + {stock.code} + + + {stock.name} + + + + ))} + + + )} + ); }; diff --git a/src/views/Company/components/CompanyHeader/index.js b/src/views/Company/components/CompanyHeader/index.js index 321333f4..16894da4 100644 --- a/src/views/Company/components/CompanyHeader/index.js +++ b/src/views/Company/components/CompanyHeader/index.js @@ -18,20 +18,20 @@ import SearchBar from './SearchBar'; * * 包含: * - 页面标题和描述(金色主题) - * - 股票搜索栏 + * - 股票搜索栏(支持模糊搜索) * * @param {Object} props * @param {string} props.inputCode - 搜索输入框值 * @param {Function} props.onInputChange - 输入变化回调 * @param {Function} props.onSearch - 搜索回调 - * @param {Function} props.onKeyPress - 键盘事件回调 + * @param {Function} props.onKeyDown - 键盘事件回调 * @param {string} props.bgColor - 背景颜色 */ const CompanyHeader = ({ inputCode, onInputChange, onSearch, - onKeyPress, + onKeyDown, bgColor, }) => { return ( @@ -51,7 +51,7 @@ const CompanyHeader = ({ inputCode={inputCode} onInputChange={onInputChange} onSearch={onSearch} - onKeyPress={onKeyPress} + onKeyDown={onKeyDown} /> diff --git a/src/views/Company/hooks/useCompanyStock.js b/src/views/Company/hooks/useCompanyStock.js index d464a74d..258c9d30 100644 --- a/src/views/Company/hooks/useCompanyStock.js +++ b/src/views/Company/hooks/useCompanyStock.js @@ -69,7 +69,7 @@ export const useCompanyStock = (options = {}) => { /** * 处理键盘事件 - 回车键触发搜索 */ - const handleKeyPress = useCallback((e) => { + const handleKeyDown = useCallback((e) => { if (e.key === 'Enter') { handleSearch(); } @@ -83,7 +83,7 @@ export const useCompanyStock = (options = {}) => { // 操作方法 setInputCode, // 更新输入框 handleSearch, // 执行搜索 - handleKeyPress, // 处理回车键 + handleKeyDown, // 处理回车键(改用 onKeyDown) }; }; diff --git a/src/views/Company/index.js b/src/views/Company/index.js index 5c2a9b86..a468cb52 100644 --- a/src/views/Company/index.js +++ b/src/views/Company/index.js @@ -3,6 +3,8 @@ import React, { useEffect, useRef } from 'react'; import { Container, VStack } from '@chakra-ui/react'; +import { useDispatch } from 'react-redux'; +import { loadAllStocks } from '@store/slices/stockSlice'; // 自定义 Hooks import { useCompanyStock } from './hooks/useCompanyStock'; @@ -24,15 +26,22 @@ import CompanyTabs from './components/CompanyTabs'; * - PostHog 事件追踪 */ const CompanyIndex = () => { + const dispatch = useDispatch(); + // 1. 先获取股票代码(不带追踪回调) const { stockCode, inputCode, setInputCode, handleSearch, - handleKeyPress, + handleKeyDown, } = useCompanyStock(); + // 加载全部股票列表(用于模糊搜索) + useEffect(() => { + dispatch(loadAllStocks()); + }, [dispatch]); + // 2. 再初始化事件追踪(传入 stockCode) const { trackStockSearched, @@ -71,7 +80,7 @@ const CompanyIndex = () => { inputCode={inputCode} onInputChange={setInputCode} onSearch={handleSearch} - onKeyPress={handleKeyPress} + onKeyDown={handleKeyDown} bgColor="#1A202C" /> @@ -83,7 +92,7 @@ const CompanyIndex = () => { /> {/* Tab 切换区域:概览、行情、财务、预测 */} - + {/* */} );