更新Company页面的UI为FUI风格
This commit is contained in:
@@ -1,45 +1,188 @@
|
||||
import React from "react";
|
||||
// src/components/Navbars/SearchBar/SearchBar.js
|
||||
// 全局股票搜索栏 - 模糊搜索 + 下拉选择
|
||||
|
||||
import React, { useRef, useEffect, useCallback } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {
|
||||
IconButton,
|
||||
Box,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputLeftElement,
|
||||
InputRightElement,
|
||||
IconButton,
|
||||
Text,
|
||||
VStack,
|
||||
HStack,
|
||||
Spinner,
|
||||
useColorModeValue,
|
||||
Tag,
|
||||
Center,
|
||||
List,
|
||||
ListItem,
|
||||
Flex,
|
||||
} from "@chakra-ui/react";
|
||||
import { SearchIcon } from "@chakra-ui/icons";
|
||||
import { SearchIcon, CloseIcon } from "@chakra-ui/icons";
|
||||
import { useStockSearch } from "@hooks/useStockSearch";
|
||||
|
||||
export function SearchBar(props) {
|
||||
// Pass the computed styles into the `__css` prop
|
||||
const { variant, children, ...rest } = props;
|
||||
// Chakra Color Mode
|
||||
const searchIconColor = useColorModeValue("gray.700", "gray.200");
|
||||
const inputBg = useColorModeValue("white", "navy.800");
|
||||
const navigate = useNavigate();
|
||||
const containerRef = useRef(null);
|
||||
|
||||
// 颜色配置
|
||||
const searchIconColor = useColorModeValue("gray.500", "gray.400");
|
||||
const inputBg = useColorModeValue("white", "whiteAlpha.100");
|
||||
const dropdownBg = useColorModeValue("white", "#1a1a2e");
|
||||
const borderColor = useColorModeValue("gray.200", "whiteAlpha.200");
|
||||
const hoverBg = useColorModeValue("gray.50", "whiteAlpha.100");
|
||||
const textColor = useColorModeValue("gray.800", "white");
|
||||
const subTextColor = useColorModeValue("gray.500", "whiteAlpha.600");
|
||||
const accentColor = useColorModeValue("blue.500", "#D4AF37");
|
||||
|
||||
// 使用搜索 Hook
|
||||
const {
|
||||
searchQuery,
|
||||
searchResults,
|
||||
isSearching,
|
||||
showResults,
|
||||
handleSearch,
|
||||
clearSearch,
|
||||
setShowResults,
|
||||
} = useStockSearch({ limit: 10, 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);
|
||||
}
|
||||
}, [searchResults, handleSelectStock, setShowResults]);
|
||||
|
||||
return (
|
||||
<InputGroup borderRadius='8px' w='200px' {...rest}>
|
||||
<InputLeftElement
|
||||
children={
|
||||
<IconButton
|
||||
bg='inherit'
|
||||
borderRadius='inherit'
|
||||
_hover={{}}
|
||||
_active={{
|
||||
bg: "inherit",
|
||||
transform: "none",
|
||||
borderColor: "transparent",
|
||||
}}
|
||||
_focus={{
|
||||
boxShadow: "none",
|
||||
}}
|
||||
icon={
|
||||
<SearchIcon color={searchIconColor} w='15px' h='15px' />
|
||||
}></IconButton>
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
variant='search'
|
||||
fontSize='xs'
|
||||
bg={inputBg}
|
||||
placeholder='Type here...'
|
||||
/>
|
||||
</InputGroup>
|
||||
<Box ref={containerRef} position="relative" {...rest}>
|
||||
<InputGroup borderRadius="8px" w="220px">
|
||||
<InputLeftElement pointerEvents="none">
|
||||
<SearchIcon color={searchIconColor} w="15px" h="15px" />
|
||||
</InputLeftElement>
|
||||
<Input
|
||||
variant="search"
|
||||
fontSize="sm"
|
||||
bg={inputBg}
|
||||
placeholder="搜索股票..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => handleSearch(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
onFocus={() => searchQuery && searchResults.length > 0 && setShowResults(true)}
|
||||
borderColor={borderColor}
|
||||
_hover={{ borderColor: accentColor }}
|
||||
_focus={{ borderColor: accentColor, boxShadow: `0 0 0 1px ${accentColor}` }}
|
||||
/>
|
||||
{(searchQuery || isSearching) && (
|
||||
<InputRightElement>
|
||||
{isSearching ? (
|
||||
<Spinner size="sm" color={accentColor} />
|
||||
) : (
|
||||
<IconButton
|
||||
size="xs"
|
||||
variant="ghost"
|
||||
icon={<CloseIcon w="10px" h="10px" />}
|
||||
onClick={clearSearch}
|
||||
aria-label="清除搜索"
|
||||
_hover={{ bg: "transparent" }}
|
||||
/>
|
||||
)}
|
||||
</InputRightElement>
|
||||
)}
|
||||
</InputGroup>
|
||||
|
||||
{/* 搜索结果下拉 */}
|
||||
{showResults && (
|
||||
<Box
|
||||
position="absolute"
|
||||
top="100%"
|
||||
left={0}
|
||||
mt={2}
|
||||
w="320px"
|
||||
bg={dropdownBg}
|
||||
border="1px solid"
|
||||
borderColor={borderColor}
|
||||
borderRadius="md"
|
||||
boxShadow="lg"
|
||||
maxH="400px"
|
||||
overflowY="auto"
|
||||
zIndex={9999}
|
||||
>
|
||||
{searchResults.length > 0 ? (
|
||||
<List spacing={0}>
|
||||
{searchResults.map((stock, index) => (
|
||||
<ListItem
|
||||
key={stock.stock_code}
|
||||
px={4}
|
||||
py={3}
|
||||
cursor="pointer"
|
||||
_hover={{ bg: hoverBg }}
|
||||
onClick={() => handleSelectStock(stock)}
|
||||
borderBottomWidth={index < searchResults.length - 1 ? "1px" : "0"}
|
||||
borderColor={borderColor}
|
||||
>
|
||||
<Flex align="center" justify="space-between">
|
||||
<VStack align="start" spacing={0} flex={1}>
|
||||
<Text fontWeight="bold" color={textColor} fontSize="sm">
|
||||
{stock.stock_name}
|
||||
</Text>
|
||||
<HStack spacing={2}>
|
||||
<Text fontSize="xs" color={subTextColor}>
|
||||
{stock.stock_code}
|
||||
</Text>
|
||||
{stock.pinyin_abbr && (
|
||||
<Text fontSize="xs" color={subTextColor}>
|
||||
({stock.pinyin_abbr.toUpperCase()})
|
||||
</Text>
|
||||
)}
|
||||
</HStack>
|
||||
</VStack>
|
||||
{stock.exchange && (
|
||||
<Tag
|
||||
size="sm"
|
||||
colorScheme="blue"
|
||||
variant="subtle"
|
||||
fontSize="xs"
|
||||
>
|
||||
{stock.exchange}
|
||||
</Tag>
|
||||
)}
|
||||
</Flex>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
) : (
|
||||
<Center p={4}>
|
||||
<Text color={subTextColor} fontSize="sm">
|
||||
{searchQuery ? "未找到相关股票" : "输入股票代码或名称搜索"}
|
||||
</Text>
|
||||
</Center>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user