/**
* Company 页面顶部搜索栏组件 - FUI 科幻风格
*
* 设计特点:
* - Glassmorphism 毛玻璃背景
* - 发光效果和微动画
* - Ash Thorp 风格的数据展示
* - James Turrell 柔和光影
*/
import React, { memo, useMemo, useCallback, useState } from 'react';
import {
Box,
Flex,
HStack,
VStack,
Text,
Button,
Icon,
Skeleton,
} from '@chakra-ui/react';
import { AutoComplete, Spin } from 'antd';
import { Search, Star, TrendingUp, TrendingDown } from 'lucide-react';
import { useStockSearch } from '@hooks/useStockSearch';
import { THEME, getSearchBoxStyles } from '../../config';
import { FUI_COLORS, FUI_GLOW, FUI_ANIMATION, FUI_GLASS } from '../../theme/fui';
import type { CompanyHeaderProps, StockSearchResult } from '../../types';
/**
* 股票信息展示组件 - FUI 风格
*/
const StockInfoDisplay = memo<{
stockCode: string;
stockName?: string;
price?: number | null;
change?: number | null;
loading: boolean;
}>(({ stockCode, stockName, price, change, loading }) => {
if (loading) {
return (
);
}
const isPositive = change !== null && change !== undefined && change >= 0;
const TrendIcon = isPositive ? TrendingUp : TrendingDown;
return (
{/* 股票代码 & 名称 */}
{stockCode}
{stockName && (
{stockName}
)}
{/* 价格 & 涨跌幅 */}
{price !== null && price !== undefined && (
{/* 价格 */}
Price
¥{price.toFixed(2)}
{/* 涨跌幅 Badge */}
{change !== null && change !== undefined && (
{isPositive ? '+' : ''}{change.toFixed(2)}%
)}
)}
);
});
StockInfoDisplay.displayName = 'StockInfoDisplay';
/**
* 搜索操作区组件(状态自管理,减少父组件重渲染)
*/
const SearchActions = memo<{
stockCode: string;
onStockChange: (value: string) => void;
isInWatchlist: boolean;
watchlistLoading: boolean;
onWatchlistToggle: () => void;
}>(({
stockCode,
onStockChange,
isInWatchlist,
watchlistLoading,
onWatchlistToggle,
}) => {
// 输入状态自管理(避免父组件重渲染)
const [inputCode, setInputCode] = useState(stockCode);
// 同步外部 stockCode 变化
React.useEffect(() => {
setInputCode(stockCode);
}, [stockCode]);
// 股票搜索 Hook
const searchHook = useStockSearch({
limit: 10,
debounceMs: 300,
onSearch: () => {}, // 空回调,追踪在父组件处理
}) as {
searchResults: StockSearchResult[];
isSearching: boolean;
handleSearch: (query: string) => void;
clearSearch: () => void;
};
const { searchResults, isSearching, handleSearch: doSearch, clearSearch } = searchHook;
// 转换为 AutoComplete options
const stockOptions = useMemo(() => {
return searchResults.map((stock: StockSearchResult) => ({
value: stock.stock_code,
label: (
{stock.stock_code}
{stock.stock_name}
{stock.pinyin_abbr && (
{stock.pinyin_abbr.toUpperCase()}
)}
),
}));
}, [searchResults]);
// 处理搜索按钮点击
const handleSearch = useCallback(() => {
if (inputCode && inputCode !== stockCode) {
onStockChange(inputCode);
}
}, [inputCode, stockCode, onStockChange]);
// 选中股票
const handleSelect = useCallback((value: string) => {
clearSearch();
setInputCode(value);
if (value !== stockCode) {
onStockChange(value);
}
}, [clearSearch, stockCode, onStockChange]);
// 键盘事件
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
handleSearch();
}
}, [handleSearch]);
return (
{/* 搜索框 - FUI 风格 */}
: null}
onKeyDown={handleKeyDown}
/>
{/* 搜索按钮 - 发光效果 */}
}
fontWeight="bold"
borderRadius="10px"
transition={`all ${FUI_ANIMATION.duration.fast} ${FUI_ANIMATION.easing.default}`}
>
查询
{/* 自选按钮 - FUI 风格 */}
}
fontWeight="bold"
borderRadius="10px"
transition={`all ${FUI_ANIMATION.duration.fast} ${FUI_ANIMATION.easing.default}`}
sx={isInWatchlist ? { animation: 'glowPulse 3s ease-in-out infinite' } : undefined}
>
{isInWatchlist ? '已自选' : '自选'}
);
});
SearchActions.displayName = 'SearchActions';
/**
* Company 页面顶部组件
*/
const CompanyHeader: React.FC = memo(({
stockCode,
stockInfo,
stockInfoLoading,
isInWatchlist,
watchlistLoading,
onStockChange,
onWatchlistToggle,
}) => {
return (
{/* 顶部发光线(环境光效果由全局 AmbientGlow 提供) */}
{/* 左侧:股票信息 */}
{/* 右侧:搜索和操作 */}
);
});
CompanyHeader.displayName = 'CompanyHeader';
export default CompanyHeader;