diff --git a/src/views/Community/components/SearchFilters/CompactSearchBox.js b/src/views/Community/components/SearchFilters/CompactSearchBox.js
index e2defe4c..65590524 100644
--- a/src/views/Community/components/SearchFilters/CompactSearchBox.js
+++ b/src/views/Community/components/SearchFilters/CompactSearchBox.js
@@ -1,641 +1,543 @@
// src/views/Community/components/SearchFilters/CompactSearchBox.js
// 紧凑版搜索和筛选组件 - 优化布局
-import React, { useState, useMemo, useEffect, useCallback, useRef } from 'react';
+import React, { useState, useMemo, useEffect, useCallback, useRef } from "react";
import {
- Input, Cascader, Button, Space, Tag, AutoComplete, Select as AntSelect,
- Tooltip, Divider, Flex
-} from 'antd';
+ Input,
+ Cascader,
+ Button,
+ Space,
+ AutoComplete,
+ Select as AntSelect,
+ Tooltip,
+ Divider,
+ Flex,
+} from "antd";
import {
- SearchOutlined, CloseCircleOutlined, StockOutlined, FilterOutlined,
- CalendarOutlined, SortAscendingOutlined, ReloadOutlined, ThunderboltOutlined
-} from '@ant-design/icons';
-import dayjs from 'dayjs';
-import debounce from 'lodash/debounce';
-import { useSelector, useDispatch } from 'react-redux';
-import { fetchIndustryData, selectIndustryData, selectIndustryLoading } from '@store/slices/industrySlice';
-import { loadAllStocks } from '@store/slices/stockSlice';
-import { stockService } from '@services/stockService';
-import { logger } from '@utils/logger';
-import TradingTimeFilter from './TradingTimeFilter';
-import { PROFESSIONAL_COLORS } from '@constants/professionalTheme';
-import './CompactSearchBox.css';
+ SearchOutlined,
+ CloseCircleOutlined,
+ StockOutlined,
+ FilterOutlined,
+ CalendarOutlined,
+ SortAscendingOutlined,
+ ReloadOutlined,
+ ThunderboltOutlined,
+} from "@ant-design/icons";
+import debounce from "lodash/debounce";
+import { useSelector, useDispatch } from "react-redux";
+import {
+ fetchIndustryData,
+ selectIndustryData,
+ selectIndustryLoading,
+} from "@store/slices/industrySlice";
+import { loadAllStocks } from "@store/slices/stockSlice";
+import { stockService } from "@services/stockService";
+import { logger } from "@utils/logger";
+import TradingTimeFilter from "./TradingTimeFilter";
+import { PROFESSIONAL_COLORS } from "@constants/professionalTheme";
+import "./CompactSearchBox.css";
+
+// 模块化导入
+import {
+ SORT_OPTIONS,
+ IMPORTANCE_OPTIONS,
+} from "./CompactSearchBox/constants";
+import {
+ findIndustryPath,
+ inferTimeRangeFromFilters,
+ buildFilterParams,
+} from "./CompactSearchBox/utils";
const { Option } = AntSelect;
-// 排序选项常量
-const SORT_OPTIONS = [
- { value: 'new', label: '最新排序', mobileLabel: '最新' },
- { value: 'hot', label: '最热排序', mobileLabel: '热门' },
- { value: 'importance', label: '重要性排序', mobileLabel: '重要' },
- { value: 'returns_avg', label: '平均收益', mobileLabel: '均收' },
- { value: 'returns_week', label: '周收益', mobileLabel: '周收' },
-];
-
-// 重要性等级常量
-const IMPORTANCE_OPTIONS = [
- { value: 'S', label: 'S级' },
- { value: 'A', label: 'A级' },
- { value: 'B', label: 'B级' },
- { value: 'C', label: 'C级' },
-];
-
const CompactSearchBox = ({
- onSearch,
- onSearchFocus,
- filters = {},
- mode,
- pageSize,
- trackingFunctions = {},
- isMobile = false
+ onSearch,
+ onSearchFocus,
+ filters = {},
+ mode,
+ pageSize,
+ trackingFunctions = {},
+ isMobile = false,
}) => {
- // 状态
- const [stockOptions, setStockOptions] = useState([]);
- const [allStocks, setAllStocks] = useState([]);
- const [industryValue, setIndustryValue] = useState([]);
- const [sort, setSort] = useState('new');
- const [importance, setImportance] = useState([]);
- const [tradingTimeRange, setTradingTimeRange] = useState(null);
- const [inputValue, setInputValue] = useState('');
+ // 状态
+ const [stockOptions, setStockOptions] = useState([]);
+ const [allStocks, setAllStocks] = useState([]);
+ const [industryValue, setIndustryValue] = useState([]);
+ const [sort, setSort] = useState("new");
+ const [importance, setImportance] = useState([]);
+ const [tradingTimeRange, setTradingTimeRange] = useState(null);
+ const [inputValue, setInputValue] = useState("");
- // Redux
- const dispatch = useDispatch();
- const industryData = useSelector(selectIndustryData);
- const industryLoading = useSelector(selectIndustryLoading);
- const reduxAllStocks = useSelector((state) => state.stock.allStocks);
+ // Redux
+ const dispatch = useDispatch();
+ const industryData = useSelector(selectIndustryData);
+ const industryLoading = useSelector(selectIndustryLoading);
+ const reduxAllStocks = useSelector((state) => state.stock.allStocks);
- // 防抖搜索
- const debouncedSearchRef = useRef(null);
- // 存储股票选择时的显示值(代码+名称),用于 useEffect 同步时显示完整信息
- const stockDisplayValueRef = useRef(null);
+ // Refs
+ const debouncedSearchRef = useRef(null);
+ const stockDisplayValueRef = useRef(null);
- const triggerSearch = useCallback((params) => {
- logger.debug('CompactSearchBox', '触发搜索', { params });
- onSearch(params);
- }, [onSearch]);
+ const triggerSearch = useCallback(
+ (params) => {
+ logger.debug("CompactSearchBox", "触发搜索", { params });
+ onSearch(params);
+ },
+ [onSearch]
+ );
- useEffect(() => {
- debouncedSearchRef.current = debounce((params) => {
- triggerSearch(params);
- }, 300);
+ // 创建构建参数的封装函数
+ const createFilterParams = useCallback(
+ (overrides = {}) =>
+ buildFilterParams({
+ overrides,
+ sort,
+ importance,
+ filtersQ: filters.q,
+ industryValue,
+ tradingTimeRange,
+ mode,
+ pageSize,
+ }),
+ [sort, importance, filters.q, industryValue, tradingTimeRange, mode, pageSize]
+ );
- return () => {
- if (debouncedSearchRef.current) {
- debouncedSearchRef.current.cancel();
- }
- };
- }, [triggerSearch]);
+ // 防抖搜索初始化
+ useEffect(() => {
+ debouncedSearchRef.current = debounce((params) => {
+ triggerSearch(params);
+ }, 300);
- // 加载股票数据(从 Redux 获取)
- useEffect(() => {
- if (!reduxAllStocks || reduxAllStocks.length === 0) {
- dispatch(loadAllStocks());
- }
- }, [dispatch, reduxAllStocks]);
-
- // 同步 Redux 数据到本地状态
- useEffect(() => {
- if (reduxAllStocks && reduxAllStocks.length > 0) {
- setAllStocks(reduxAllStocks);
- }
- }, [reduxAllStocks]);
-
- // 预加载行业数据(解决第一次点击无数据问题)
- useEffect(() => {
- if (!industryData || industryData.length === 0) {
- dispatch(fetchIndustryData());
- }
- }, [dispatch, industryData]);
-
- // 初始化筛选条件
- const findIndustryPath = useCallback((targetCode, data, currentPath = []) => {
- if (!data || data.length === 0) return null;
-
- for (const item of data) {
- const newPath = [...currentPath, item.value];
-
- if (item.value === targetCode) {
- return newPath;
- }
-
- if (item.children && item.children.length > 0) {
- const found = findIndustryPath(targetCode, item.children, newPath);
- if (found) return found;
- }
- }
- return null;
- }, []);
-
- useEffect(() => {
- if (!filters) return;
-
- // 优先使用 _sortDisplay(原始排序值),否则回退到 sort
- // 这样可以正确显示 returns_avg, returns_week 等复合排序选项
- if (filters._sortDisplay || filters.sort) {
- setSort(filters._sortDisplay || filters.sort);
- }
-
- if (filters.importance) {
- const importanceArray = filters.importance === 'all'
- ? []
- : filters.importance.split(',').map(v => v.trim()).filter(Boolean);
- setImportance(importanceArray);
- } else {
- setImportance([]);
- }
-
- if (filters.industry_code && industryData && industryData.length > 0 && (!industryValue || industryValue.length === 0)) {
- const path = findIndustryPath(filters.industry_code, industryData);
- if (path) {
- setIndustryValue(path);
- }
- } else if (!filters.industry_code && industryValue && industryValue.length > 0) {
- setIndustryValue([]);
- }
-
- if (filters.q) {
- // 如果是股票选择触发的搜索,使用存储的显示值(代码+名称)
- if (stockDisplayValueRef.current && stockDisplayValueRef.current.code === filters.q) {
- setInputValue(stockDisplayValueRef.current.displayValue);
- } else {
- setInputValue(filters.q);
- // 清除已失效的显示值缓存
- stockDisplayValueRef.current = null;
- }
- } else if (!filters.q) {
- setInputValue('');
- stockDisplayValueRef.current = null;
- }
-
- const hasTimeInFilters = filters.start_date || filters.end_date || filters.recent_days || filters.time_filter_key;
-
- if (hasTimeInFilters && (!tradingTimeRange || !tradingTimeRange.key)) {
- // 优先使用 time_filter_key(来自 useEventFilters 的默认值)
- let inferredKey = filters.time_filter_key || 'custom';
- let inferredLabel = '';
-
- if (filters.time_filter_key === 'current-trading-day') {
- inferredKey = 'current-trading-day';
- inferredLabel = '当前交易日';
- } else if (filters.time_filter_key === 'all') {
- inferredKey = 'all';
- inferredLabel = '全部';
- } else if (filters.recent_days) {
- if (filters.recent_days === '7') {
- inferredKey = 'week';
- inferredLabel = '近一周';
- } else if (filters.recent_days === '30') {
- inferredKey = 'month';
- inferredLabel = '近一月';
- } else {
- inferredLabel = `近${filters.recent_days}天`;
- }
- } else if (filters.start_date && filters.end_date) {
- inferredLabel = `${dayjs(filters.start_date).format('MM-DD HH:mm')} - ${dayjs(filters.end_date).format('MM-DD HH:mm')}`;
- }
-
- const timeRange = {
- start_date: filters.start_date || '',
- end_date: filters.end_date || '',
- recent_days: filters.recent_days || '',
- label: inferredLabel,
- key: inferredKey
- };
- setTradingTimeRange(timeRange);
- } else if (!hasTimeInFilters && tradingTimeRange) {
- setTradingTimeRange(null);
- }
- }, [filters, industryData, findIndustryPath, industryValue, tradingTimeRange]);
-
- // 搜索股票
- const handleSearch = (value) => {
- if (!value || !allStocks || allStocks.length === 0) {
- setStockOptions([]);
- return;
- }
-
- const results = stockService.fuzzySearch(value, allStocks, 10);
- const options = results.map(stock => ({
- value: stock.code,
- label: (
-
-
- {stock.code}
- {stock.name}
-
- ),
- stockInfo: stock
- }));
-
- setStockOptions(options);
+ return () => {
+ if (debouncedSearchRef.current) {
+ debouncedSearchRef.current.cancel();
+ }
};
+ }, [triggerSearch]);
- const buildFilterParams = useCallback((overrides = {}) => {
- const sortValue = overrides.sort ?? sort;
- let actualSort = sortValue;
- let returnType;
+ // 加载股票数据
+ useEffect(() => {
+ if (!reduxAllStocks || reduxAllStocks.length === 0) {
+ dispatch(loadAllStocks());
+ }
+ }, [dispatch, reduxAllStocks]);
- if (sortValue === 'returns_avg') {
- actualSort = 'returns';
- returnType = 'avg';
- } else if (sortValue === 'returns_week') {
- actualSort = 'returns';
- returnType = 'week';
- }
+ useEffect(() => {
+ if (reduxAllStocks && reduxAllStocks.length > 0) {
+ setAllStocks(reduxAllStocks);
+ }
+ }, [reduxAllStocks]);
- let importanceValue = overrides.importance ?? importance;
- if (Array.isArray(importanceValue)) {
- importanceValue = importanceValue.length === 0
- ? 'all'
- : importanceValue.join(',');
- }
+ // 预加载行业数据
+ useEffect(() => {
+ if (!industryData || industryData.length === 0) {
+ dispatch(fetchIndustryData());
+ }
+ }, [dispatch, industryData]);
- // 先展开 overrides,再用处理后的值覆盖,避免 overrides.sort 覆盖 actualSort
- const result = {
- ...overrides,
- sort: actualSort,
- // 保留原始排序值用于 UI 显示(如 returns_avg, returns_week)
- _sortDisplay: sortValue,
- importance: importanceValue,
- q: (overrides.q ?? filters.q) ?? '',
- industry_code: overrides.industry_code ?? (industryValue?.join(',') || ''),
- start_date: overrides.start_date ?? (tradingTimeRange?.start_date || ''),
- end_date: overrides.end_date ?? (tradingTimeRange?.end_date || ''),
- recent_days: overrides.recent_days ?? (tradingTimeRange?.recent_days || ''),
- page: 1,
- };
+ // 同步外部 filters 到本地状态
+ useEffect(() => {
+ if (!filters) return;
- // 移除不需要的字段
- delete result.per_page;
+ // 排序
+ if (filters._sortDisplay || filters.sort) {
+ setSort(filters._sortDisplay || filters.sort);
+ }
- // 添加 return_type 参数(用于收益排序)
- if (returnType) {
- result.return_type = returnType;
- } else {
- // 确保非收益排序时不带 return_type
- delete result.return_type;
- }
+ // 重要性
+ if (filters.importance) {
+ const importanceArray =
+ filters.importance === "all"
+ ? []
+ : filters.importance
+ .split(",")
+ .map((v) => v.trim())
+ .filter(Boolean);
+ setImportance(importanceArray);
+ } else {
+ setImportance([]);
+ }
- if (mode !== undefined && mode !== null) {
- result.mode = mode;
- }
- if (pageSize !== undefined && pageSize !== null) {
- result.per_page = pageSize;
- }
+ // 行业
+ if (
+ filters.industry_code &&
+ industryData &&
+ industryData.length > 0 &&
+ (!industryValue || industryValue.length === 0)
+ ) {
+ const path = findIndustryPath(filters.industry_code, industryData);
+ if (path) {
+ setIndustryValue(path);
+ }
+ } else if (
+ !filters.industry_code &&
+ industryValue &&
+ industryValue.length > 0
+ ) {
+ setIndustryValue([]);
+ }
- return result;
- }, [sort, importance, filters.q, industryValue, tradingTimeRange, mode, pageSize]);
+ // 搜索关键词
+ if (filters.q) {
+ if (
+ stockDisplayValueRef.current &&
+ stockDisplayValueRef.current.code === filters.q
+ ) {
+ setInputValue(stockDisplayValueRef.current.displayValue);
+ } else {
+ setInputValue(filters.q);
+ stockDisplayValueRef.current = null;
+ }
+ } else if (!filters.q) {
+ setInputValue("");
+ stockDisplayValueRef.current = null;
+ }
- const handleStockSelect = (_value, option) => {
- const stockInfo = option.stockInfo;
- if (stockInfo) {
- if (trackingFunctions.trackRelatedStockClicked) {
- trackingFunctions.trackRelatedStockClicked({
- stockCode: stockInfo.code,
- stockName: stockInfo.name,
- source: 'search_box_autocomplete',
- timestamp: new Date().toISOString(),
- });
- }
+ // 时间范围
+ const hasTimeInFilters =
+ filters.start_date ||
+ filters.end_date ||
+ filters.recent_days ||
+ filters.time_filter_key;
- const displayValue = `${stockInfo.code} ${stockInfo.name}`;
- setInputValue(displayValue);
- // 存储显示值,供 useEffect 同步时使用
- stockDisplayValueRef.current = { code: stockInfo.code, displayValue };
+ if (hasTimeInFilters && (!tradingTimeRange || !tradingTimeRange.key)) {
+ const timeRange = inferTimeRangeFromFilters(filters);
+ if (timeRange) {
+ setTradingTimeRange(timeRange);
+ }
+ } else if (!hasTimeInFilters && tradingTimeRange) {
+ setTradingTimeRange(null);
+ }
+ }, [filters, industryData, industryValue, tradingTimeRange]);
- const params = buildFilterParams({
- q: stockInfo.code, // 接口只传代码
- industry_code: ''
- });
- triggerSearch(params);
- }
- };
+ // 股票搜索
+ const handleSearch = (value) => {
+ if (!value || !allStocks || allStocks.length === 0) {
+ setStockOptions([]);
+ return;
+ }
- const handleImportanceChange = (value) => {
- setImportance(value);
-
- if (debouncedSearchRef.current) {
- debouncedSearchRef.current.cancel();
- }
-
- const importanceStr = value.length === 0 ? 'all' : value.join(',');
-
- if (trackingFunctions.trackNewsFilterApplied) {
- trackingFunctions.trackNewsFilterApplied({
- filterType: 'importance',
- filterValue: importanceStr,
- timestamp: new Date().toISOString(),
- });
- }
-
- const params = buildFilterParams({ importance: importanceStr });
- triggerSearch(params);
- };
-
- const handleSortChange = (value) => {
- setSort(value);
-
- if (debouncedSearchRef.current) {
- debouncedSearchRef.current.cancel();
- }
-
- if (trackingFunctions.trackNewsSorted) {
- trackingFunctions.trackNewsSorted({
- sortBy: value,
- previousSortBy: sort,
- timestamp: new Date().toISOString(),
- });
- }
-
- const params = buildFilterParams({ sort: value });
- triggerSearch(params);
- };
-
- const handleIndustryChange = (value) => {
- setIndustryValue(value);
-
- if (debouncedSearchRef.current) {
- debouncedSearchRef.current.cancel();
- }
-
- if (trackingFunctions.trackNewsFilterApplied) {
- trackingFunctions.trackNewsFilterApplied({
- filterType: 'industry',
- filterValue: value?.[value.length - 1] || '',
- timestamp: new Date().toISOString(),
- });
- }
-
- const params = buildFilterParams({
- industry_code: value?.[value.length - 1] || ''
- });
- triggerSearch(params);
- };
-
- const handleTradingTimeChange = (timeConfig) => {
- if (!timeConfig) {
- setTradingTimeRange(null);
-
- if (trackingFunctions.trackNewsFilterApplied) {
- trackingFunctions.trackNewsFilterApplied({
- filterType: 'time_range',
- filterValue: 'cleared',
- timestamp: new Date().toISOString(),
- });
- }
-
- const params = buildFilterParams({
- start_date: '',
- end_date: '',
- recent_days: ''
- });
- triggerSearch(params);
- return;
- }
-
- const { range, type, label, key } = timeConfig;
- let params = {};
-
- if (type === 'all') {
- // "全部"按钮:清除所有时间限制
- params.start_date = '';
- params.end_date = '';
- params.recent_days = '';
- } else if (type === 'recent_days') {
- params.recent_days = range;
- params.start_date = '';
- params.end_date = '';
- } else {
- params.start_date = range[0].format('YYYY-MM-DD HH:mm:ss');
- params.end_date = range[1].format('YYYY-MM-DD HH:mm:ss');
- params.recent_days = '';
- }
-
- setTradingTimeRange({ ...params, label, key });
-
- if (trackingFunctions.trackNewsFilterApplied) {
- trackingFunctions.trackNewsFilterApplied({
- filterType: 'time_range',
- filterValue: label,
- timeRangeType: type,
- timestamp: new Date().toISOString(),
- });
- }
-
- const searchParams = buildFilterParams({ ...params, mode });
- triggerSearch(searchParams);
- };
-
- const handleMainSearch = () => {
- if (debouncedSearchRef.current) {
- debouncedSearchRef.current.cancel();
- }
-
- const params = buildFilterParams({
- q: inputValue,
- industry_code: ''
- });
-
- if (trackingFunctions.trackNewsSearched && inputValue) {
- trackingFunctions.trackNewsSearched({
- searchQuery: inputValue,
- searchType: 'main_search',
- filters: params,
- timestamp: new Date().toISOString(),
- });
- }
-
- triggerSearch(params);
- };
-
- const handleInputChange = (value) => {
- setInputValue(value);
- };
-
- const handleReset = () => {
- setInputValue('');
- setStockOptions([]);
- setIndustryValue([]);
- setSort('new');
- setImportance([]);
- setTradingTimeRange(null);
-
- if (trackingFunctions.trackNewsFilterApplied) {
- trackingFunctions.trackNewsFilterApplied({
- filterType: 'reset',
- filterValue: 'all_filters_cleared',
- timestamp: new Date().toISOString(),
- });
- }
-
- const resetParams = {
- q: '',
- industry_code: '',
- sort: 'new',
- importance: 'all',
- start_date: '',
- end_date: '',
- recent_days: '',
- page: 1,
- _forceRefresh: Date.now()
- };
-
- onSearch(resetParams);
- };
-
- const handleCascaderFocus = async () => {
- if (!industryData || industryData.length === 0) {
- dispatch(fetchIndustryData());
- }
- };
- return (
-
- {/* 第一行:搜索框 + 日期筛选 */}
-
- {/* 搜索框 - flex: 1 占满剩余空间 */}
- {
- if (e.key === 'Enter') {
- handleMainSearch();
- }
- }}
- style={{ flex: 1, minWidth: isMobile ? 100 : 200 }}
- className="gold-placeholder"
- allowClear={{
- clearIcon:
- }}
- >
- }
- placeholder="搜索股票/话题..."
- style={{
- border: 'none',
- background: 'transparent',
- color: PROFESSIONAL_COLORS.text.primary,
- boxShadow: 'none'
- }}
- />
-
-
- {/* 分隔线 - H5 时隐藏 */}
- {!isMobile && }
-
- {/* 日期筛选按钮组 */}
-
-
-
-
-
-
- {/* 第二行:筛选条件 - 主线模式下隐藏(主线模式有自己的筛选器) */}
- {mode !== 'mainline' && (
-
- {/* 左侧筛选 */}
-
- {/* 行业筛选 */}
-
-
- {isMobile ? '行业' : '行业筛选'}
-
- }
- changeOnSelect
- showSearch={{
- filter: (inputValue, path) =>
- path.some(option =>
- option.label.toLowerCase().includes(inputValue.toLowerCase())
- )
- }}
- allowClear
- expandTrigger="hover"
- displayRender={(labels) => labels[labels.length - 1] || (isMobile ? '行业' : '行业筛选')}
- disabled={industryLoading}
- style={{ minWidth: isMobile ? 70 : 80 }}
- suffixIcon={null}
- className="transparent-cascader"
- />
-
- {/* 事件等级 */}
-
-
- {isMobile ? '等级' : '事件等级'}
-
- }
- maxTagCount={0}
- maxTagPlaceholder={(omittedValues) => isMobile ? `${omittedValues.length}项` : `已选 ${omittedValues.length} 项`}
- className="bracket-select"
- >
- {IMPORTANCE_OPTIONS.map(opt => (
-
- ))}
-
-
-
- {/* 右侧排序和重置 */}
-
- {/* 排序 */}
-
- {SORT_OPTIONS.map(opt => (
-
- ))}
-
-
- {/* 重置按钮 */}
- }
- onClick={handleReset}
- type="text"
- style={{ color: PROFESSIONAL_COLORS.text.secondary }}
- >
- {!isMobile && '重置筛选'}
-
-
-
- )}
+ const results = stockService.fuzzySearch(value, allStocks, 10);
+ const options = results.map((stock) => ({
+ value: stock.code,
+ label: (
+
+
+ {stock.code}
+ {stock.name}
- );
+ ),
+ stockInfo: stock,
+ }));
+
+ setStockOptions(options);
+ };
+
+ // 股票选择
+ const handleStockSelect = (_value, option) => {
+ const stockInfo = option.stockInfo;
+ if (stockInfo) {
+ trackingFunctions.trackRelatedStockClicked?.({
+ stockCode: stockInfo.code,
+ stockName: stockInfo.name,
+ source: "search_box_autocomplete",
+ timestamp: new Date().toISOString(),
+ });
+
+ const displayValue = `${stockInfo.code} ${stockInfo.name}`;
+ setInputValue(displayValue);
+ stockDisplayValueRef.current = { code: stockInfo.code, displayValue };
+
+ const params = createFilterParams({ q: stockInfo.code, industry_code: "" });
+ triggerSearch(params);
+ }
+ };
+
+ // 重要性变更
+ const handleImportanceChange = (value) => {
+ setImportance(value);
+ debouncedSearchRef.current?.cancel();
+
+ const importanceStr = value.length === 0 ? "all" : value.join(",");
+ trackingFunctions.trackNewsFilterApplied?.({
+ filterType: "importance",
+ filterValue: importanceStr,
+ timestamp: new Date().toISOString(),
+ });
+
+ triggerSearch(createFilterParams({ importance: importanceStr }));
+ };
+
+ // 排序变更
+ const handleSortChange = (value) => {
+ setSort(value);
+ debouncedSearchRef.current?.cancel();
+
+ trackingFunctions.trackNewsSorted?.({
+ sortBy: value,
+ previousSortBy: sort,
+ timestamp: new Date().toISOString(),
+ });
+
+ triggerSearch(createFilterParams({ sort: value }));
+ };
+
+ // 行业变更
+ const handleIndustryChange = (value) => {
+ setIndustryValue(value);
+ debouncedSearchRef.current?.cancel();
+
+ trackingFunctions.trackNewsFilterApplied?.({
+ filterType: "industry",
+ filterValue: value?.[value.length - 1] || "",
+ timestamp: new Date().toISOString(),
+ });
+
+ triggerSearch(createFilterParams({ industry_code: value?.[value.length - 1] || "" }));
+ };
+
+ // 时间筛选变更
+ const handleTradingTimeChange = (timeConfig) => {
+ if (!timeConfig) {
+ setTradingTimeRange(null);
+ trackingFunctions.trackNewsFilterApplied?.({
+ filterType: "time_range",
+ filterValue: "cleared",
+ timestamp: new Date().toISOString(),
+ });
+ triggerSearch(createFilterParams({ start_date: "", end_date: "", recent_days: "" }));
+ return;
+ }
+
+ const { range, type, label, key } = timeConfig;
+ let params = {};
+
+ if (type === "all") {
+ params = { start_date: "", end_date: "", recent_days: "" };
+ } else if (type === "recent_days") {
+ params = { recent_days: range, start_date: "", end_date: "" };
+ } else {
+ params = {
+ start_date: range[0].format("YYYY-MM-DD HH:mm:ss"),
+ end_date: range[1].format("YYYY-MM-DD HH:mm:ss"),
+ recent_days: "",
+ };
+ }
+
+ setTradingTimeRange({ ...params, label, key });
+ trackingFunctions.trackNewsFilterApplied?.({
+ filterType: "time_range",
+ filterValue: label,
+ timeRangeType: type,
+ timestamp: new Date().toISOString(),
+ });
+
+ triggerSearch(createFilterParams({ ...params, mode }));
+ };
+
+ // 主搜索
+ const handleMainSearch = () => {
+ debouncedSearchRef.current?.cancel();
+ const params = createFilterParams({ q: inputValue, industry_code: "" });
+
+ if (inputValue) {
+ trackingFunctions.trackNewsSearched?.({
+ searchQuery: inputValue,
+ searchType: "main_search",
+ filters: params,
+ timestamp: new Date().toISOString(),
+ });
+ }
+
+ triggerSearch(params);
+ };
+
+ // 重置
+ const handleReset = () => {
+ setInputValue("");
+ setStockOptions([]);
+ setIndustryValue([]);
+ setSort("new");
+ setImportance([]);
+ setTradingTimeRange(null);
+
+ trackingFunctions.trackNewsFilterApplied?.({
+ filterType: "reset",
+ filterValue: "all_filters_cleared",
+ timestamp: new Date().toISOString(),
+ });
+
+ onSearch({
+ q: "",
+ industry_code: "",
+ sort: "new",
+ importance: "all",
+ start_date: "",
+ end_date: "",
+ recent_days: "",
+ page: 1,
+ _forceRefresh: Date.now(),
+ });
+ };
+
+ const handleCascaderFocus = async () => {
+ if (!industryData || industryData.length === 0) {
+ dispatch(fetchIndustryData());
+ }
+ };
+
+ return (
+
+ {/* 第一行:搜索框 + 日期筛选 */}
+
+ e.key === "Enter" && handleMainSearch()}
+ style={{ flex: 1, minWidth: isMobile ? 100 : 200 }}
+ className="gold-placeholder"
+ allowClear={{
+ clearIcon: (
+
+ ),
+ }}
+ >
+ }
+ placeholder="搜索股票/话题..."
+ style={{
+ border: "none",
+ background: "transparent",
+ color: PROFESSIONAL_COLORS.text.primary,
+ boxShadow: "none",
+ }}
+ />
+
+
+ {!isMobile && (
+
+ )}
+
+
+
+
+
+
+
+ {/* 第二行:筛选条件 */}
+ {mode !== "mainline" && (
+
+
+
+
+ {isMobile ? "行业" : "行业筛选"}
+
+ }
+ changeOnSelect
+ showSearch={{
+ filter: (inputValue, path) =>
+ path.some((option) =>
+ option.label.toLowerCase().includes(inputValue.toLowerCase())
+ ),
+ }}
+ allowClear
+ expandTrigger="hover"
+ displayRender={(labels) =>
+ labels[labels.length - 1] || (isMobile ? "行业" : "行业筛选")
+ }
+ disabled={industryLoading}
+ style={{ minWidth: isMobile ? 70 : 80 }}
+ suffixIcon={null}
+ className="transparent-cascader"
+ />
+
+
+
+ {isMobile ? "等级" : "事件等级"}
+
+ }
+ maxTagCount={0}
+ maxTagPlaceholder={(omittedValues) =>
+ isMobile ? `${omittedValues.length}项` : `已选 ${omittedValues.length} 项`
+ }
+ className="bracket-select"
+ >
+ {IMPORTANCE_OPTIONS.map((opt) => (
+
+ ))}
+
+
+
+
+
+ {SORT_OPTIONS.map((opt) => (
+
+ ))}
+
+
+ }
+ onClick={handleReset}
+ type="text"
+ style={{ color: PROFESSIONAL_COLORS.text.secondary }}
+ >
+ {!isMobile && "重置筛选"}
+
+
+
+ )}
+
+ );
};
export default CompactSearchBox;
diff --git a/src/views/Community/components/SearchFilters/CompactSearchBox/constants.js b/src/views/Community/components/SearchFilters/CompactSearchBox/constants.js
new file mode 100644
index 00000000..80247ddb
--- /dev/null
+++ b/src/views/Community/components/SearchFilters/CompactSearchBox/constants.js
@@ -0,0 +1,18 @@
+// CompactSearchBox 常量定义
+
+// 排序选项常量
+export const SORT_OPTIONS = [
+ { value: "new", label: "最新排序", mobileLabel: "最新" },
+ { value: "hot", label: "最热排序", mobileLabel: "热门" },
+ { value: "importance", label: "重要性排序", mobileLabel: "重要" },
+ { value: "returns_avg", label: "平均收益", mobileLabel: "均收" },
+ { value: "returns_week", label: "周收益", mobileLabel: "周收" },
+];
+
+// 重要性等级常量
+export const IMPORTANCE_OPTIONS = [
+ { value: "S", label: "S级" },
+ { value: "A", label: "A级" },
+ { value: "B", label: "B级" },
+ { value: "C", label: "C级" },
+];
diff --git a/src/views/Community/components/SearchFilters/CompactSearchBox/index.js b/src/views/Community/components/SearchFilters/CompactSearchBox/index.js
new file mode 100644
index 00000000..4eed6d6b
--- /dev/null
+++ b/src/views/Community/components/SearchFilters/CompactSearchBox/index.js
@@ -0,0 +1,4 @@
+// CompactSearchBox 模块导出
+
+export { SORT_OPTIONS, IMPORTANCE_OPTIONS } from "./constants";
+export { findIndustryPath, inferTimeRangeFromFilters, buildFilterParams } from "./utils";
diff --git a/src/views/Community/components/SearchFilters/CompactSearchBox/utils.js b/src/views/Community/components/SearchFilters/CompactSearchBox/utils.js
new file mode 100644
index 00000000..29c30dd4
--- /dev/null
+++ b/src/views/Community/components/SearchFilters/CompactSearchBox/utils.js
@@ -0,0 +1,140 @@
+// CompactSearchBox 工具函数
+
+import dayjs from "dayjs";
+
+/**
+ * 在行业树中查找指定代码的完整路径
+ * @param {string} targetCode - 目标行业代码
+ * @param {Array} data - 行业数据树
+ * @param {Array} currentPath - 当前路径(递归用)
+ * @returns {Array|null} - 找到的完整路径,未找到返回 null
+ */
+export const findIndustryPath = (targetCode, data, currentPath = []) => {
+ if (!data || data.length === 0) return null;
+
+ for (const item of data) {
+ const newPath = [...currentPath, item.value];
+
+ if (item.value === targetCode) {
+ return newPath;
+ }
+
+ if (item.children && item.children.length > 0) {
+ const found = findIndustryPath(targetCode, item.children, newPath);
+ if (found) return found;
+ }
+ }
+ return null;
+};
+
+/**
+ * 从 filters 中推断时间范围配置
+ * @param {Object} filters - 筛选条件
+ * @returns {Object|null} - 时间范围配置
+ */
+export const inferTimeRangeFromFilters = (filters) => {
+ if (!filters) return null;
+
+ const hasTimeInFilters =
+ filters.start_date ||
+ filters.end_date ||
+ filters.recent_days ||
+ filters.time_filter_key;
+
+ if (!hasTimeInFilters) return null;
+
+ let inferredKey = filters.time_filter_key || "custom";
+ let inferredLabel = "";
+
+ if (filters.time_filter_key === "current-trading-day") {
+ inferredKey = "current-trading-day";
+ inferredLabel = "当前交易日";
+ } else if (filters.time_filter_key === "all") {
+ inferredKey = "all";
+ inferredLabel = "全部";
+ } else if (filters.recent_days) {
+ if (filters.recent_days === "7") {
+ inferredKey = "week";
+ inferredLabel = "近一周";
+ } else if (filters.recent_days === "30") {
+ inferredKey = "month";
+ inferredLabel = "近一月";
+ } else {
+ inferredLabel = `近${filters.recent_days}天`;
+ }
+ } else if (filters.start_date && filters.end_date) {
+ inferredLabel = `${dayjs(filters.start_date).format("MM-DD HH:mm")} - ${dayjs(filters.end_date).format("MM-DD HH:mm")}`;
+ }
+
+ return {
+ start_date: filters.start_date || "",
+ end_date: filters.end_date || "",
+ recent_days: filters.recent_days || "",
+ label: inferredLabel,
+ key: inferredKey,
+ };
+};
+
+/**
+ * 构建筛选参数
+ * @param {Object} options - 配置选项
+ * @returns {Object} - 构建的参数对象
+ */
+export const buildFilterParams = ({
+ overrides = {},
+ sort,
+ importance,
+ filtersQ,
+ industryValue,
+ tradingTimeRange,
+ mode,
+ pageSize,
+}) => {
+ const sortValue = overrides.sort ?? sort;
+ let actualSort = sortValue;
+ let returnType;
+
+ if (sortValue === "returns_avg") {
+ actualSort = "returns";
+ returnType = "avg";
+ } else if (sortValue === "returns_week") {
+ actualSort = "returns";
+ returnType = "week";
+ }
+
+ let importanceValue = overrides.importance ?? importance;
+ if (Array.isArray(importanceValue)) {
+ importanceValue =
+ importanceValue.length === 0 ? "all" : importanceValue.join(",");
+ }
+
+ const result = {
+ ...overrides,
+ sort: actualSort,
+ _sortDisplay: sortValue,
+ importance: importanceValue,
+ q: overrides.q ?? filtersQ ?? "",
+ industry_code: overrides.industry_code ?? (industryValue?.join(",") || ""),
+ start_date: overrides.start_date ?? (tradingTimeRange?.start_date || ""),
+ end_date: overrides.end_date ?? (tradingTimeRange?.end_date || ""),
+ recent_days: overrides.recent_days ?? (tradingTimeRange?.recent_days || ""),
+ page: 1,
+ };
+
+ delete result.per_page;
+
+ if (returnType) {
+ result.return_type = returnType;
+ } else {
+ delete result.return_type;
+ }
+
+ if (mode !== undefined && mode !== null) {
+ result.mode = mode;
+ }
+ if (pageSize !== undefined && pageSize !== null) {
+ result.per_page = pageSize;
+ }
+
+ return result;
+};