From f4b58b42cc3a5d7cf48c4d984b363253bdf54537 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Thu, 6 Nov 2025 11:46:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=BA=A4=E6=98=93?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E6=AE=B5=E7=AD=9B=E9=80=89=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Community/components/TradingTimeFilter.js | 340 ++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100644 src/views/Community/components/TradingTimeFilter.js diff --git a/src/views/Community/components/TradingTimeFilter.js b/src/views/Community/components/TradingTimeFilter.js new file mode 100644 index 00000000..88829eb2 --- /dev/null +++ b/src/views/Community/components/TradingTimeFilter.js @@ -0,0 +1,340 @@ +// src/views/Community/components/TradingTimeFilter.js +// 交易时段智能筛选组件 +import React, { useState, useMemo } from 'react'; +import { Space, Button, Tag, Tooltip, DatePicker, Popover } from 'antd'; +import { ClockCircleOutlined, CalendarOutlined } from '@ant-design/icons'; +import dayjs from 'dayjs'; +import locale from 'antd/es/date-picker/locale/zh_CN'; +import { logger } from '../../../utils/logger'; + +const { RangePicker } = DatePicker; + +/** + * 交易时段筛选组件 + * @param {Function} onChange - 时间范围变化回调 (timeConfig) => void + */ +const TradingTimeFilter = ({ onChange }) => { + const [selectedKey, setSelectedKey] = useState(null); + const [customRangeVisible, setCustomRangeVisible] = useState(false); + const [customRange, setCustomRange] = useState(null); + + // 获取当前交易时段 + const getCurrentTradingSession = () => { + const now = dayjs(); + const hour = now.hour(); + const minute = now.minute(); + const currentMinutes = hour * 60 + minute; + + // 转换为分钟数便于比较 + const PRE_MARKET_END = 9 * 60 + 30; // 09:30 + const MORNING_END = 11 * 60 + 30; // 11:30 + const AFTERNOON_START = 13 * 60; // 13:00 + const MARKET_CLOSE = 15 * 60; // 15:00 + + if (currentMinutes < PRE_MARKET_END) { + return 'pre-market'; // 盘前 + } else if (currentMinutes < MORNING_END) { + return 'morning'; // 早盘 + } else if (currentMinutes < AFTERNOON_START) { + return 'lunch'; // 午休 + } else if (currentMinutes < MARKET_CLOSE) { + return 'afternoon'; // 午盘 + } else { + return 'after-hours'; // 盘后 + } + }; + + // 获取时间范围配置 + const timeRangeConfig = useMemo(() => { + const session = getCurrentTradingSession(); + const now = dayjs(); + + // 今日关键时间点 + const today0930 = now.hour(9).minute(30).second(0); + const today1130 = now.hour(11).minute(30).second(0); + const today1300 = now.hour(13).minute(0).second(0); + const today1500 = now.hour(15).minute(0).second(0); + const todayStart = now.startOf('day'); + const todayEnd = now.endOf('day'); + + // 昨日关键时间点 + const yesterday1500 = now.subtract(1, 'day').hour(15).minute(0).second(0); + const yesterdayStart = now.subtract(1, 'day').startOf('day'); + const yesterdayEnd = now.subtract(1, 'day').endOf('day'); + + // 动态按钮配置(根据时段返回不同按钮数组) + const dynamicButtonsMap = { + 'pre-market': [ + { + key: 'latest', + label: '最新', + range: [yesterday1500, today0930], + tooltip: '盘前资讯', + timeHint: `昨日 15:00 - 今日 09:30`, + color: 'purple', + type: 'precise' + } + ], + 'morning': [ + { + key: 'latest', + label: '最新', + range: [today0930, now], + tooltip: '早盘最新', + timeHint: `今日 09:30 - ${now.format('HH:mm')}`, + color: 'green', + type: 'precise' + }, + { + key: 'intraday', + label: '盘中', + range: [today0930, today1500], + tooltip: '盘中交易时段', + timeHint: '今日 09:30 - 15:00', + color: 'blue', + type: 'precise' + } + ], + 'lunch': [ + { + key: 'latest', + label: '最新', + range: [today1130, now], + tooltip: '午休时段', + timeHint: `今日 11:30 - ${now.format('HH:mm')}`, + color: 'orange', + type: 'precise' + } + ], + 'afternoon': [ + { + key: 'latest', + label: '最新', + range: [today1300, now], + tooltip: '午盘最新', + timeHint: `今日 13:00 - ${now.format('HH:mm')}`, + color: 'green', + type: 'precise' + }, + { + key: 'intraday', + label: '盘中', + range: [today0930, today1500], + tooltip: '盘中交易时段', + timeHint: '今日 09:30 - 15:00', + color: 'blue', + type: 'precise' + }, + { + key: 'afternoon', + label: '午盘', + range: [today1300, today1500], + tooltip: '午盘交易时段', + timeHint: '今日 13:00 - 15:00', + color: 'cyan', + type: 'precise' + } + ], + 'after-hours': [ + { + key: 'latest', + label: '最新', + range: [today1500, now], + tooltip: '盘后最新', + timeHint: `今日 15:00 - ${now.format('HH:mm')}`, + color: 'red', + type: 'precise' + } + ] + }; + + // 固定按钮配置(始终显示) + const fixedButtons = [ + { + key: 'morning-fixed', + label: '早盘', + range: [today0930, today1130], + tooltip: '早盘交易时段', + timeHint: '09:30 - 11:30', + color: 'geekblue', + type: 'precise' + }, + { + key: 'today', + label: '今日全天', + range: [todayStart, todayEnd], + tooltip: '今日全天', + timeHint: '今日 00:00 - 23:59', + color: 'purple', + type: 'precise' + }, + { + key: 'yesterday', + label: '昨日', + range: [yesterdayStart, yesterdayEnd], + tooltip: '昨日全天', + timeHint: '昨日 00:00 - 23:59', + color: 'orange', + type: 'precise' + }, + { + key: 'week', + label: '近一周', + range: 7, // 天数 + tooltip: '过去7个交易日', + timeHint: '过去7天', + color: 'magenta', + type: 'recent_days' + }, + { + key: 'month', + label: '近一月', + range: 30, // 天数 + tooltip: '过去30个交易日', + timeHint: '过去30天', + color: 'volcano', + type: 'recent_days' + } + ]; + + return { + dynamic: dynamicButtonsMap[session] || [], + fixed: fixedButtons + }; + }, []); // 空依赖,首次渲染时计算 + + // 按钮点击处理 + const handleButtonClick = (config) => { + logger.debug('TradingTimeFilter', '按钮点击', { config }); + + if (selectedKey === config.key) { + // 取消选中 + setSelectedKey(null); + onChange(null); + } else { + // 选中 + setSelectedKey(config.key); + onChange({ + range: config.range, + type: config.type, + label: config.label, + key: config.key + }); + } + }; + + // 自定义范围确认 + const handleCustomRangeOk = (dates) => { + if (dates && dates.length === 2) { + setCustomRange(dates); + setSelectedKey('custom'); + setCustomRangeVisible(false); + + onChange({ + range: dates, + type: 'precise', + label: `${dates[0].format('MM-DD HH:mm')} - ${dates[1].format('MM-DD HH:mm')}`, + key: 'custom' + }); + + logger.debug('TradingTimeFilter', '自定义范围', { + start: dates[0].format('YYYY-MM-DD HH:mm:ss'), + end: dates[1].format('YYYY-MM-DD HH:mm:ss') + }); + } + }; + + // 渲染按钮 + const renderButton = (config) => { + const isSelected = selectedKey === config.key; + + // 构建完整的 tooltip 提示(文字 + 时间) + const fullTooltip = config.timeHint ? `${config.tooltip} · ${config.timeHint}` : config.tooltip; + + if (isSelected) { + // 选中状态:只显示 Tag,不显示下方时间 + return ( + + handleButtonClick(config)} + > + + {config.label} + + + ); + } else { + // 未选中状态:普通按钮 + return ( + + + + ); + } + }; + + // "更多时间" 按钮内容 + const customRangeContent = ( +
+ +
+ 支持精确到分钟的时间范围选择 +
+
+ ); + + return ( + + {/* 动态按钮(根据时段显示多个) */} + {timeRangeConfig.dynamic.map(config => renderButton(config))} + + {/* 固定按钮(始终显示) */} + {timeRangeConfig.fixed.map(config => renderButton(config))} + + {/* 更多时间 - 自定义范围 */} + + {selectedKey === 'custom' ? ( + + + + 自定义 + + + ) : ( + + + + )} + + + ); +}; + +export default TradingTimeFilter;