@@ -1,45 +1,55 @@
// 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 ,
@@ -47,16 +57,16 @@ const CompactSearchBox = ({
mode ,
pageSize ,
trackingFunctions = { } ,
isMobile = false
isMobile = false ,
} ) => {
// 状态
const [ stockOptions , setStockOptions ] = useState ( [ ] ) ;
const [ allStocks , setAllStocks ] = useState ( [ ] ) ;
const [ industryValue , setIndustryValue ] = useState ( [ ] ) ;
const [ sort , setSort ] = useState ( ' new' ) ;
const [ sort , setSort ] = useState ( " new" ) ;
const [ importance , setImportance ] = useState ( [ ] ) ;
const [ tradingTimeRange , setTradingTimeRange ] = useState ( null ) ;
const [ inputValue , setInputValue ] = useState ( '' ) ;
const [ inputValue , setInputValue ] = useState ( "" ) ;
// Redux
const dispatch = useDispatch ( ) ;
@@ -64,16 +74,35 @@ const CompactSearchBox = ({
const industryLoading = useSelector ( selectIndustryLoading ) ;
const reduxAllStocks = useSelector ( ( state ) => state . stock . allStocks ) ;
// 防抖搜索
// Refs
const debouncedSearchRef = useRef ( null ) ;
// 存储股票选择时的显示值(代码+名称),用于 useEffect 同步时显示完整信息
const stockDisplayValueRef = useRef ( null ) ;
const triggerSearch = useCallback ( ( params ) => {
logger . debug ( 'CompactSearchBox' , '触发搜索' , { params } ) ;
const triggerSearch = useCallback (
( params ) => {
logger . debug ( "CompactSearchBox" , "触发搜索" , { params } ) ;
onSearch ( params ) ;
} , [ onSearch ] ) ;
} ,
[ onSearch ]
) ;
// 创建构建参数的封装函数
const createFilterParams = useCallback (
( overrides = { } ) =>
buildFilterParams ( {
overrides ,
sort ,
importance ,
filtersQ : filters . q ,
industryValue ,
tradingTimeRange ,
mode ,
pageSize ,
} ) ,
[ sort , importance , filters . q , industryValue , tradingTimeRange , mode , pageSize ]
) ;
// 防抖搜索初始化
useEffect ( ( ) => {
debouncedSearchRef . current = debounce ( ( params ) => {
triggerSearch ( params ) ;
@@ -86,128 +115,102 @@ const CompactSearchBox = ({
} ;
} , [ triggerSearch ] ) ;
// 加载股票数据(从 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 ;
} , [ ] ) ;
// 同步外部 filters 到本地状态
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'
const importanceArray =
filters . importance === "all"
? [ ]
: filters . importance . split ( ',' ) . map ( v => v . trim ( ) ) . filter ( Boolean ) ;
: 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 ) ) {
// 行业
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 ) {
} else if (
! filters . industry _code &&
industryValue &&
industryValue . length > 0
) {
setIndustryValue ( [ ] ) ;
}
// 搜索关键词
if ( filters . q ) {
// 如果是股票选择触发的搜索,使用存储的显示值(代码+名称)
if ( stockDisplayValueRef . current && stockDisplayValueRef . current . code === 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 ( '' ) ;
setInputValue ( "" ) ;
stockDisplayValueRef . current = null ;
}
const hasTimeInFilters = filters . start _date || filters . end _date || filters . recent _days || filters . time _filter _key ;
// 时间范围
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
} ;
const timeRange = inferTimeRangeFromFilters ( filters ) ;
if ( timeRange ) {
setTradingTimeRange ( timeRange ) ;
}
} else if ( ! hasTimeInFilters && tradingTimeRange ) {
setTradingTimeRange ( null ) ;
}
} , [ filters , industryData , findIndustryPath , industryValue , tradingTimeRange ] ) ;
} , [ filters , industryData , industryValue , tradingTimeRange ] ) ;
// 搜索 股票
// 股票搜索
const handleSearch = ( value ) => {
if ( ! value || ! allStocks || allStocks . length === 0 ) {
setStockOptions ( [ ] ) ;
@@ -215,231 +218,132 @@ const CompactSearchBox = ({
}
const results = stockService . fuzzySearch ( value , allStocks , 10 ) ;
const options = results . map ( stock => ( {
const options = results . map ( ( stock ) => ( {
value : stock . code ,
label : (
< div style = { { display : ' flex' , alignItems : ' center' , gap : 8 } } >
< StockOutlined style = { { color : ' #1890ff' } } / >
< span style = { { fontWeight : 500 , color : ' #333' } } > { stock . code } < / s p a n >
< span style = { { color : ' #666' } } > { stock . name } < / s p a n >
< div style = { { display : " flex" , alignItems : " center" , gap : 8 } } >
< StockOutlined style = { { color : " #1890ff" } } / >
< span style = { { fontWeight : 500 , color : " #333" } } > { stock . code } < / s p a n >
< span style = { { color : " #666" } } > { stock . name } < / s p a n >
< / d i v >
) ,
stockInfo : stock
stockInfo : stock ,
} ) ) ;
setStockOptions ( options ) ;
} ;
const buildFilterParams = useCallback ( ( overrides = { } ) => {
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 ( ',' ) ;
}
// 先展开 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 ,
} ;
// 移除不需要的字段
delete result . per _page ;
// 添加 return_type 参数(用于收益排序)
if ( returnType ) {
result . return _type = returnType ;
} else {
// 确保非收益排序时不带 return_type
delete result . return _type ;
}
if ( mode !== undefined && mode !== null ) {
result . mode = mode ;
}
if ( pageSize !== undefined && pageSize !== null ) {
result . per _page = pageSize ;
}
return result ;
} , [ sort , importance , filters . q , industryValue , tradingTimeRange , mode , pageSize ] ) ;
// 股票选择
const handleStockSelect = ( _value , option ) => {
const stockInfo = option . stockInfo ;
if ( stockInfo ) {
if ( trackingFunctions . trackRelatedStockClicked ) {
trackingFunctions . trackRelatedStockClicked ( {
trackingFunctions . trackRelatedStockClicked ? . ( {
stockCode : stockInfo . code ,
stockName : stockInfo . name ,
source : ' search_box_autocomplete' ,
source : " search_box_autocomplete" ,
timestamp : new Date ( ) . toISOString ( ) ,
} ) ;
}
const displayValue = ` ${ stockInfo . code } ${ stockInfo . name } ` ;
setInputValue ( displayValue ) ;
// 存储显示值,供 useEffect 同步时使用
stockDisplayValueRef . current = { code : stockInfo . code , displayValue } ;
const params = build FilterParams( {
q : stockInfo . code , // 接口只传代码
industry _code : ''
} ) ;
const params = create FilterParams( { q : stockInfo . code , industry _code : "" } ) ;
triggerSearch ( params ) ;
}
} ;
// 重要性变更
const handleImportanceChange = ( value ) => {
setImportance ( value ) ;
debouncedSearchRef . current ? . cancel ( ) ;
if ( debouncedSearchRef . current ) {
debouncedSearchRef . current . cancel ( ) ;
}
const importanceStr = value . length === 0 ? 'all' : value . join ( ',' ) ;
if ( trackingFunctions . trackNewsFilterApplied ) {
trackingFunctions . trackNewsFilterApplied ( {
filterType : 'importance' ,
const importanceStr = value . length === 0 ? "all" : value . join ( "," ) ;
trackingFunctions . trackNewsFilterApplied ? . ( {
filterType : "importance" ,
filterValue : importanceStr ,
timestamp : new Date ( ) . toISOString ( ) ,
} ) ;
}
const params = build FilterParams ( { importance : importanceStr } ) ;
triggerSearch ( params ) ;
triggerSearch ( create FilterParams( { importance : importanceStr } ) ) ;
} ;
// 排序变更
const handleSortChange = ( value ) => {
setSort ( value ) ;
debouncedSearchRef . current ? . cancel ( ) ;
if ( debouncedSearchRef . current ) {
debouncedSearchRef . current . cancel ( ) ;
}
if ( trackingFunctions . trackNewsSorted ) {
trackingFunctions . trackNewsSorted ( {
trackingFunctions . trackNewsSorted ? . ( {
sortBy : value ,
previousSortBy : sort ,
timestamp : new Date ( ) . toISOString ( ) ,
} ) ;
}
const params = build FilterParams ( { sort : value } ) ;
triggerSearch ( params ) ;
triggerSearch ( create FilterParams( { sort : value } ) ) ;
} ;
// 行业变更
const handleIndustryChange = ( value ) => {
setIndustryValue ( value ) ;
debouncedSearchRef . current ? . cancel ( ) ;
if ( debouncedSearchRef . current ) {
debouncedSearchRef . current . cancel ( ) ;
}
if ( trackingFunctions . trackNewsFilterApplied ) {
trackingFunctions . trackNewsFilterApplied ( {
filterType : 'industry' ,
filterValue : value ? . [ value . length - 1 ] || '' ,
trackingFunctions . trackNewsFilterApplied ? . ( {
filterType : "industry" ,
filterValue : value ? . [ value . length - 1 ] || "" ,
timestamp : new Date ( ) . toISOString ( ) ,
} ) ;
}
const params = buildFilterParams ( {
industry _code : value ? . [ value . length - 1 ] || ''
} ) ;
triggerSearch ( params ) ;
triggerSearch ( createFilterParams ( { industry _code : value ? . [ value . length - 1 ] || "" } ) ) ;
} ;
// 时间筛选变更
const handleTradingTimeChange = ( timeConfig ) => {
if ( ! timeConfig ) {
setTradingTimeRange ( null ) ;
if ( trackingFunctions . trackNewsFilterApplied ) {
trackingFunctions . trackNewsFilterAppli ed( {
filterType : 'time_range' ,
filterValue : 'cleared' ,
trackingFunctions . trackNewsFilterApplied ? . ( {
filterType : "time_range" ,
filterValue : "clear ed" ,
timestamp : new Date ( ) . toISOString ( ) ,
} ) ;
}
const params = buildFilterParams ( {
start _date : '' ,
end _date : '' ,
recent _days : ''
} ) ;
triggerSearch ( params ) ;
triggerSearch ( createFilterParams ( { start _date : "" , end _date : "" , recent _days : "" } ) ) ;
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 = '' ;
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' ) ;
params . end _date = range [ 1 ] . format ( ' YYYY-MM-DD HH:mm:ss' ) ;
params . recent _days = '' ;
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 } ) ;
if ( trackingFunctions . trackNewsFilterApplied ) {
trackingFunctions . trackNewsFilterApplied ( {
filterType : 'time_range' ,
trackingFunctions . trackNewsFilterApplied ? . ( {
filterType : "time_range" ,
filterValue : label ,
timeRangeType : type ,
timestamp : new Date ( ) . toISOString ( ) ,
} ) ;
}
const searchParams = build FilterParams ( { ... params , mode } ) ;
triggerSearch ( searchParams ) ;
triggerSearch ( create FilterParams( { ... params , mode } ) ) ;
} ;
// 主搜索
const handleMainSearch = ( ) => {
if ( debouncedSearchRef . current ) {
debouncedSearchRef . current . cancel ( ) ;
}
debouncedSearchRef . current ? . cancel ( ) ;
const params = createFilterParams ( { q : inputValue , industry _code : "" } ) ;
const params = buildFilterParams ( {
q : inputValue ,
industry _code : ''
} ) ;
if ( trackingFunctions . trackNewsSearched && inputValue ) {
trackingFunctions . trackNewsSearched ( {
if ( inputValue ) {
trackingFunctions . trackNewsSearched ? . ( {
searchQuery : inputValue ,
searchType : ' main_search' ,
searchType : " main_search" ,
filters : params ,
timestamp : new Date ( ) . toISOString ( ) ,
} ) ;
@@ -448,39 +352,32 @@ const CompactSearchBox = ({
triggerSearch ( params ) ;
} ;
const handleInputChange = ( value ) => {
setInputValue ( value ) ;
} ;
// 重置
const handleReset = ( ) => {
setInputValue ( '' ) ;
setInputValue ( "" ) ;
setStockOptions ( [ ] ) ;
setIndustryValue ( [ ] ) ;
setSort ( ' new' ) ;
setSort ( " new" ) ;
setImportance ( [ ] ) ;
setTradingTimeRange ( null ) ;
if ( trackingFunctions . trackNewsFilterApplied ) {
trackingFunctions . trackNewsFilterApplied ( {
filterTyp e : 'reset' ,
filterValue : 'all_filters_cleared' ,
trackingFunctions . trackNewsFilterApplied ? . ( {
filterType : "reset" ,
filterValu e : "all_filters_cleared" ,
timestamp : new Date ( ) . toISOString ( ) ,
} ) ;
}
const resetParams = {
q : '' ,
industry _code : '' ,
sort : ' new' ,
importance : ' all' ,
start _date : '' ,
end _date : '' ,
recent _days : '' ,
onSearch ( {
q : "" ,
industry _code : "" ,
sort : " new" ,
importance : " all" ,
start _date : "" ,
end _date : "" ,
recent _days : "" ,
page : 1 ,
_forceRefresh : Date . now ( )
} ;
onSearch ( resetParams ) ;
_forceRefresh : Date . now ( ) ,
} ) ;
} ;
const handleCascaderFocus = async ( ) => {
@@ -488,57 +385,62 @@ const CompactSearchBox = ({
dispatch ( fetchIndustryData ( ) ) ;
}
} ;
return (
< div style = { { padding : 0 , background : ' transparent' } } >
< div style = { { padding : 0 , background : " transparent" } } >
{ /* 第一行:搜索框 + 日期筛选 */ }
< Flex
align = "center"
gap = { isMobile ? 8 : 12 }
style = { {
background : ' rgba(255, 255, 255, 0.03)' ,
background : " rgba(255, 255, 255, 0.03)" ,
border : ` 1px solid ${ PROFESSIONAL _COLORS . gold [ 500 ] } ` ,
borderRadius : ' 24px' ,
padding : isMobile ? ' 2px 4px' : ' 8px 16px' ,
marginBottom : isMobile ? 8 : 12
borderRadius : " 24px" ,
padding : isMobile ? " 2px 4px" : " 8px 16px" ,
marginBottom : isMobile ? 8 : 12 ,
} }
>
{ /* 搜索框 - flex: 1 占满剩余空间 */ }
< AutoComplete
value = { inputValue }
onChange = { handleInputChang e}
onChange = { setInputValu e}
onSearch = { handleSearch }
onSelect = { handleStockSelect }
onFocus = { onSearchFocus }
options = { stockOptions }
onKeyDown = { ( e ) => {
if ( e . key === 'Enter' ) {
handleMainSearch ( ) ;
}
} }
onKeyDown = { ( e ) => e . key === "Enter" && handleMainSearch ( ) }
style = { { flex : 1 , minWidth : isMobile ? 100 : 200 } }
className = "gold-placeholder"
allowClear = { {
clearIcon : < CloseCircleOutlined style = { { color : PROFESSIONAL _COLORS . text . muted , fontSize : 14 } } / >
clearIcon : (
< CloseCircleOutlined
style = { { color : PROFESSIONAL _COLORS . text . muted , fontSize : 14 } }
/ >
) ,
} }
>
< Input
prefix = { < SearchOutlined style = { { color : PROFESSIONAL _COLORS . gold [ 500 ] } } / > }
placeholder = "搜索股票/话题..."
style = { {
border : ' none' ,
background : ' transparent' ,
border : " none" ,
background : " transparent" ,
color : PROFESSIONAL _COLORS . text . primary ,
boxShadow : ' none'
boxShadow : " none" ,
} }
/ >
< / A u t o C o m p l e t e >
{ /* 分隔线 - H5 时隐藏 */ }
{ ! isMobile && < Divider type = "vertical" style = { { height : 24 , margin : '0 8px' , borderColor : 'rgba(255,255,255,0.15)' } } / > }
{ ! isMobile && (
< Divider
type = "vertical"
style = { { height : 24 , margin : "0 8px" , borderColor : "rgba(255,255,255,0.15)" } }
/ >
) }
{ /* 日期筛选按钮组 */ }
< div style = { { display : 'flex' , alignItems : 'center' , gap : 0 } } >
< CalendarOutlined style = { { color : PROFESSIONAL _COLORS . gold [ 500 ] , fontSize : 14 , marginRight : 8 } } / >
< div style = { { display : "flex" , alignItems : "center" , gap : 0 } } >
< CalendarOutlined
style = { { color : PROFESSIONAL _COLORS . gold [ 500 ] , fontSize : 14 , marginRight : 8 } }
/ >
< TradingTimeFilter
value = { tradingTimeRange ? . key || null }
onChange = { handleTradingTimeChange }
@@ -548,73 +450,74 @@ const CompactSearchBox = ({
< / d i v >
< / F l e x >
{ /* 第二行:筛选条件 - 主线模式下隐藏(主线模式有自己的筛选器) */ }
{ mode !== ' mainline' && (
{ /* 第二行:筛选条件 */ }
{ mode !== " mainline" && (
< Flex justify = "space-between" align = "center" >
{ /* 左侧筛选 */ }
< Space size = { isMobile ? 4 : 8 } >
{ /* 行业筛选 */ }
< Cascader
value = { industryValue }
onChange = { handleIndustryChange }
onFocus = { handleCascaderFocus }
options = { industryData || [ ] }
placeholder = {
< span style = { { display : ' flex' , alignItems : ' center' , gap : 4 } } >
< span style = { { display : " flex" , alignItems : " center" , gap : 4 } } >
< FilterOutlined style = { { fontSize : 12 } } / >
{ isMobile ? ' 行业' : ' 行业筛选' }
{ isMobile ? " 行业" : " 行业筛选" }
< / s p a n >
}
changeOnSelect
showSearch = { {
filter : ( inputValue , path ) =>
path . some ( option =>
path . some ( ( option ) =>
option . label . toLowerCase ( ) . includes ( inputValue . toLowerCase ( ) )
)
) ,
} }
allowClear
expandTrigger = "hover"
displayRender = { ( labels ) => labels [ labels . length - 1 ] || ( isMobile ? '行业' : '行业筛选' ) }
displayRender = { ( labels ) =>
labels [ labels . length - 1 ] || ( isMobile ? "行业" : "行业筛选" )
}
disabled = { industryLoading }
style = { { minWidth : isMobile ? 70 : 80 } }
suffixIcon = { null }
className = "transparent-cascader"
/ >
{ /* 事件等级 */ }
< AntSelect
mode = "multiple"
value = { importance }
onChange = { handleImportanceChange }
style = { { minWidth : isMobile ? 100 : 120 } }
placeholder = {
< span style = { { display : ' flex' , alignItems : ' center' , gap : 4 } } >
< span style = { { display : " flex" , alignItems : " center" , gap : 4 } } >
< ThunderboltOutlined style = { { fontSize : 12 } } / >
{ isMobile ? ' 等级' : ' 事件等级' }
{ isMobile ? " 等级" : " 事件等级" }
< / s p a n >
}
maxTagCount = { 0 }
maxTagPlaceholder = { ( omittedValues ) => isMobile ? ` ${ omittedValues . length } 项 ` : ` 已选 ${ omittedValues . length } 项 ` }
maxTagPlaceholder = { ( omittedValues ) =>
isMobile ? ` ${ omittedValues . length } 项 ` : ` 已选 ${ omittedValues . length } 项 `
}
className = "bracket-select"
>
{ IMPORTANCE _OPTIONS . map ( opt => (
< Option key = { opt . value } value = { opt . value } > { opt . label } < / O p t i o n >
{ IMPORTANCE _OPTIONS . map ( ( opt ) => (
< Option key = { opt . value } value = { opt . value } >
{ opt . label }
< / O p t i o n >
) ) }
< / A n t S e l e c t >
< / S p a c e >
{ /* 右侧排序和重置 */ }
< Space size = { isMobile ? 4 : 8 } >
{ /* 排序 */ }
< AntSelect
value = { sort }
onChange = { handleSortChange }
style = { { minWidth : isMobile ? 55 : 120 } }
className = "bracket-select"
>
{ SORT _OPTIONS . map ( opt => (
{ SORT _OPTIONS . map ( ( opt ) => (
< Option key = { opt . value } value = { opt . value } >
< span style = { { display : ' flex' , alignItems : ' center' , gap : 4 } } >
< span style = { { display : " flex" , alignItems : " center" , gap : 4 } } >
< SortAscendingOutlined style = { { fontSize : 12 } } / >
{ isMobile ? opt . mobileLabel : opt . label }
< / s p a n >
@@ -622,14 +525,13 @@ const CompactSearchBox = ({
) ) }
< / A n t S e l e c t >
{ /* 重置按钮 */ }
< Button
icon = { < ReloadOutlined / > }
onClick = { handleReset }
type = "text"
style = { { color : PROFESSIONAL _COLORS . text . secondary } }
>
{ ! isMobile && ' 重置筛选' }
{ ! isMobile && " 重置筛选" }
< / B u t t o n >
< / S p a c e >
< / F l e x >