feat: 新增实时要闻·动态追踪与市场复盘功能,优化导航体验

新增功能:
- 实时要闻·动态追踪横向滚动卡片(DynamicNewsCard)
- 动态新闻事件卡片组件(DynamicNewsEventCard)
- 市场复盘卡片组件(MarketReviewCard)
- 股票涨跌幅指标组件(StockChangeIndicators)
- 交易时间工具函数(tradingTimeUtils)
- Mock API 支持动态新闻数据生成

UI 优化:
- EventFollowButton 改用 react-icons 星星图标,实现真正的空心/实心效果
- 关注按钮添加半透明白色背景(whiteAlpha.500),悬停效果更明显
- 事件卡片标题添加右侧留白,防止关注按钮遮挡文字

性能优化:
- 禁用 Router v7_startTransition 特性,解决路由切换延迟 2 秒问题
- 调整导航菜单点击顺序(先跳转后关闭),提升响应速度

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-10-31 14:11:03 +08:00
parent 5d8ad5e442
commit c372832f1f
11 changed files with 1211 additions and 23 deletions

View File

@@ -0,0 +1,175 @@
// src/utils/tradingTimeUtils.js
// 交易时间相关工具函数
import moment from 'moment';
/**
* 获取当前时间应该显示的实时要闻时间范围
* 规则:
* - 15:00 之前:显示昨日 15:00 - 今日 15:00
* - 15:30 之后:显示今日 15:00 - 当前时间
*
* @returns {{ startTime: Date, endTime: Date, description: string }}
*/
export const getCurrentTradingTimeRange = () => {
const now = moment();
const currentHour = now.hour();
const currentMinute = now.minute();
// 计算当前是第几分钟(方便比较)
const currentTimeInMinutes = currentHour * 60 + currentMinute;
const cutoffTime1500 = 15 * 60; // 15:00 = 900分钟
const cutoffTime1530 = 15 * 60 + 30; // 15:30 = 930分钟
let startTime, endTime, description;
if (currentTimeInMinutes < cutoffTime1500) {
// 15:00 之前:显示昨日 15:00 - 今日 15:00
startTime = moment().subtract(1, 'day').hour(15).minute(0).second(0).millisecond(0).toDate();
endTime = moment().hour(15).minute(0).second(0).millisecond(0).toDate();
description = '昨日15:00 - 今日15:00';
} else if (currentTimeInMinutes >= cutoffTime1530) {
// 15:30 之后:显示今日 15:00 - 当前时间
startTime = moment().hour(15).minute(0).second(0).millisecond(0).toDate();
endTime = now.toDate();
description = '今日15:00 - 当前时间';
} else {
// 15:00 - 15:30 之间:过渡期,保持显示昨日 15:00 - 今日 15:00
startTime = moment().subtract(1, 'day').hour(15).minute(0).second(0).millisecond(0).toDate();
endTime = moment().hour(15).minute(0).second(0).millisecond(0).toDate();
description = '昨日15:00 - 今日15:00';
}
return {
startTime,
endTime,
description,
rangeType: currentTimeInMinutes >= cutoffTime1530 ? 'current_day' : 'full_day'
};
};
/**
* 获取市场复盘的时间范围
* 规则:显示最近一个完整的交易日(昨日 15:00 - 今日 15:00
*
* @returns {{ startTime: Date, endTime: Date, description: string }}
*/
export const getMarketReviewTimeRange = () => {
const now = moment();
const currentHour = now.hour();
const currentMinute = now.minute();
// 计算当前是第几分钟
const currentTimeInMinutes = currentHour * 60 + currentMinute;
const cutoffTime1530 = 15 * 60 + 30; // 15:30 = 930分钟
let startTime, endTime, description;
if (currentTimeInMinutes >= cutoffTime1530) {
// 15:30 之后:显示昨日 15:00 - 今日 15:00刚刚完成的交易日
startTime = moment().subtract(1, 'day').hour(15).minute(0).second(0).millisecond(0).toDate();
endTime = moment().hour(15).minute(0).second(0).millisecond(0).toDate();
description = '昨日15:00 - 今日15:00';
} else {
// 15:30 之前:显示前日 15:00 - 昨日 15:00上一个完整交易日
startTime = moment().subtract(2, 'days').hour(15).minute(0).second(0).millisecond(0).toDate();
endTime = moment().subtract(1, 'day').hour(15).minute(0).second(0).millisecond(0).toDate();
description = '前日15:00 - 昨日15:00';
}
return {
startTime,
endTime,
description,
rangeType: 'market_review'
};
};
/**
* 根据时间范围过滤事件列表
*
* @param {Array} events - 事件列表
* @param {Date} startTime - 开始时间
* @param {Date} endTime - 结束时间
* @returns {Array} 过滤后的事件列表
*/
export const filterEventsByTimeRange = (events, startTime, endTime) => {
if (!events || !Array.isArray(events)) {
return [];
}
if (!startTime || !endTime) {
return events;
}
const startMoment = moment(startTime);
const endMoment = moment(endTime);
return events.filter(event => {
if (!event.created_at) {
return false;
}
const eventTime = moment(event.created_at);
return eventTime.isSameOrAfter(startMoment) && eventTime.isSameOrBefore(endMoment);
});
};
/**
* 判断当前是否应该显示市场复盘模块
* 根据需求:市场复盘模块一直显示
*
* @returns {boolean}
*/
export const shouldShowMarketReview = () => {
// 市场复盘模块始终显示
return true;
};
/**
* 获取时间范围的描述文本
*
* @param {Date} startTime - 开始时间
* @param {Date} endTime - 结束时间
* @returns {string}
*/
export const getTimeRangeDescription = (startTime, endTime) => {
if (!startTime || !endTime) {
return '';
}
const startStr = moment(startTime).format('MM-DD HH:mm');
const endStr = moment(endTime).format('MM-DD HH:mm');
return `${startStr} - ${endStr}`;
};
/**
* 判断是否为交易日(简化版本,只判断周末)
* 注意这里没有考虑节假日如需精确判断需要接入交易日历API
*
* @param {Date} date - 日期
* @returns {boolean}
*/
export const isTradingDay = (date) => {
const day = moment(date).day();
// 0 = 周日, 6 = 周六
return day !== 0 && day !== 6;
};
/**
* 获取上一个交易日(简化版本)
*
* @param {Date} date - 日期
* @returns {Date}
*/
export const getPreviousTradingDay = (date) => {
let prevDay = moment(date).subtract(1, 'day');
// 如果是周末,继续往前找
while (!isTradingDay(prevDay.toDate())) {
prevDay = prevDay.subtract(1, 'day');
}
return prevDay.toDate();
};