From aacbe5c31c39c2f6dee82d4ee6dba81fa35c48df Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Mon, 27 Oct 2025 10:32:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E4=B8=AD=E5=BF=83=E6=90=9C=E7=B4=A2=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Community/components/UnifiedSearchBox.js | 218 +++++++++++++----- src/views/Community/hooks/useEventData.js | 60 +++-- src/views/Community/hooks/useEventFilters.js | 11 +- 3 files changed, 208 insertions(+), 81 deletions(-) diff --git a/src/views/Community/components/UnifiedSearchBox.js b/src/views/Community/components/UnifiedSearchBox.js index f3fc8dd6..45dfa04c 100644 --- a/src/views/Community/components/UnifiedSearchBox.js +++ b/src/views/Community/components/UnifiedSearchBox.js @@ -35,23 +35,18 @@ const UnifiedSearchBox = ({ const [importance, setImportance] = useState('all'); // 重要性 const [dateRange, setDateRange] = useState(null); // 日期范围 + // ✅ 本地输入状态 - 管理用户的实时输入 + const [inputValue, setInputValue] = useState(''); + // 使用全局行业数据 const { industryData, loadIndustryData, loading: industryLoading } = useIndustry(); - // ✅ 从 filters 推导搜索框显示值(受控状态) - const searchValue = useMemo(() => { - if (filters.stock_code && allStocks.length > 0) { - // 股票搜索模式:显示 "代码 名称" - const stock = allStocks.find(s => s.code === filters.stock_code); - return stock ? `${stock.code} ${stock.name}` : filters.stock_code; - } - // 话题搜索模式:显示关键词 - return filters.q || ''; - }, [filters.stock_code, filters.q, allStocks]); - // 搜索触发函数 const triggerSearch = useCallback((params) => { - logger.debug('UnifiedSearchBox', '触发搜索', params); + logger.debug('UnifiedSearchBox', '【5/5】✅ 最终触发搜索 - 调用onSearch回调', { + params: params, + timestamp: new Date().toISOString() + }); onSearch(params); }, [onSearch]); @@ -61,7 +56,10 @@ const UnifiedSearchBox = ({ useEffect(() => { // 创建防抖函数,使用 triggerSearch 而不是直接调用 onSearch debouncedSearchRef.current = debounce((params) => { - logger.debug('UnifiedSearchBox', '防抖搜索触发', params); + logger.debug('UnifiedSearchBox', '⏱️ 防抖延迟结束,执行搜索', { + params: params, + delayMs: 300 + }); triggerSearch(params); }, 300); @@ -116,7 +114,7 @@ const UnifiedSearchBox = ({ return null; }, []); - // ✅ 从 props.filters 初始化筛选条件(不包括搜索值,搜索值由 searchValue useMemo 推导) + // ✅ 从 props.filters 初始化筛选条件和输入框值 useEffect(() => { if (!filters) return; @@ -146,7 +144,15 @@ const UnifiedSearchBox = ({ }); } } - }, [filters.sort, filters.importance, filters.date_range, filters.industry_code, industryData, findIndustryPath]); + + // ✅ 同步 filters.q 到输入框显示值 + if (filters.q) { + setInputValue(filters.q); + } else if (!filters.q) { + // 如果 filters 中没有搜索关键词,清空输入框 + setInputValue(''); + } + }, [filters.sort, filters.importance, filters.date_range, filters.industry_code, filters.q, industryData, findIndustryPath]); // AutoComplete 搜索股票(模糊匹配 code 或 name) const handleSearch = (value) => { @@ -179,7 +185,7 @@ const UnifiedSearchBox = ({ }); }; - // ✅ 选中股票(从下拉选择) - 直接构建参数并触发搜索 + // ✅ 选中股票(从下拉选择) - 更新输入框并触发搜索 const handleStockSelect = (_value, option) => { const stockInfo = option.stockInfo; if (stockInfo) { @@ -188,12 +194,13 @@ const UnifiedSearchBox = ({ name: stockInfo.name }); - // 直接构建参数并触发搜索(股票搜索模式) + // 更新输入框显示 + setInputValue(`${stockInfo.code} ${stockInfo.name}`); + + // 直接构建参数并触发搜索 - 使用股票代码作为 q 参数 const params = buildFilterParams({ - stock_code: stockInfo.code, - q: '', - industry_code: '', - industry_classification: '' + q: stockInfo.code, // 使用股票代码作为搜索关键词 + industry_code: '' }); logger.debug('UnifiedSearchBox', '自动触发股票搜索', params); triggerSearch(params); @@ -202,53 +209,119 @@ const UnifiedSearchBox = ({ // ✅ 日期范围变化(使用防抖) const handleDateRangeChange = (dates) => { + logger.debug('UnifiedSearchBox', '【1/5】日期范围值改变', { + oldValue: dateRange, + newValue: dates + }); setDateRange(dates); - // 使用防抖搜索(与其他筛选器保持一致) - const params = buildFilterParams(); - logger.debug('UnifiedSearchBox', '日期范围变化,准备触发搜索', { dates, params }); + + // ⚠️ 注意:setState是异步的,此时dateRange仍是旧值 + logger.debug('UnifiedSearchBox', '【2/5】调用buildFilterParams前的状态', { + dateRange: dateRange, // 旧值 + sort: sort, + importance: importance, + industryValue: industryValue + }); + + // 使用防抖搜索(需要从新值推导参数) + const params = { + ...buildFilterParams(), + date_range: dates ? `${dates[0].format('YYYY-MM-DD')} 至 ${dates[1].format('YYYY-MM-DD')}` : '' + }; + logger.debug('UnifiedSearchBox', '【3/5】buildFilterParams返回的参数', params); + if (debouncedSearchRef.current) { + logger.debug('UnifiedSearchBox', '【4/5】调用防抖函数(300ms延迟)'); debouncedSearchRef.current(params); } }; // ✅ 重要性变化(使用防抖) const handleImportanceChange = (value) => { + logger.debug('UnifiedSearchBox', '【1/5】重要性值改变', { + oldValue: importance, + newValue: value + }); setImportance(value); + + // ⚠️ 注意:setState是异步的,此时importance仍是旧值 + logger.debug('UnifiedSearchBox', '【2/5】调用buildFilterParams前的状态', { + importance: importance, // 旧值 + sort: sort, + dateRange: dateRange, + industryValue: industryValue + }); + // 使用防抖搜索 const params = buildFilterParams({ importance: value }); - logger.debug('UnifiedSearchBox', '重要性变化,准备触发搜索', params); + logger.debug('UnifiedSearchBox', '【3/5】buildFilterParams返回的参数', params); + if (debouncedSearchRef.current) { + logger.debug('UnifiedSearchBox', '【4/5】调用防抖函数(300ms延迟)'); debouncedSearchRef.current(params); } }; // ✅ 排序变化(使用防抖) const handleSortChange = (value) => { + logger.debug('UnifiedSearchBox', '【1/5】排序值改变', { + oldValue: sort, + newValue: value + }); setSort(value); + + // ⚠️ 注意:setState是异步的,此时sort仍是旧值 + logger.debug('UnifiedSearchBox', '【2/5】调用buildFilterParams前的状态', { + sort: sort, // 旧值 + importance: importance, + dateRange: dateRange, + industryValue: industryValue + }); + // 使用防抖搜索 const params = buildFilterParams({ sort: value }); - logger.debug('UnifiedSearchBox', '排序变化,准备触发搜索', params); + logger.debug('UnifiedSearchBox', '【3/5】buildFilterParams返回的参数', params); + if (debouncedSearchRef.current) { + logger.debug('UnifiedSearchBox', '【4/5】调用防抖函数(300ms延迟)'); debouncedSearchRef.current(params); } }; // ✅ 行业分类变化(使用防抖) const handleIndustryChange = (value) => { - setIndustryValue(value); - // 使用防抖搜索 - const params = buildFilterParams(); - logger.debug('UnifiedSearchBox', '行业分类变化,准备触发搜索', { - industry: value, - params + logger.debug('UnifiedSearchBox', '【1/5】行业分类值改变', { + oldValue: industryValue, + newValue: value }); + setIndustryValue(value); + + // ⚠️ 注意:setState是异步的,此时industryValue仍是旧值 + logger.debug('UnifiedSearchBox', '【2/5】调用buildFilterParams前的状态', { + industryValue: industryValue, // 旧值 + sort: sort, + importance: importance, + dateRange: dateRange + }); + + // 使用防抖搜索 (需要从新值推导参数) + const params = { + ...buildFilterParams(), + industry_code: value?.[value.length - 1] || '' + }; + logger.debug('UnifiedSearchBox', '【3/5】buildFilterParams返回的参数', params); + if (debouncedSearchRef.current) { + logger.debug('UnifiedSearchBox', '【4/5】调用防抖函数(300ms延迟)'); debouncedSearchRef.current(params); } }; - // ✅ 热门概念点击处理(立即搜索,不使用防抖) - 直接构建参数并触发搜索 + // ✅ 热门概念点击处理(立即搜索,不使用防抖) - 更新输入框并触发搜索 const handleKeywordClick = (keyword) => { + // 更新输入框显示 + setInputValue(keyword); + // 立即触发搜索(取消之前的防抖) if (debouncedSearchRef.current) { debouncedSearchRef.current.cancel(); @@ -256,9 +329,7 @@ const UnifiedSearchBox = ({ const params = buildFilterParams({ q: keyword, - stock_code: '', - industry_code: '', - industry_classification: '' + industry_code: '' }); logger.debug('UnifiedSearchBox', '热门概念点击,立即触发搜索', { keyword, @@ -269,37 +340,62 @@ const UnifiedSearchBox = ({ // 主搜索(点击搜索按钮或回车) const handleMainSearch = () => { - // 直接输入文本时,构建参数并触发搜索 - logger.debug('UnifiedSearchBox', '主搜索触发', { searchValue }); - handleApplyFilters(); + // 取消之前的防抖 + if (debouncedSearchRef.current) { + debouncedSearchRef.current.cancel(); + } + + // 构建参数并触发搜索 - 使用用户输入作为 q 参数 + const params = buildFilterParams({ + q: inputValue, // 使用用户输入(可能是话题、股票代码、股票名称等) + industry_code: '' + }); + + logger.debug('UnifiedSearchBox', '主搜索触发', { + inputValue, + params + }); + triggerSearch(params); }; - // ✅ 处理输入变化 - AutoComplete 内部处理显示,不需要更新状态 + // ✅ 处理输入变化 - 更新本地输入状态 const handleInputChange = (value) => { - // AutoComplete 组件会自动处理显示值 - // 这里可以添加其他逻辑(如实时搜索建议) logger.debug('UnifiedSearchBox', '输入变化', { value }); + setInputValue(value); }; // ✅ 生成完整的筛选参数对象 - 直接从 filters 和本地筛选器状态构建 const buildFilterParams = useCallback((overrides = {}) => { - return { + logger.debug('UnifiedSearchBox', '🔧 buildFilterParams - 输入参数', { + overrides: overrides, + currentState: { + sort, + importance, + dateRange, + industryValue, + 'filters.q': filters.q + } + }); + + const result = { // 基础参数(overrides 优先级高于本地状态) sort: overrides.sort ?? sort, importance: overrides.importance ?? importance, date_range: dateRange ? `${dateRange[0].format('YYYY-MM-DD')} 至 ${dateRange[1].format('YYYY-MM-DD')}` : '', page: 1, - // 搜索参数(从 filters 继承,overrides 可覆盖) + // 搜索参数: 统一使用 q 参数进行搜索(话题/股票/关键词) q: overrides.q ?? filters.q ?? '', - stock_code: overrides.stock_code ?? filters.stock_code ?? '', + // 行业代码: 取选中路径的最后一级(最具体的行业代码) industry_code: overrides.industry_code ?? (industryValue?.[industryValue.length - 1] || ''), - industry_classification: overrides.industry_classification ?? (industryValue?.[0] || ''), // 最终 overrides 具有最高优先级 ...overrides }; - }, [sort, importance, dateRange, filters.q, filters.stock_code, industryValue]); + + logger.debug('UnifiedSearchBox', '🔧 buildFilterParams - 输出结果', result); + return result; + }, [sort, importance, dateRange, filters.q, industryValue]); // ✅ 应用筛选(立即搜索,取消防抖) const handleApplyFilters = () => { @@ -316,6 +412,7 @@ const UnifiedSearchBox = ({ // ✅ 重置筛选 - 清空所有筛选器并触发搜索 const handleReset = () => { // 重置所有筛选器状态 + setInputValue(''); // 清空输入框 setStockOptions([]); setIndustryValue([]); setSort('new'); @@ -325,9 +422,7 @@ const UnifiedSearchBox = ({ // 输出重置后的完整参数 const resetParams = { q: '', - stock_code: '', industry_code: '', - industry_classification: '', sort: 'new', importance: 'all', date_range: '', @@ -342,13 +437,9 @@ const UnifiedSearchBox = ({ const filterTags = useMemo(() => { const tags = []; - // 股票/话题标签 - 从 filters 读取 - if (filters.stock_code) { - const stock = allStocks.find(s => s.code === filters.stock_code); - const label = stock ? `${stock.code} ${stock.name}` : filters.stock_code; - tags.push({ key: 'stock', label: `股票: ${label}` }); - } else if (filters.q) { - tags.push({ key: 'topic', label: `话题: ${filters.q}` }); + // 搜索关键词标签 - 从 filters.q 读取 + if (filters.q) { + tags.push({ key: 'search', label: `搜索: ${filters.q}` }); } // 行业标签 @@ -375,21 +466,22 @@ const UnifiedSearchBox = ({ } return tags; - }, [filters.stock_code, filters.q, allStocks, industryValue, dateRange, importance, sort]); + }, [filters.q, industryValue, dateRange, importance, sort]); // ✅ 移除单个标签 - 构建新参数并触发搜索 const handleRemoveTag = (key) => { logger.debug('UnifiedSearchBox', '移除标签', { key }); - if (key === 'topic' || key === 'stock') { - // 清除搜索关键词/股票,立即触发搜索 - const params = buildFilterParams({ q: '', stock_code: '' }); + if (key === 'search') { + // 清除搜索关键词和输入框,立即触发搜索 + setInputValue(''); // 清空输入框 + const params = buildFilterParams({ q: '' }); logger.debug('UnifiedSearchBox', '移除搜索标签后触发搜索', { key, params }); triggerSearch(params); } else if (key === 'industry') { // 清除行业选择 setIndustryValue([]); - const params = buildFilterParams({ industry_code: '', industry_classification: '' }); + const params = buildFilterParams({ industry_code: '' }); triggerSearch(params); } else if (key === 'date_range') { // 清除日期范围 @@ -425,7 +517,7 @@ const UnifiedSearchBox = ({ color: '#666' }} />