feat(workbench): expand SCADA monitoring and investigation presentation

This commit is contained in:
2026-11-07 19:50:14 +08:00
parent 0801e161ec
commit ba71e2d529
133 changed files with 6754 additions and 1438 deletions
+43
View File
@@ -0,0 +1,43 @@
import { formatDateTime, formatTime } from "@/shared/utils/date-time";
const zonedTime = /(?<![\w/+:.-])(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?)(Z|[+-]\d{2}:\d{2})(?![\w:+/-])/g;
const utcMarker = String.raw`(?:Z|(?:[((]\s*)?UTC(?:\s*\+?0(?::00)?)?(?:\s*[))])?)(?![\w:+/-])`;
const utcRange = new RegExp(String.raw`(?<![\w/+:.-])(?:(\d{4}-\d{2}-\d{2})[T ])?(\d{2}:\d{2})\s*[–—~至-]\s*(\d{2}:\d{2})\s*${utcMarker}`, "g");
const utcPoint = new RegExp(String.raw`(?<![\w/+:.-])(?:(\d{4}-\d{2}-\d{2})[T ])?(\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?)\s*${utcMarker}`, "g");
function parseTime(day: string, clock: string, zone = "Z"): number | null {
const date = new Date(`${day}T00:00:00Z`);
const [hours, minutes, seconds = "0"] = clock.split(":");
if (!Number.isFinite(date.getTime()) || date.toISOString().slice(0, 10) !== day
|| Number(hours) > 23 || Number(minutes) > 59 || Number(seconds) >= 60) return null;
const value = Date.parse(`${day}T${clock}${zone}`);
return Number.isFinite(value) ? value : null;
}
/** Display historical report prose without changing stored facts, IDs, URLs or code. */
export function formatDisplayText(text: string | null | undefined): string {
if (!text) return text ?? "";
return text.split(/(https?:\/\/\S+|tjwater:\/\/\S+|`[^`]*`)/g).map((part, index) => {
if (index % 2) return part;
return part.replace(utcRange, (original, day: string | undefined, start: string, end: string) => {
const startAt = parseTime(day ?? "2000-01-01", start);
let endAt = parseTime(day ?? "2000-01-01", end);
if (startAt === null || endAt === null) return original;
if (endAt < startAt) endAt += 24 * 60 * 60_000;
const dateOptions = { year: "numeric", month: "numeric", day: "numeric" } as const;
const crossesDay = formatDateTime(startAt, dateOptions) !== formatDateTime(endAt, dateOptions);
const startLabel = day ? formatDateTime(startAt, { ...dateOptions, hour: "2-digit", minute: "2-digit" }) : formatTime(startAt);
const endLabel = crossesDay && day
? formatDateTime(endAt, { ...dateOptions, hour: "2-digit", minute: "2-digit" })
: `${crossesDay ? "次日 " : ""}${formatTime(endAt)}`;
return `${startLabel}–${endLabel}(UTC+8)`;
}).replace(zonedTime, (original, day: string, clock: string, zone: string) => {
const value = parseTime(day, clock, zone);
return value === null ? original : `${formatDateTime(value)}(UTC+8)`;
}).replace(utcPoint, (original, day: string | undefined, clock: string) => {
const value = parseTime(day ?? "2000-01-01", clock);
if (value === null) return original;
return `${day ? formatDateTime(value) : formatTime(value)}(UTC+8)`;
});
}).join("");
}