diff --git a/src/views/Community/components/CompactSearchBox.css b/src/views/Community/components/CompactSearchBox.css
new file mode 100644
index 00000000..6a8c0ede
--- /dev/null
+++ b/src/views/Community/components/CompactSearchBox.css
@@ -0,0 +1,80 @@
+/* CompactSearchBox.css */
+/* 紧凑版搜索和筛选组件样式 */
+
+/* 搜索框 placeholder 白色 - 全覆盖选择器 */
+.gold-placeholder input::placeholder,
+.gold-placeholder input[type="text"]::placeholder,
+.gold-placeholder .ant-input::placeholder,
+.gold-placeholder .ant-input-affix-wrapper input::placeholder,
+.gold-placeholder .ant-select-selection-search-input::placeholder,
+.gold-placeholder .ant-input-affix-wrapper .ant-input::placeholder {
+ color: #FFFFFF !important;
+ opacity: 0.8 !important;
+}
+
+/* AutoComplete placeholder - 关键选择器 */
+.gold-placeholder .ant-select-selection-placeholder {
+ color: #FFFFFF !important;
+ opacity: 0.8 !important;
+}
+
+.gold-placeholder .ant-input-affix-wrapper .ant-input,
+.gold-placeholder .ant-input {
+ color: #FFFFFF !important;
+}
+
+.gold-placeholder .ant-input-affix-wrapper {
+ background: transparent !important;
+}
+
+/* 透明下拉框样式 */
+.transparent-select .ant-select-selector,
+.transparent-cascader .ant-select-selector {
+ background: transparent !important;
+ border: none !important;
+ box-shadow: none !important;
+}
+
+/* 行业筛选宽度自适应,减少间距 */
+.transparent-cascader {
+ width: auto !important;
+}
+
+.transparent-cascader .ant-select-selector {
+ padding-right: 8px !important;
+ min-width: unset !important;
+}
+
+/* 行业筛选 Cascader placeholder 白色 */
+.transparent-select .ant-select-selection-placeholder,
+.transparent-cascader .ant-select-selection-placeholder,
+.transparent-cascader input::placeholder,
+.transparent-cascader .ant-cascader-input::placeholder {
+ color: #FFFFFF !important;
+}
+
+.transparent-cascader .ant-cascader-input {
+ background: transparent !important;
+}
+
+/* 行业筛选 Cascader 选中值白色 */
+.transparent-cascader .ant-select-selection-item,
+.transparent-cascader .ant-cascader-picker-label {
+ color: #FFFFFF !important;
+}
+
+/* 方括号样式下拉框 - 无边框 */
+.bracket-select .ant-select-selector {
+ background: transparent !important;
+ border: none !important;
+ box-shadow: none !important;
+}
+
+.bracket-select .ant-select-selection-item,
+.bracket-select .ant-select-selection-placeholder {
+ color: #FFFFFF !important;
+}
+
+.bracket-select .ant-select-arrow {
+ color: rgba(255, 255, 255, 0.65) !important;
+}
diff --git a/src/views/Community/components/CompactSearchBox.js b/src/views/Community/components/CompactSearchBox.js
index b51eb8ba..1cb9d51a 100644
--- a/src/views/Community/components/CompactSearchBox.js
+++ b/src/views/Community/components/CompactSearchBox.js
@@ -4,30 +4,49 @@
import React, { useState, useMemo, useEffect, useCallback, useRef } from 'react';
import {
Input, Cascader, Button, Space, Tag, AutoComplete, Select as AntSelect,
- Tooltip
+ Tooltip, Divider, Flex
} from 'antd';
import {
SearchOutlined, CloseCircleOutlined, StockOutlined, FilterOutlined,
- CalendarOutlined, SortAscendingOutlined
+ 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 { stockService } from '../../../services/stockService';
-import { logger } from '../../../utils/logger';
+import { fetchIndustryData, selectIndustryData, selectIndustryLoading } from '@store/slices/industrySlice';
+import { stockService } from '@services/stockService';
+import { logger } from '@utils/logger';
import TradingTimeFilter from './TradingTimeFilter';
-import { PROFESSIONAL_COLORS } from '../../../constants/professionalTheme';
+import { PROFESSIONAL_COLORS } from '@constants/professionalTheme';
+import './CompactSearchBox.css';
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 = {}
+ trackingFunctions = {},
+ isMobile = false
}) => {
// 状态
const [stockOptions, setStockOptions] = useState([]);
@@ -420,19 +439,21 @@ const CompactSearchBox = ({
dispatch(fetchIndustryData());
}
};
-
return (
-
- {/* 单行紧凑布局 - 移动端自动换行 */}
-
- {/* 搜索框 */}
+
+ {/* 第一行:搜索框 + 日期筛选 */}
+
+ {/* 搜索框 - flex: 1 占满剩余空间 */}
{
if (e.key === 'Enter') {
handleMainSearch();
}
}}
- style={{ width: window.innerWidth < 768 ? '100%' : 240, minWidth: window.innerWidth < 768 ? 0 : 240 }}
+ style={{ flex: 1, minWidth: isMobile ? 100 : 200 }}
+ className="gold-placeholder"
>
}
+ placeholder="搜索股票/话题..."
style={{
- borderRadius: '8px',
- border: `1px solid ${PROFESSIONAL_COLORS.border.default}`,
- boxShadow: `0 2px 8px rgba(255, 195, 0, 0.1)`,
- background: PROFESSIONAL_COLORS.background.secondary,
- color: PROFESSIONAL_COLORS.text.primary
+ border: 'none',
+ background: 'transparent',
+ color: PROFESSIONAL_COLORS.text.primary,
+ boxShadow: 'none'
}}
/>
- {/* 时间筛选 */}
-
-
-
-
-
-
+ {/* 分隔线 - H5 时隐藏 */}
+ {!isMobile && }
- {/* 行业筛选 */}
-
+ {/* 日期筛选按钮组 */}
+
+
+
+
+
+
+ {/* 第二行:筛选条件 */}
+
+ {/* 左侧筛选 */}
+
+ {/* 行业筛选 */}
+
+ {isMobile ? '行业' : '行业筛选'}
+
+ }
changeOnSelect
showSearch={{
filter: (inputValue, path) =>
@@ -489,145 +521,65 @@ const CompactSearchBox = ({
}}
allowClear
expandTrigger="hover"
- displayRender={(labels) => labels[labels.length - 1] || '行业'}
+ displayRender={(labels) => labels[labels.length - 1] || (isMobile ? '行业' : '行业筛选')}
disabled={industryLoading}
- style={{
- width: window.innerWidth < 768 ? '100%' : 120,
- minWidth: window.innerWidth < 768 ? 0 : 120,
- borderRadius: '8px'
- }}
- suffixIcon={}
+ style={{ minWidth: isMobile ? 70 : 80 }}
+ suffixIcon={null}
+ className="transparent-cascader"
/>
-
- {/* 重要性筛选 */}
-
+ {/* 事件等级 */}
+
+ {isMobile ? '等级' : '事件等级'}
+
+ }
maxTagCount={0}
- maxTagPlaceholder={(omittedValues) => `已选 ${omittedValues.length} 项`}
+ maxTagPlaceholder={(omittedValues) => isMobile ? `${omittedValues.length}项` : `已选 ${omittedValues.length} 项`}
+ className="bracket-select"
>
-
-
-
-
+ {IMPORTANCE_OPTIONS.map(opt => (
+
+ ))}
-
+
- {/* 排序 */}
-
+ {/* 右侧排序和重置 */}
+
+ {/* 排序 */}
}
+ style={{ minWidth: isMobile ? 55 : 120 }}
+ className="bracket-select"
>
-
-
-
-
-
+ {SORT_OPTIONS.map(opt => (
+
+ ))}
-
- {/* 重置按钮 */}
-
+ {/* 重置按钮 */}
}
+ icon={}
onClick={handleReset}
- danger
- type="primary"
- style={{
- borderRadius: '8px',
- boxShadow: '0 2px 8px rgba(255, 77, 79, 0.2)'
- }}
+ type="text"
+ style={{ color: PROFESSIONAL_COLORS.text.secondary }}
>
- 重置
+ {!isMobile && '重置筛选'}
-
-
-
- {/* 激活的筛选标签(如果有的话) */}
- {(inputValue || industryValue.length > 0 || importance.length > 0 || tradingTimeRange || sort !== 'new') && (
-
- {inputValue && (
- {
- setInputValue('');
- const params = buildFilterParams({ q: '' });
- triggerSearch(params);
- }} color="blue">
- 搜索: {inputValue}
-
- )}
- {tradingTimeRange && (
- {
- setTradingTimeRange(null);
- const params = buildFilterParams({
- start_date: '',
- end_date: '',
- recent_days: ''
- });
- triggerSearch(params);
- }} color="green">
- {tradingTimeRange.label}
-
- )}
- {industryValue.length > 0 && industryData && (
- {
- setIndustryValue([]);
- const params = buildFilterParams({ industry_code: '' });
- triggerSearch(params);
- }} color="orange">
- 行业: {(() => {
- const findLabel = (code, data) => {
- for (const item of data) {
- if (code.startsWith(item.value)) {
- if (item.value === code) {
- return item.label;
- } else {
- return findLabel(code, item.children);
- }
- }
- }
- return null;
- };
- const lastLevelCode = industryValue[industryValue.length - 1];
- return findLabel(lastLevelCode, industryData);
- })()}
-
- )}
- {importance.length > 0 && (
- {
- setImportance([]);
- const params = buildFilterParams({ importance: 'all' });
- triggerSearch(params);
- }} color="purple">
- 重要性: {importance.map(imp => ({ 'S': '极高', 'A': '高', 'B': '中', 'C': '低' }[imp] || imp)).join(', ')}
-
- )}
- {sort && sort !== 'new' && (
- {
- setSort('new');
- const params = buildFilterParams({ sort: 'new' });
- triggerSearch(params);
- }} color="cyan">
- 排序: {({ 'hot': '最热', 'importance': '重要性', 'returns_avg': '平均收益', 'returns_week': '周收益' }[sort] || sort)}
-
- )}
-
- )}
+
+
);
};
diff --git a/src/views/Community/components/TradingTimeFilter.js b/src/views/Community/components/TradingTimeFilter.js
index 2ebe7d5b..f1ee1749 100644
--- a/src/views/Community/components/TradingTimeFilter.js
+++ b/src/views/Community/components/TradingTimeFilter.js
@@ -1,20 +1,25 @@
// src/views/Community/components/TradingTimeFilter.js
// 交易时段智能筛选组件
import React, { useState, useMemo, useEffect } from 'react';
-import { Space, Button, Tag, Tooltip, DatePicker, Popover } from 'antd';
-import { ClockCircleOutlined, CalendarOutlined } from '@ant-design/icons';
+import { Space, Button, Tag, Tooltip, DatePicker, Popover, Select } from 'antd';
+import { ClockCircleOutlined, CalendarOutlined, FilterOutlined } from '@ant-design/icons';
import dayjs from 'dayjs';
import locale from 'antd/es/date-picker/locale/zh_CN';
-import { logger } from '../../../utils/logger';
+import { logger } from '@utils/logger';
+import { PROFESSIONAL_COLORS } from '@constants/professionalTheme';
const { RangePicker } = DatePicker;
+const { Option } = Select;
+
/**
* 交易时段筛选组件
* @param {string} value - 当前选中的 key(受控)
* @param {Function} onChange - 时间范围变化回调 (timeConfig) => void
+ * @param {boolean} compact - 是否使用紧凑模式(PC 端搜索栏内使用)
+ * @param {boolean} mobile - 是否使用移动端模式(下拉选择)
*/
-const TradingTimeFilter = ({ value, onChange }) => {
+const TradingTimeFilter = ({ value, onChange, compact = false, mobile = false }) => {
const [selectedKey, setSelectedKey] = useState(null);
const [customRangeVisible, setCustomRangeVisible] = useState(false);
const [customRange, setCustomRange] = useState(null);
@@ -266,7 +271,39 @@ const TradingTimeFilter = ({ value, onChange }) => {
}
};
- // 渲染按钮
+ // 渲染紧凑模式按钮(PC 端搜索栏内使用,文字按钮 + | 分隔符)
+ const renderCompactButton = (config, showDivider = true) => {
+ const isSelected = selectedKey === config.key;
+ const fullTooltip = config.timeHint ? `${config.tooltip} · ${config.timeHint}` : config.tooltip;
+
+ return (
+
+
+ handleButtonClick(config)}
+ style={{
+ cursor: 'pointer',
+ padding: '4px 8px',
+ borderRadius: '4px',
+ fontSize: '13px',
+ fontWeight: isSelected ? 600 : 400,
+ color: isSelected ? PROFESSIONAL_COLORS.gold[500] : PROFESSIONAL_COLORS.text.secondary,
+ background: isSelected ? 'rgba(255, 195, 0, 0.15)' : 'transparent',
+ transition: 'all 0.2s ease',
+ whiteSpace: 'nowrap',
+ }}
+ >
+ {config.label}
+
+
+ {showDivider && (
+ |
+ )}
+
+ );
+ };
+
+ // 渲染按钮(默认模式)
const renderButton = (config) => {
const isSelected = selectedKey === config.key;
@@ -321,6 +358,98 @@ const TradingTimeFilter = ({ value, onChange }) => {
);
+ // 移动端模式:下拉选择器
+ if (mobile) {
+ const allButtons = [...timeRangeConfig.dynamic, ...timeRangeConfig.fixed];
+
+ const handleMobileSelect = (key) => {
+ if (key === selectedKey) {
+ // 取消选中
+ setSelectedKey(null);
+ onChange(null);
+ } else {
+ const config = allButtons.find(b => b.key === key);
+ if (config) {
+ handleButtonClick(config);
+ }
+ }
+ };
+
+ return (
+
+ );
+ }
+
+ // 紧凑模式:PC 端搜索栏内的样式
+ if (compact) {
+ // 合并所有按钮配置
+ const allButtons = [...timeRangeConfig.dynamic, ...timeRangeConfig.fixed];
+
+ return (
+
+ {allButtons.map((config, index) =>
+ renderCompactButton(config, index < allButtons.length - 1)
+ )}
+ {/* 更多时间 */}
+
|
+
+
+
+
+ 更多
+
+
+
+
+ );
+ }
+
+ // 默认模式:移动端/独立使用
return (
{/* 动态按钮(根据时段显示多个) */}