更新Company页面的UI为FUI风格

This commit is contained in:
2025-12-18 00:05:55 +08:00
parent 852438b17e
commit 3199e6764d

View File

@@ -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 { import {
IconButton, Box,
Input, Input,
InputGroup, InputGroup,
InputLeftElement, InputLeftElement,
InputRightElement,
IconButton,
Text,
VStack,
HStack,
Spinner,
useColorModeValue, useColorModeValue,
Tag,
Center,
List,
ListItem,
Flex,
} from "@chakra-ui/react"; } 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) { export function SearchBar(props) {
// Pass the computed styles into the `__css` prop
const { variant, children, ...rest } = props; const { variant, children, ...rest } = props;
// Chakra Color Mode const navigate = useNavigate();
const searchIconColor = useColorModeValue("gray.700", "gray.200"); const containerRef = useRef(null);
const inputBg = useColorModeValue("white", "navy.800");
// 颜色配置
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 ( return (
<InputGroup borderRadius='8px' w='200px' {...rest}> <Box ref={containerRef} position="relative" {...rest}>
<InputLeftElement <InputGroup borderRadius="8px" w="220px">
children={ <InputLeftElement pointerEvents="none">
<IconButton <SearchIcon color={searchIconColor} w="15px" h="15px" />
bg='inherit' </InputLeftElement>
borderRadius='inherit' <Input
_hover={{}} variant="search"
_active={{ fontSize="sm"
bg: "inherit", bg={inputBg}
transform: "none", placeholder="搜索股票..."
borderColor: "transparent", value={searchQuery}
}} onChange={(e) => handleSearch(e.target.value)}
_focus={{ onKeyDown={handleKeyDown}
boxShadow: "none", onFocus={() => searchQuery && searchResults.length > 0 && setShowResults(true)}
}} borderColor={borderColor}
icon={ _hover={{ borderColor: accentColor }}
<SearchIcon color={searchIconColor} w='15px' h='15px' /> _focus={{ borderColor: accentColor, boxShadow: `0 0 0 1px ${accentColor}` }}
}></IconButton> />
} {(searchQuery || isSearching) && (
/> <InputRightElement>
<Input {isSearching ? (
variant='search' <Spinner size="sm" color={accentColor} />
fontSize='xs' ) : (
bg={inputBg} <IconButton
placeholder='Type here...' size="xs"
/> variant="ghost"
</InputGroup> 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>
); );
} }