// src/views/Community/components/UnifiedSearchBox.js // 搜索组件:三行布局(主搜索 + 热门概念 + 筛选区) import React, { useState, useMemo, useEffect } from 'react'; import { Card, Input, Cascader, Button, Space, Tag, AutoComplete, DatePicker, Select as AntSelect } from 'antd'; import { SearchOutlined, CloseCircleOutlined, StockOutlined } from '@ant-design/icons'; import moment from 'moment'; import locale from 'antd/es/date-picker/locale/zh_CN'; import { useIndustry } from '../../../contexts/IndustryContext'; import { stockService } from '../../../services/stockService'; import { logger } from '../../../utils/logger'; import PopularKeywords from './PopularKeywords'; const { RangePicker } = DatePicker; const { Option } = AntSelect; const UnifiedSearchBox = ({ onSearch, popularKeywords = [], filters = {}, loading = false }) => { const [searchValue, setSearchValue] = useState(''); // 统一搜索值 const [isStockSearch, setIsStockSearch] = useState(false); // 是否股票搜索 const [stockOptions, setStockOptions] = useState([]); // 股票下拉选项列表 const [allStocks, setAllStocks] = useState([]); // 所有股票数据 const [industryValue, setIndustryValue] = useState([]); // 使用全局行业数据 const { industryData, loadIndustryData, loading: industryLoading } = useIndustry(); // 加载所有股票数据 useEffect(() => { const loadStocks = async () => { const response = await stockService.getAllStocks(); if (response.success && response.data) { setAllStocks(response.data); logger.debug('UnifiedSearchBox', '股票数据加载成功', { count: response.data.length }); } }; loadStocks(); }, []); // Cascader 获得焦点时加载数据 const handleCascaderFocus = async () => { if (!industryData || industryData.length === 0) { logger.debug('UnifiedSearchBox', 'Cascader 获得焦点,开始加载行业数据'); await loadIndustryData(); } }; // ⚡ 提取 filters 中的原始值,避免对象引用导致无限循环 const stockCode = filters.stock_code; const q = filters.q; const industryCode = filters.industry_code; // 初始化:从 URL 恢复状态 React.useEffect(() => { if (stockCode) { setSearchValue(stockCode); setIsStockSearch(true); } else if (q) { setSearchValue(q); setIsStockSearch(false); } if (industryCode) { // TODO: 从 industry_code 恢复 industryValue 需要查找对应路径 } }, [stockCode, q, industryCode]); // ⚡ 只依赖原始值,不依赖整个 filters 对象 // AutoComplete 搜索股票(模糊匹配 code 或 name) const handleSearch = (value) => { if (!value || !allStocks || allStocks.length === 0) { setStockOptions([]); return; } // 使用 stockService 进行模糊搜索 const results = stockService.fuzzySearch(value, allStocks, 10); // 转换为 AutoComplete 选项格式 const options = results.map(stock => ({ value: stock.code, label: (
{stock.code} {stock.name}
), // 保存完整的股票信息,用于选中后显示 stockInfo: stock })); setStockOptions(options); logger.debug('UnifiedSearchBox', '股票模糊搜索', { query: value, resultCount: options.length }); }; // 选中股票(从下拉选择) const handleStockSelect = (value, option) => { const stockInfo = option.stockInfo; if (stockInfo) { // 显示格式:股票代码 + 股票名称 const displayValue = `${stockInfo.code} ${stockInfo.name}`; setSearchValue(displayValue); setIsStockSearch(true); logger.debug('UnifiedSearchBox', '选中股票', { code: stockInfo.code, name: stockInfo.name }); } }; // 日期范围变化 const handleDateRangeChange = (dates) => { // 这里不直接调用 onSearch,等用户点击搜索按钮 logger.debug('UnifiedSearchBox', '日期范围变化', { dates }); }; // 重要性变化 const handleImportanceChange = (value) => { logger.debug('UnifiedSearchBox', '重要性变化', { value }); }; // 排序变化 const handleSortChange = (value) => { // 排序直接生效 onSearch({ sort: value, page: 1 }); }; // 主搜索(点击搜索按钮或回车) const handleMainSearch = () => { // 直接输入文本(未选择下拉股票)时,作为话题搜索 if (!isStockSearch && searchValue) { logger.debug('UnifiedSearchBox', '话题搜索', { topic: searchValue }); } handleApplyFilters(); }; // 处理输入变化 const handleInputChange = (value) => { setSearchValue(value); // 输入变化时重置股票搜索标记(只有从下拉选择才是股票搜索) if (isStockSearch) { setIsStockSearch(false); logger.debug('UnifiedSearchBox', '切换为话题搜索模式'); } }; // 应用筛选 const handleApplyFilters = () => { const params = { page: 1 }; if (isStockSearch && searchValue) { // 股票搜索模式:从下拉选择的股票 // 提取股票代码(searchValue 格式:"000001 平安银行") const stockCode = searchValue.split(' ')[0]; params.stock_code = stockCode; params.q = ''; params.industry_code = ''; params.industry_classification = ''; logger.debug('UnifiedSearchBox', '应用股票筛选', { stockCode }); } else { // 话题搜索模式:直接输入的文本 params.q = searchValue || ''; params.stock_code = ''; if (industryValue && industryValue.length > 0) { params.industry_code = industryValue[industryValue.length - 1]; params.industry_classification = industryValue[0]; } else { params.industry_code = ''; params.industry_classification = ''; } logger.debug('UnifiedSearchBox', '应用话题筛选', { topic: searchValue }); } onSearch(params); }; // 重置筛选 const handleReset = () => { setSearchValue(''); setIsStockSearch(false); setStockOptions([]); setIndustryValue([]); onSearch({ q: '', stock_code: '', industry_code: '', industry_classification: '', sort: 'new', importance: 'all', page: 1 }); }; // 生成已选条件标签 const filterTags = useMemo(() => { const tags = []; if (isStockSearch && searchValue) { tags.push({ key: 'stock', label: `股票: ${searchValue}` }); } else if (!isStockSearch && searchValue) { tags.push({ key: 'topic', label: `话题: ${searchValue}` }); } if (industryValue && industryValue.length > 0) { const industryLabel = industryValue.slice(1).join(' > '); tags.push({ key: 'industry', label: `行业: ${industryLabel}` }); } return tags; }, [searchValue, isStockSearch, industryValue]); // 移除单个标签 const handleRemoveTag = (key) => { if (key === 'topic' || key === 'stock') { setSearchValue(''); setIsStockSearch(false); } else if (key === 'industry') { setIndustryValue([]); } setTimeout(handleApplyFilters, 100); }; return ( {/* 第一行:主搜索框 */} {/* 第二行:热门概念 */}
{/* 第三行:筛选器 + 排序 */} {/* 左侧:筛选器组 */} 筛选: {/* 行业分类 */} path.some(option => option.label.toLowerCase().includes(inputValue.toLowerCase()) ) }} allowClear expandTrigger="hover" displayRender={(labels) => labels.join(' > ')} disabled={industryLoading} loading={industryLoading} style={{ width: 200 }} size="middle" /> {/* 日期范围 */} {/* 重要性 */} 重要性: {/* 重置按钮 - 现代化设计 */} {/* 右侧:排序 */} 排序: {/* 已选条件标签 */} {filterTags.length > 0 && ( {filterTags.map(tag => ( handleRemoveTag(tag.key)} color="blue" > {tag.label} ))} )}
); }; export default UnifiedSearchBox;