feat: 调整请求参数

This commit is contained in:
zdl
2025-10-26 23:46:54 +08:00
parent 97c5ce0d4d
commit 5b25136c28
2 changed files with 97 additions and 148 deletions

View File

@@ -57,7 +57,6 @@ const EventTimelineCard = forwardRef(({
onSearch={onSearch} onSearch={onSearch}
popularKeywords={popularKeywords} popularKeywords={popularKeywords}
filters={filters} filters={filters}
loading={loading}
/> />
</Box> </Box>

View File

@@ -8,6 +8,7 @@ import {
SearchOutlined, CloseCircleOutlined, StockOutlined SearchOutlined, CloseCircleOutlined, StockOutlined
} from '@ant-design/icons'; } from '@ant-design/icons';
import moment from 'moment'; import moment from 'moment';
import dayjs from 'dayjs';
import locale from 'antd/es/date-picker/locale/zh_CN'; import locale from 'antd/es/date-picker/locale/zh_CN';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import { useIndustry } from '../../../contexts/IndustryContext'; import { useIndustry } from '../../../contexts/IndustryContext';
@@ -21,17 +22,15 @@ const { Option } = AntSelect;
const UnifiedSearchBox = ({ const UnifiedSearchBox = ({
onSearch, onSearch,
popularKeywords = [], popularKeywords = [],
filters = {}, filters = {}
loading = false
}) => { }) => {
const [searchValue, setSearchValue] = useState(''); // 统一搜索值
const [isStockSearch, setIsStockSearch] = useState(false); // 是否股票搜索 // 其他状态
const [stockOptions, setStockOptions] = useState([]); // 股票下拉选项列表 const [stockOptions, setStockOptions] = useState([]); // 股票下拉选项列表
const [allStocks, setAllStocks] = useState([]); // 所有股票数据 const [allStocks, setAllStocks] = useState([]); // 所有股票数据
const [industryValue, setIndustryValue] = useState([]); const [industryValue, setIndustryValue] = useState([]);
const [selectedStockInfo, setSelectedStockInfo] = useState(null); // 保存选中的股票完整信息
// 新增:管理所有筛选条件 // 筛选条件状态
const [sort, setSort] = useState('new'); // 排序方式 const [sort, setSort] = useState('new'); // 排序方式
const [importance, setImportance] = useState('all'); // 重要性 const [importance, setImportance] = useState('all'); // 重要性
const [dateRange, setDateRange] = useState(null); // 日期范围 const [dateRange, setDateRange] = useState(null); // 日期范围
@@ -39,14 +38,31 @@ const UnifiedSearchBox = ({
// 使用全局行业数据 // 使用全局行业数据
const { industryData, loadIndustryData, loading: industryLoading } = useIndustry(); 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);
onSearch(params);
}, [onSearch]);
// ✅ 创建防抖的搜索函数300ms 延迟) // ✅ 创建防抖的搜索函数300ms 延迟)
const debouncedSearchRef = useRef(null); const debouncedSearchRef = useRef(null);
useEffect(() => { useEffect(() => {
// 创建防抖函数 // 创建防抖函数,使用 triggerSearch 而不是直接调用 onSearch
debouncedSearchRef.current = debounce((params) => { debouncedSearchRef.current = debounce((params) => {
logger.debug('UnifiedSearchBox', '防抖搜索触发', params); logger.debug('UnifiedSearchBox', '防抖搜索触发', params);
onSearch(params); triggerSearch(params);
}, 300); }, 300);
// 清理函数 // 清理函数
@@ -55,7 +71,7 @@ const UnifiedSearchBox = ({
debouncedSearchRef.current.cancel(); debouncedSearchRef.current.cancel();
} }
}; };
}, [onSearch]); }, [triggerSearch]);
// 加载所有股票数据 // 加载所有股票数据
useEffect(() => { useEffect(() => {
@@ -100,8 +116,8 @@ const UnifiedSearchBox = ({
return null; return null;
}, []); }, []);
// ✅ 修复:从 props.filters 初始化所有筛选条件 // ✅ 从 props.filters 初始化筛选条件(不包括搜索值,搜索值由 searchValue useMemo 推导)
React.useEffect(() => { useEffect(() => {
if (!filters) return; if (!filters) return;
// 初始化排序和重要性 // 初始化排序和重要性
@@ -112,41 +128,13 @@ const UnifiedSearchBox = ({
if (filters.date_range) { if (filters.date_range) {
const parts = filters.date_range.split(' 至 '); const parts = filters.date_range.split(' 至 ');
if (parts.length === 2) { if (parts.length === 2) {
try { setDateRange([dayjs(parts[0]), dayjs(parts[1])]);
setDateRange([dayjs(parts[0]), dayjs(parts[1])]); logger.debug('UnifiedSearchBox', '初始化日期范围', {
logger.debug('UnifiedSearchBox', '初始化日期范围', { date_range: filters.date_range
date_range: filters.date_range });
});
} catch (error) {
logger.error('UnifiedSearchBox', '日期范围解析失败', error);
}
} }
} }
// 初始化搜索值(只在 allStocks 加载完成后处理股票搜索)
if (filters.stock_code) {
// 股票搜索模式
if (allStocks && allStocks.length > 0) {
const stock = allStocks.find(s => s.code === filters.stock_code);
if (stock) {
const displayValue = `${stock.code} ${stock.name}`;
setSearchValue(displayValue);
setSelectedStockInfo(stock);
} else {
setSearchValue(filters.stock_code);
}
setIsStockSearch(true);
} else {
// allStocks 还未加载,先设置 stock_code
setSearchValue(filters.stock_code);
setIsStockSearch(true);
}
} else if (filters.q) {
// 话题搜索模式
setSearchValue(filters.q);
setIsStockSearch(false);
}
// ✅ 初始化行业分类(需要 industryData 加载完成) // ✅ 初始化行业分类(需要 industryData 加载完成)
if (filters.industry_code && industryData && industryData.length > 0) { if (filters.industry_code && industryData && industryData.length > 0) {
const path = findIndustryPath(filters.industry_code, industryData); const path = findIndustryPath(filters.industry_code, industryData);
@@ -158,7 +146,7 @@ const UnifiedSearchBox = ({
}); });
} }
} }
}, [filters, allStocks, industryData, findIndustryPath]); // ✅ 添加完整依赖 }, [filters.sort, filters.importance, filters.date_range, filters.industry_code, industryData, findIndustryPath]);
// AutoComplete 搜索股票(模糊匹配 code 或 name // AutoComplete 搜索股票(模糊匹配 code 或 name
const handleSearch = (value) => { const handleSearch = (value) => {
@@ -191,32 +179,24 @@ const UnifiedSearchBox = ({
}); });
}; };
// 选中股票(从下拉选择) // 选中股票(从下拉选择) - 直接构建参数并触发搜索
const handleStockSelect = (value, option) => { const handleStockSelect = (_value, option) => {
const stockInfo = option.stockInfo; const stockInfo = option.stockInfo;
if (stockInfo) { if (stockInfo) {
// 显示格式:股票代码 + 股票名称
const displayValue = `${stockInfo.code} ${stockInfo.name}`;
setSearchValue(displayValue);
setIsStockSearch(true);
setSelectedStockInfo(stockInfo); // 保存完整股票信息
logger.debug('UnifiedSearchBox', '选中股票', { logger.debug('UnifiedSearchBox', '选中股票', {
code: stockInfo.code, code: stockInfo.code,
name: stockInfo.name name: stockInfo.name
}); });
// 自动触发搜索(股票搜索模式) // 直接构建参数并触发搜索(股票搜索模式)
// 注意:此时 isStockSearch 状态还没更新,需要手动构建参数 const params = buildFilterParams({
setTimeout(() => { stock_code: stockInfo.code,
const params = buildFilterParams(); q: '',
// 手动覆盖股票相关参数(因为状态还没更新) industry_code: '',
params.stock_code = stockInfo.code; industry_classification: ''
params.q = ''; });
params.industry_code = ''; logger.debug('UnifiedSearchBox', '自动触发股票搜索', params);
params.industry_classification = ''; triggerSearch(params);
logger.debug('UnifiedSearchBox', '自动触发股票搜索', params);
onSearch(params);
}, 100);
} }
}; };
@@ -267,84 +247,59 @@ const UnifiedSearchBox = ({
} }
}; };
// ✅ 热门概念点击处理(立即搜索,不使用防抖) // ✅ 热门概念点击处理(立即搜索,不使用防抖) - 直接构建参数并触发搜索
const handleKeywordClick = (keyword) => { const handleKeywordClick = (keyword) => {
// 设置搜索框的值为热门概念文本
setSearchValue(keyword);
setIsStockSearch(false); // 作为话题搜索
setSelectedStockInfo(null);
// 立即触发搜索(取消之前的防抖) // 立即触发搜索(取消之前的防抖)
if (debouncedSearchRef.current) { if (debouncedSearchRef.current) {
debouncedSearchRef.current.cancel(); debouncedSearchRef.current.cancel();
} }
const params = buildFilterParams({ q: keyword }); const params = buildFilterParams({
q: keyword,
stock_code: '',
industry_code: '',
industry_classification: ''
});
logger.debug('UnifiedSearchBox', '热门概念点击,立即触发搜索', { logger.debug('UnifiedSearchBox', '热门概念点击,立即触发搜索', {
keyword, keyword,
params params
}); });
onSearch(params); triggerSearch(params);
}; };
// 主搜索(点击搜索按钮或回车) // 主搜索(点击搜索按钮或回车)
const handleMainSearch = () => { const handleMainSearch = () => {
// 直接输入文本(未选择下拉股票)时,作为话题搜索 // 直接输入文本时,构建参数并触发搜索
if (!isStockSearch && searchValue) { logger.debug('UnifiedSearchBox', '主搜索触发', { searchValue });
logger.debug('UnifiedSearchBox', '话题搜索', { topic: searchValue });
}
handleApplyFilters(); handleApplyFilters();
}; };
// 处理输入变化 // 处理输入变化 - AutoComplete 内部处理显示,不需要更新状态
const handleInputChange = (value) => { const handleInputChange = (value) => {
setSearchValue(value); // AutoComplete 组件会自动处理显示值
// 输入变化时重置股票搜索标记(只有从下拉选择才是股票搜索 // 这里可以添加其他逻辑(如实时搜索建议
if (isStockSearch) { logger.debug('UnifiedSearchBox', '输入变化', { value });
setIsStockSearch(false);
setSelectedStockInfo(null); // 清除保存的股票信息
logger.debug('UnifiedSearchBox', '切换为话题搜索模式');
}
}; };
// ✅ 生成完整的筛选参数对象 // ✅ 生成完整的筛选参数对象 - 直接从 filters 和本地筛选器状态构建
const buildFilterParams = (overrides = {}) => { const buildFilterParams = useCallback((overrides = {}) => {
// 构建基础参数overrides 优先级高于本地状态) return {
const baseParams = { // 基础参数overrides 优先级高于本地状态)
sort: overrides.sort ?? sort, sort: overrides.sort ?? sort,
importance: overrides.importance ?? importance, importance: overrides.importance ?? importance,
date_range: dateRange ? `${dateRange[0].format('YYYY-MM-DD')}${dateRange[1].format('YYYY-MM-DD')}` : '', date_range: dateRange ? `${dateRange[0].format('YYYY-MM-DD')}${dateRange[1].format('YYYY-MM-DD')}` : '',
page: 1 page: 1,
};
// 构建搜索相关参数 // 搜索参数(从 filters 继承overrides 可覆盖)
let searchParams; q: overrides.q ?? filters.q ?? '',
if (isStockSearch && searchValue) { stock_code: overrides.stock_code ?? filters.stock_code ?? '',
// 股票搜索模式 industry_code: overrides.industry_code ?? (industryValue?.[industryValue.length - 1] || ''),
const stockCode = searchValue.split(' ')[0]; industry_classification: overrides.industry_classification ?? (industryValue?.[0] || ''),
searchParams = {
stock_code: stockCode,
q: '',
industry_code: '',
industry_classification: ''
};
} else {
// 话题搜索模式
searchParams = {
q: overrides.q ?? searchValue || '',
stock_code: '',
industry_code: industryValue?.[industryValue.length - 1] || '',
industry_classification: industryValue?.[0] || ''
};
}
// 合并所有参数,overrides 具有最高优先级 // 最终 overrides 具有最高优先级
return {
...baseParams,
...searchParams,
...overrides ...overrides
}; };
}; }, [sort, importance, dateRange, filters.q, filters.stock_code, industryValue]);
// ✅ 应用筛选(立即搜索,取消防抖) // ✅ 应用筛选(立即搜索,取消防抖)
const handleApplyFilters = () => { const handleApplyFilters = () => {
@@ -355,17 +310,14 @@ const UnifiedSearchBox = ({
const params = buildFilterParams(); const params = buildFilterParams();
logger.debug('UnifiedSearchBox', '应用筛选,立即触发搜索', params); logger.debug('UnifiedSearchBox', '应用筛选,立即触发搜索', params);
onSearch(params); triggerSearch(params);
}; };
// 重置筛选 // 重置筛选 - 清空所有筛选器并触发搜索
const handleReset = () => { const handleReset = () => {
// 重置所有状态 // 重置所有筛选器状态
setSearchValue('');
setIsStockSearch(false);
setStockOptions([]); setStockOptions([]);
setIndustryValue([]); setIndustryValue([]);
setSelectedStockInfo(null);
setSort('new'); setSort('new');
setImportance('all'); setImportance('all');
setDateRange(null); setDateRange(null);
@@ -386,15 +338,17 @@ const UnifiedSearchBox = ({
onSearch(resetParams); onSearch(resetParams);
}; };
// 生成已选条件标签(包含所有筛选条件) // 生成已选条件标签(包含所有筛选条件) - 从 filters 和本地状态读取
const filterTags = useMemo(() => { const filterTags = useMemo(() => {
const tags = []; const tags = [];
// 搜索关键词/股票标签 // 股票/话题标签 - 从 filters 读取
if (isStockSearch && searchValue) { if (filters.stock_code) {
tags.push({ key: 'stock', label: `股票: ${searchValue}` }); const stock = allStocks.find(s => s.code === filters.stock_code);
} else if (!isStockSearch && searchValue) { const label = stock ? `${stock.code} ${stock.name}` : filters.stock_code;
tags.push({ key: 'topic', label: `话题: ${searchValue}` }); tags.push({ key: 'stock', label: `股票: ${label}` });
} else if (filters.q) {
tags.push({ key: 'topic', label: `话题: ${filters.q}` });
} }
// 行业标签 // 行业标签
@@ -421,37 +375,40 @@ const UnifiedSearchBox = ({
} }
return tags; return tags;
}, [searchValue, isStockSearch, industryValue, dateRange, importance, sort]); }, [filters.stock_code, filters.q, allStocks, industryValue, dateRange, importance, sort]);
// 移除单个标签 // 移除单个标签 - 构建新参数并触发搜索
const handleRemoveTag = (key) => { const handleRemoveTag = (key) => {
logger.debug('UnifiedSearchBox', '移除标签', { key }); logger.debug('UnifiedSearchBox', '移除标签', { key });
if (key === 'topic' || key === 'stock') { if (key === 'topic' || key === 'stock') {
// 清除搜索框内容和股票选择状态 // 清除搜索关键词/股票,立即触发搜索
setSearchValue(''); const params = buildFilterParams({ q: '', stock_code: '' });
setIsStockSearch(false); logger.debug('UnifiedSearchBox', '移除搜索标签后触发搜索', { key, params });
setSelectedStockInfo(null); triggerSearch(params);
} else if (key === 'industry') { } else if (key === 'industry') {
// 清除行业选择 // 清除行业选择
setIndustryValue([]); setIndustryValue([]);
const params = buildFilterParams({ industry_code: '', industry_classification: '' });
triggerSearch(params);
} else if (key === 'date_range') { } else if (key === 'date_range') {
// 清除日期范围 // 清除日期范围
setDateRange(null); setDateRange(null);
setTimeout(() => {
const params = buildFilterParams();
triggerSearch(params);
}, 50);
} else if (key === 'importance') { } else if (key === 'importance') {
// 重置重要性为默认值 // 重置重要性为默认值
setImportance('all'); setImportance('all');
const params = buildFilterParams({ importance: 'all' });
triggerSearch(params);
} else if (key === 'sort') { } else if (key === 'sort') {
// 重置排序为默认值 // 重置排序为默认值
setSort('new'); setSort('new');
const params = buildFilterParams({ sort: 'new' });
triggerSearch(params);
} }
// 延迟触发搜索,确保状态已更新
setTimeout(() => {
const params = buildFilterParams();
logger.debug('UnifiedSearchBox', '移除标签后触发搜索', { key, params });
onSearch(params);
}, 50);
}; };
return ( return (
@@ -475,7 +432,6 @@ const UnifiedSearchBox = ({
options={stockOptions} options={stockOptions}
placeholder="请输入股票代码/股票名称/相关话题" placeholder="请输入股票代码/股票名称/相关话题"
onPressEnter={handleMainSearch} onPressEnter={handleMainSearch}
disabled={loading}
style={{ flex: 1 }} style={{ flex: 1 }}
size="large" size="large"
notFoundContent={searchValue && stockOptions.length === 0 ? "未找到匹配的股票" : null} notFoundContent={searchValue && stockOptions.length === 0 ? "未找到匹配的股票" : null}
@@ -483,7 +439,6 @@ const UnifiedSearchBox = ({
<Button <Button
type="primary" type="primary"
onClick={handleMainSearch} onClick={handleMainSearch}
loading={loading}
size="large" size="large"
style={{ minWidth: 80 }} style={{ minWidth: 80 }}
> >
@@ -521,8 +476,7 @@ const UnifiedSearchBox = ({
allowClear allowClear
expandTrigger="hover" expandTrigger="hover"
displayRender={(labels) => labels.join(' > ')} displayRender={(labels) => labels.join(' > ')}
disabled={industryLoading || loading} disabled={industryLoading}
loading={industryLoading}
style={{ width: 200 }} style={{ width: 200 }}
size="middle" size="middle"
/> />
@@ -533,7 +487,6 @@ const UnifiedSearchBox = ({
onChange={handleDateRangeChange} onChange={handleDateRangeChange}
locale={locale} locale={locale}
placeholder={['开始日期', '结束日期']} placeholder={['开始日期', '结束日期']}
disabled={loading}
style={{ width: 240 }} style={{ width: 240 }}
size="middle" size="middle"
/> />
@@ -544,7 +497,6 @@ const UnifiedSearchBox = ({
<AntSelect <AntSelect
value={importance} value={importance}
onChange={handleImportanceChange} onChange={handleImportanceChange}
disabled={loading}
style={{ width: 100 }} style={{ width: 100 }}
size="middle" size="middle"
> >
@@ -560,7 +512,6 @@ const UnifiedSearchBox = ({
<Button <Button
icon={<CloseCircleOutlined />} icon={<CloseCircleOutlined />}
onClick={handleReset} onClick={handleReset}
disabled={loading}
size="middle" size="middle"
style={{ style={{
borderRadius: 6, borderRadius: 6,
@@ -600,7 +551,6 @@ const UnifiedSearchBox = ({
<AntSelect <AntSelect
value={sort} value={sort}
onChange={handleSortChange} onChange={handleSortChange}
disabled={loading}
style={{ width: 120 }} style={{ width: 120 }}
size="middle" size="middle"
> >