- 新增 useStockSearch.ts:统一股票模糊搜索逻辑 - 支持按代码或名称搜索 - 支持排除指定股票(用于对比场景) - 使用 useMemo 优化性能 - 重构 SearchBar.js:使用共享 Hook,减少 15 行代码 - 重构 CompareStockInput.tsx:使用共享 Hook,减少 20 行代码 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
148 lines
4.2 KiB
JavaScript
148 lines
4.2 KiB
JavaScript
// src/views/Company/components/CompanyHeader/SearchBar.js
|
|
// 股票搜索栏组件 - 金色主题 + 模糊搜索下拉
|
|
|
|
import React, { useState, useEffect, useRef } from 'react';
|
|
import { useSelector } from 'react-redux';
|
|
import {
|
|
Box,
|
|
HStack,
|
|
Input,
|
|
InputGroup,
|
|
InputLeftElement,
|
|
Text,
|
|
VStack,
|
|
} from '@chakra-ui/react';
|
|
import { SearchIcon } from '@chakra-ui/icons';
|
|
import { useStockSearch } from '../../hooks/useStockSearch';
|
|
|
|
/**
|
|
* 股票搜索栏组件(带模糊搜索下拉)
|
|
*
|
|
* @param {Object} props
|
|
* @param {string} props.inputCode - 输入框当前值
|
|
* @param {Function} props.onInputChange - 输入变化回调
|
|
* @param {Function} props.onSearch - 搜索按钮点击回调
|
|
* @param {Function} props.onKeyDown - 键盘事件回调
|
|
*/
|
|
const SearchBar = ({
|
|
inputCode,
|
|
onInputChange,
|
|
onSearch,
|
|
onKeyDown,
|
|
}) => {
|
|
// 下拉状态
|
|
const [showDropdown, setShowDropdown] = useState(false);
|
|
const containerRef = useRef(null);
|
|
|
|
// 从 Redux 获取全部股票列表
|
|
const allStocks = useSelector(state => state.stock.allStocks);
|
|
|
|
// 使用共享的搜索 Hook
|
|
const filteredStocks = useStockSearch(allStocks, inputCode, { limit: 10 });
|
|
|
|
// 根据搜索结果更新下拉显示状态
|
|
useEffect(() => {
|
|
setShowDropdown(filteredStocks.length > 0 && !!inputCode?.trim());
|
|
}, [filteredStocks, inputCode]);
|
|
|
|
// 点击外部关闭下拉
|
|
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);
|
|
onSearch(stock.code);
|
|
};
|
|
|
|
// 处理键盘事件
|
|
const handleKeyDownWrapper = (e) => {
|
|
if (e.key === 'Enter') {
|
|
setShowDropdown(false);
|
|
}
|
|
onKeyDown?.(e);
|
|
};
|
|
|
|
return (
|
|
<Box ref={containerRef} position="relative" w="300px">
|
|
<InputGroup size="lg">
|
|
<InputLeftElement pointerEvents="none">
|
|
<SearchIcon color="#C9A961" />
|
|
</InputLeftElement>
|
|
<Input
|
|
placeholder="输入股票代码或名称"
|
|
value={inputCode}
|
|
onChange={(e) => 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',
|
|
}}
|
|
/>
|
|
</InputGroup>
|
|
|
|
{/* 模糊搜索下拉列表 */}
|
|
{showDropdown && (
|
|
<Box
|
|
position="absolute"
|
|
top="100%"
|
|
left={0}
|
|
mt={1}
|
|
w="100%"
|
|
bg="#1A202C"
|
|
border="1px solid #C9A961"
|
|
borderRadius="md"
|
|
maxH="300px"
|
|
overflowY="auto"
|
|
zIndex={1000}
|
|
boxShadow="0 4px 12px rgba(0, 0, 0, 0.3)"
|
|
>
|
|
<VStack align="stretch" spacing={0}>
|
|
{filteredStocks.map((stock) => (
|
|
<Box
|
|
key={stock.code}
|
|
px={4}
|
|
py={2}
|
|
cursor="pointer"
|
|
_hover={{ bg: 'whiteAlpha.100' }}
|
|
onClick={() => handleSelectStock(stock)}
|
|
borderBottom="1px solid"
|
|
borderColor="whiteAlpha.100"
|
|
_last={{ borderBottom: 'none' }}
|
|
>
|
|
<HStack justify="space-between">
|
|
<Text color="#F4D03F" fontWeight="bold" fontSize="sm">
|
|
{stock.code}
|
|
</Text>
|
|
<Text color="#C9A961" fontSize="sm" noOfLines={1} maxW="180px">
|
|
{stock.name}
|
|
</Text>
|
|
</HStack>
|
|
</Box>
|
|
))}
|
|
</VStack>
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default SearchBar;
|