fix(timeline): prevent negative initial time

This commit is contained in:
2026-07-16 11:31:51 +08:00
parent f5e7312e3b
commit 041b4ef89d
4 changed files with 83 additions and 24 deletions
+25 -22
View File
@@ -30,6 +30,11 @@ import { useData } from "../MapComponent";
import { config, NETWORK_NAME } from "@/config/config";
import { apiFetch } from "@/lib/apiFetch";
import { useMap } from "../MapComponent";
import {
formatTimelineTime,
getRoundedCurrentTimelineMinutes,
normalizeTimelineMinutes,
} from "./timelineTime";
interface TimelineProps {
schemeDate?: Date;
@@ -99,6 +104,11 @@ const Timeline: React.FC<TimelineProps> = ({
const maxTime = timeRange
? timeRange.end.getHours() * 60 + timeRange.end.getMinutes()
: 1440;
const timelineCurrentTime = normalizeTimelineMinutes(
currentTime,
minTime,
maxTime,
);
useEffect(() => {
if (schemeDate) {
setSelectedDate(schemeDate);
@@ -327,20 +337,17 @@ const Timeline: React.FC<TimelineProps> = ({
// 格式化时间显示
function formatTime(minutes: number): string {
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
return `${hours.toString().padStart(2, "0")}:${mins
.toString()
.padStart(2, "0")}`;
return formatTimelineTime(minutes, minTime, maxTime);
}
function currentTimeToDate(selectedDate: Date, minutes: number): Date {
const currentTimeToDate = useCallback((selectedDate: Date, minutes: number): Date => {
const date = new Date(selectedDate);
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
const normalizedMinutes = normalizeTimelineMinutes(minutes, minTime, maxTime);
const hours = Math.floor(normalizedMinutes / 60);
const mins = normalizedMinutes % 60;
date.setHours(hours, mins, 0, 0);
return date;
}
}, [maxTime, minTime]);
// 播放时间间隔选项
const intervalOptions = [
@@ -414,9 +421,7 @@ const Timeline: React.FC<TimelineProps> = ({
const handleStop = useCallback(() => {
setIsPlaying(false);
// 设置为当前时间
const currentTime = new Date();
const minutes = currentTime.getHours() * 60 + currentTime.getMinutes();
setCurrentTime(minutes); // 组件卸载时重置时间
setCurrentTime(getRoundedCurrentTimelineMinutes()); // 重置为当前时间
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
@@ -514,7 +519,7 @@ const Timeline: React.FC<TimelineProps> = ({
// return;
// }
fetchFrameData(
currentTimeToDate(selectedDate, currentTime),
currentTimeToDate(selectedDate, timelineCurrentTime),
junctionText,
pipeText,
schemeName,
@@ -526,6 +531,8 @@ const Timeline: React.FC<TimelineProps> = ({
junctionText,
pipeText,
currentTime,
currentTimeToDate,
timelineCurrentTime,
selectedDate,
schemeName,
schemeType,
@@ -534,11 +541,7 @@ const Timeline: React.FC<TimelineProps> = ({
// 组件卸载时清理定时器和防抖
useEffect(() => {
// 设置为当前时间
const currentTime = new Date();
const minutes = currentTime.getHours() * 60 + currentTime.getMinutes();
// 找到最近的前15分钟刻度
const roundedMinutes = Math.floor(minutes / 15) * 15;
setCurrentTime(roundedMinutes); // 组件卸载时重置时间
setCurrentTime(getRoundedCurrentTimelineMinutes()); // 初始化为当前时间
return () => {
if (intervalRef.current) {
@@ -603,7 +606,7 @@ const Timeline: React.FC<TimelineProps> = ({
clearCache(linkCacheRef);
// 重新获取当前时刻的新数据
fetchFrameData(
currentTimeToDate(selectedDate, currentTime),
currentTimeToDate(selectedDate, timelineCurrentTime),
junctionText,
pipeText,
schemeName,
@@ -622,7 +625,7 @@ const Timeline: React.FC<TimelineProps> = ({
// 提前提取日期和时间值,避免异步操作期间被时间轴拖动改变
const calculationDate = selectedDate;
const calculationTime = currentTime;
const calculationTime = timelineCurrentTime;
const calculationDateTime = currentTimeToDate(
calculationDate,
calculationTime
@@ -890,13 +893,13 @@ const Timeline: React.FC<TimelineProps> = ({
color: "primary.main",
}}
>
{formatTime(currentTime)}
{formatTime(timelineCurrentTime)}
</Typography>
</Stack>
<Box ref={timelineRef} sx={{ px: 2, position: "relative" }}>
<Slider
value={currentTime}
value={timelineCurrentTime}
min={0}
max={1440} // 24:00 = 1440分钟
step={15} // 每15分钟一个步进
@@ -0,0 +1,21 @@
import {
formatTimelineTime,
getRoundedCurrentTimelineMinutes,
normalizeTimelineMinutes,
} from "./timelineTime";
describe("timelineTime", () => {
it("normalizes invalid minutes before formatting", () => {
expect(formatTimelineTime(-1)).toBe("00:00");
expect(formatTimelineTime(Number.NaN)).toBe("00:00");
});
it("clamps minutes to the configured range", () => {
expect(normalizeTimelineMinutes(-1, 60, 120)).toBe(60);
expect(normalizeTimelineMinutes(180, 60, 120)).toBe(120);
});
it("rounds the current time down to the timeline step", () => {
expect(getRoundedCurrentTimelineMinutes(new Date("2026-07-16T10:29:00"))).toBe(615);
});
});
@@ -0,0 +1,32 @@
export const TIMELINE_MINUTES_PER_DAY = 1440;
export const TIMELINE_STEP_MINUTES = 15;
export const normalizeTimelineMinutes = (
minutes: number | undefined,
minTime = 0,
maxTime = TIMELINE_MINUTES_PER_DAY,
) => {
if (typeof minutes !== "number" || !Number.isFinite(minutes)) {
return minTime;
}
return Math.min(Math.max(minutes, minTime), maxTime);
};
export const getRoundedCurrentTimelineMinutes = (date = new Date()) => {
const minutes = date.getHours() * 60 + date.getMinutes();
return Math.floor(minutes / TIMELINE_STEP_MINUTES) * TIMELINE_STEP_MINUTES;
};
export const formatTimelineTime = (
minutes: number | undefined,
minTime = 0,
maxTime = TIMELINE_MINUTES_PER_DAY,
) => {
const normalizedMinutes = normalizeTimelineMinutes(minutes, minTime, maxTime);
const hours = Math.floor(normalizedMinutes / 60);
const mins = normalizedMinutes % 60;
return `${hours.toString().padStart(2, "0")}:${mins
.toString()
.padStart(2, "0")}`;
};
+5 -2
View File
@@ -32,6 +32,7 @@ import {
markMapResourcePersistent,
} from "./mapLifecycle";
import { createOperationalMapResources } from "./operationalLayers";
import { getRoundedCurrentTimelineMinutes } from "./Controls/timelineTime";
interface MapComponentProps {
children?: React.ReactNode;
@@ -155,7 +156,9 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
const [compareMap, setCompareMap] = useState<OlMap>();
const [compareDeckLayer, setCompareDeckLayer] = useState<DeckLayer>();
// currentCalData 用于存储当前计算结果
const [currentTime, setCurrentTime] = useState<number>(-1); // 默认选择当前时间
const [currentTime, setCurrentTime] = useState<number>(
getRoundedCurrentTimelineMinutes,
); // 默认选择当前时间
// const [selectedDate, setSelectedDate] = useState<Date>(new Date("2025-9-17"));
const [selectedDate, setSelectedDate] = useState<Date>(new Date()); // 默认今天
const [schemeName, setSchemeName] = useState<string>(""); // 当前方案名称
@@ -775,7 +778,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
}, [compareMap, isCompareMode, map]);
useEffect(() => {
setCurrentTime(-1);
setCurrentTime(getRoundedCurrentTimelineMinutes());
setSelectedDate(new Date());
setSchemeName("");
setCurrentJunctionCalData([]);