diff --git a/src/views/Community/components/DynamicNews/layouts/MainlineTimelineView.js b/src/views/Community/components/DynamicNews/layouts/MainlineTimelineView.js
index 24dea4ff..49eba932 100644
--- a/src/views/Community/components/DynamicNews/layouts/MainlineTimelineView.js
+++ b/src/views/Community/components/DynamicNews/layouts/MainlineTimelineView.js
@@ -24,12 +24,12 @@ import {
Button,
} from "@chakra-ui/react";
import { ChevronDownIcon, ChevronUpIcon, RepeatIcon } from "@chakra-ui/icons";
-import { FiTrendingUp, FiZap, FiClock } from "react-icons/fi";
+import { FiTrendingUp, FiZap } from "react-icons/fi";
import { FireOutlined } from "@ant-design/icons";
import dayjs from "dayjs";
import { Select } from "antd";
-import MiniEventCard from "../../EventCard/MiniEventCard";
import { getApiBase } from "@utils/apiConfig";
+import { getChangeColor } from "@utils/colorUtils";
import "../../SearchFilters/CompactSearchBox.css";
// 固定深色主题颜色
@@ -50,7 +50,7 @@ const COLORS = {
const EVENTS_PER_LOAD = 12;
/**
- * 格式化时间显示
+ * 格式化时间显示 - 始终显示日期,避免跨天混淆
*/
const formatEventTime = (dateStr) => {
if (!dateStr) return "";
@@ -59,78 +59,164 @@ const formatEventTime = (dateStr) => {
const isToday = date.isSame(now, "day");
const isYesterday = date.isSame(now.subtract(1, "day"), "day");
+ // 始终显示日期,用标签区分今天/昨天
if (isToday) {
- return date.format("HH:mm");
+ return `今天 ${date.format("MM-DD HH:mm")}`;
} else if (isYesterday) {
- return `昨天 ${date.format("HH:mm")}`;
+ return `昨天 ${date.format("MM-DD HH:mm")}`;
} else {
return date.format("MM-DD HH:mm");
}
};
/**
- * 单个事件项组件 - 带时间轴
+ * 根据涨跌幅获取背景色
*/
-const TimelineEventItem = React.memo(({ event, isSelected, onEventClick, isHot }) => {
- // 使用后端返回的字段:related_max_chg(最大涨幅)或 related_avg_chg(平均涨幅)
- const changePct = event.related_max_chg ?? event.related_avg_chg ?? event.max_change_pct ?? event.change_pct;
- const hasChange = changePct != null;
- const isPositive = hasChange && changePct >= 0;
+const getChangeBgColor = (value) => {
+ if (value == null || isNaN(value)) return "transparent";
+ const absChange = Math.abs(value);
+ if (value > 0) {
+ if (absChange >= 5) return "rgba(239, 68, 68, 0.12)";
+ if (absChange >= 3) return "rgba(239, 68, 68, 0.08)";
+ return "rgba(239, 68, 68, 0.05)";
+ } else if (value < 0) {
+ if (absChange >= 5) return "rgba(16, 185, 129, 0.12)";
+ if (absChange >= 3) return "rgba(16, 185, 129, 0.08)";
+ return "rgba(16, 185, 129, 0.05)";
+ }
+ return "transparent";
+};
+
+/**
+ * 单个事件项组件 - 卡片式布局
+ */
+const TimelineEventItem = React.memo(({ event, isSelected, onEventClick }) => {
+ // 使用 related_max_chg 作为主要涨幅显示
+ const maxChange = event.related_max_chg;
+ const avgChange = event.related_avg_chg;
+ const hasMaxChange = maxChange != null && !isNaN(maxChange);
+ const hasAvgChange = avgChange != null && !isNaN(avgChange);
+
+ // 用于背景色的涨幅(使用平均超额)
+ const bgValue = avgChange;
return (
- onEventClick?.(event)}
- _hover={{ bg: "rgba(255, 255, 255, 0.06)" }}
- borderRadius="md"
- transition="all 0.15s"
- bg={isSelected ? "rgba(66, 153, 225, 0.15)" : "transparent"}
- py={2}
- px={1}
+ bg={isSelected ? "rgba(66, 153, 225, 0.15)" : getChangeBgColor(bgValue)}
+ borderWidth="1px"
+ borderColor={isSelected ? "#4299e1" : COLORS.cardBorderColor}
+ borderRadius="lg"
+ p={3}
+ mb={2}
+ _hover={{
+ bg: isSelected ? "rgba(66, 153, 225, 0.2)" : "rgba(255, 255, 255, 0.06)",
+ borderColor: isSelected ? "#63b3ed" : "#5a6070",
+ transform: "translateY(-1px)",
+ }}
+ transition="all 0.2s ease"
>
- {/* 左侧时间 */}
+ {/* 第一行:时间 */}
{formatEventTime(event.created_at || event.event_time)}
- {/* 右侧内容 */}
-
-
- {event.title}
-
-
+ {/* 第二行:标题 */}
+
+ {event.title}
+
- {/* 涨跌幅 */}
- {hasChange && (
-
- {isPositive ? "+" : ""}
- {changePct.toFixed(1)}%
-
+ {/* 第三行:涨跌幅指标 */}
+ {(hasMaxChange || hasAvgChange) && (
+
+ {/* 最大超额 */}
+ {hasMaxChange && (
+ 0 ? "rgba(239, 68, 68, 0.15)" : "rgba(16, 185, 129, 0.15)"}
+ borderWidth="1px"
+ borderColor={maxChange > 0 ? "rgba(239, 68, 68, 0.3)" : "rgba(16, 185, 129, 0.3)"}
+ borderRadius="md"
+ px={2}
+ py={1}
+ >
+
+ 最大超额
+
+ 0 ? "#fc8181" : "#68d391"}
+ >
+ {maxChange > 0 ? "+" : ""}{maxChange.toFixed(2)}%
+
+
+ )}
+
+ {/* 平均超额 */}
+ {hasAvgChange && (
+ 0 ? "rgba(239, 68, 68, 0.15)" : "rgba(16, 185, 129, 0.15)"}
+ borderWidth="1px"
+ borderColor={avgChange > 0 ? "rgba(239, 68, 68, 0.3)" : "rgba(16, 185, 129, 0.3)"}
+ borderRadius="md"
+ px={2}
+ py={1}
+ >
+
+ 平均超额
+
+ 0 ? "#fc8181" : "#68d391"}
+ >
+ {avgChange > 0 ? "+" : ""}{avgChange.toFixed(2)}%
+
+
+ )}
+
+ {/* 超预期得分 */}
+ {event.expectation_surprise_score != null && (
+ = 60 ? "rgba(239, 68, 68, 0.15)" :
+ event.expectation_surprise_score >= 40 ? "rgba(237, 137, 54, 0.15)" : "rgba(66, 153, 225, 0.15)"}
+ borderWidth="1px"
+ borderColor={event.expectation_surprise_score >= 60 ? "rgba(239, 68, 68, 0.3)" :
+ event.expectation_surprise_score >= 40 ? "rgba(237, 137, 54, 0.3)" : "rgba(66, 153, 225, 0.3)"}
+ borderRadius="md"
+ px={2}
+ py={1}
+ >
+
+ 超预期
+
+ = 60 ? "#fc8181" :
+ event.expectation_surprise_score >= 40 ? "#ed8936" : "#63b3ed"}
+ >
+ {Math.round(event.expectation_surprise_score)}分
+
+
+ )}
+
)}
-
+
);
});
@@ -159,25 +245,23 @@ const MainlineCard = React.memo(
}
}, [isExpanded]);
- // 找出涨幅最大的事件(HOT 事件)
+ // 找出最大超额涨幅最高的事件(HOT 事件)
const hotEvent = useMemo(() => {
if (!mainline.events || mainline.events.length === 0) return null;
let maxChange = -Infinity;
let hot = null;
mainline.events.forEach((event) => {
- // 使用后端返回的字段:related_max_chg(最大涨幅)
- const change = event.related_max_chg ?? event.related_avg_chg ?? event.max_change_pct ?? event.change_pct ?? -Infinity;
+ // 统一使用 related_max_chg(最大超额)
+ const change = event.related_max_chg ?? -Infinity;
if (change > maxChange) {
maxChange = change;
hot = event;
}
});
- // 只有当有正涨幅时才显示 HOT
+ // 只有当最大超额 > 0 时才显示 HOT
return maxChange > 0 ? hot : null;
}, [mainline.events]);
- const hotEventId = hotEvent?.id;
-
// 当前显示的事件
const displayedEvents = useMemo(() => {
return mainline.events.slice(0, displayCount);
@@ -292,8 +376,8 @@ const MainlineCard = React.memo(
{hotEvent && (
-
- {/* HOT 标签 */}
+ {/* 第一行:HOT 标签 + 最大超额 */}
+
HOT
- {/* HOT 事件标题 */}
-
- {hotEvent.title}
-
- {/* HOT 事件涨幅 */}
- {(hotEvent.related_max_chg ?? hotEvent.related_avg_chg) != null && (
-
- +{(hotEvent.related_max_chg ?? hotEvent.related_avg_chg).toFixed(1)}%
-
+
+ 最大超额 +{hotEvent.related_max_chg.toFixed(2)}%
+
+
)}
+ {/* 第二行:标题 */}
+
+ {hotEvent.title}
+
)}
@@ -351,7 +436,7 @@ const MainlineCard = React.memo(
{/* 事件列表区域 */}
{isExpanded ? (
- {/* 事件列表 - 带时间轴 */}
-
- {displayedEvents.map((event) => (
-
- ))}
-
+ {/* 事件列表 - 卡片式 */}
+ {displayedEvents.map((event) => (
+
+ ))}
{/* 加载更多按钮 */}
{hasMore && (
@@ -389,7 +471,7 @@ const MainlineCard = React.memo(
isLoading={isLoadingMore}
loadingText="加载中..."
w="100%"
- mt={2}
+ mt={1}
_hover={{ bg: COLORS.headerHoverBg }}
>
加载更多 ({mainline.events.length - displayCount} 条)
@@ -397,24 +479,21 @@ const MainlineCard = React.memo(
)}
) : (
- /* 折叠时显示简要信息 - 也带时间轴 */
-
-
- {mainline.events.slice(0, 4).map((event) => (
-
- ))}
- {mainline.events.length > 4 && (
-
- ... 还有 {mainline.events.length - 4} 条
-
- )}
-
+ /* 折叠时显示简要信息 - 卡片式 */
+
+ {mainline.events.slice(0, 3).map((event) => (
+
+ ))}
+ {mainline.events.length > 3 && (
+
+ ... 还有 {mainline.events.length - 3} 条
+
+ )}
)}