更新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");
return ( // 颜色配置
<InputGroup borderRadius='8px' w='200px' {...rest}> const searchIconColor = useColorModeValue("gray.500", "gray.400");
<InputLeftElement const inputBg = useColorModeValue("white", "whiteAlpha.100");
children={ const dropdownBg = useColorModeValue("white", "#1a1a2e");
<IconButton const borderColor = useColorModeValue("gray.200", "whiteAlpha.200");
bg='inherit' const hoverBg = useColorModeValue("gray.50", "whiteAlpha.100");
borderRadius='inherit' const textColor = useColorModeValue("gray.800", "white");
_hover={{}} const subTextColor = useColorModeValue("gray.500", "whiteAlpha.600");
_active={{ const accentColor = useColorModeValue("blue.500", "#D4AF37");
bg: "inherit",
transform: "none", // 使用搜索 Hook
borderColor: "transparent", const {
}} searchQuery,
_focus={{ searchResults,
boxShadow: "none", isSearching,
}} showResults,
icon={ handleSearch,
<SearchIcon color={searchIconColor} w='15px' h='15px' /> clearSearch,
}></IconButton> 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 (
<Box ref={containerRef} position="relative" {...rest}>
<InputGroup borderRadius="8px" w="220px">
<InputLeftElement pointerEvents="none">
<SearchIcon color={searchIconColor} w="15px" h="15px" />
</InputLeftElement>
<Input <Input
variant='search' variant="search"
fontSize='xs' fontSize="sm"
bg={inputBg} bg={inputBg}
placeholder='Type here...' 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> </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>
); );
} }