Files
model-platform/frontend/app/features/schedules/utils.ts
T
2026-08-05 15:58:18 +08:00

96 lines
3.1 KiB
TypeScript

import { type Schedule, type ScheduleArtifact, type ScheduleNode } from "../../services/api";
import { EMPTY_SCHEDULE_FORM, EMPTY_NODE_FORM } from "./constants";
export function formatTime(value: string | null): string {
if (!value) return "尚未执行";
return new Intl.DateTimeFormat("zh-CN", {
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
hour12: false,
}).format(new Date(value));
}
export function shortHash(value: string): string {
return value ? `${value.slice(0, 7)}${value.slice(-5)}` : "—";
}
export function formatDuration(value: number | null): string {
if (value === null) return "—";
if (value < 1000) return `${value} ms`;
if (value < 60_000) return `${(value / 1000).toFixed(1)} s`;
return `${Math.floor(value / 60_000)}m ${Math.round((value % 60_000) / 1000)}s`;
}
export function parseObject(text: string, label: string): Record<string, unknown> {
let value: unknown;
try {
value = JSON.parse(text);
} catch {
throw new Error(`${label}必须是合法 JSON`);
}
if (!value || Array.isArray(value) || typeof value !== "object") {
throw new Error(`${label}必须是 JSON 对象`);
}
return value as Record<string, unknown>;
}
export function scheduleToForm(schedule: Schedule) {
return {
scheduleName: schedule.schedule_name,
description: schedule.description ?? "",
triggerType: schedule.trigger_type,
cronExpression: schedule.cron_expression ?? "0 9 * * *",
timezone: schedule.timezone,
enabled: schedule.enabled,
maxConcurrency: String(schedule.max_concurrency),
failurePolicy: schedule.failure_policy,
};
}
export function nodeToForm(node: ScheduleNode) {
return {
nodeName: node.node_name,
timeoutSeconds: String(node.timeout_seconds),
retryCount: String(node.retry_count),
retryIntervalSec: String(node.retry_interval_sec),
argumentsJson: JSON.stringify(node.arguments_json ?? {}, null, 2),
envRefsJson: JSON.stringify(node.env_refs_json ?? {}, null, 2),
};
}
export function artifactNodeKey(
artifact: ScheduleArtifact,
schedule: Schedule,
): string {
const ascii = artifact.script_name
.replace(/\.[^.]+$/, "")
.replace(/[^A-Za-z0-9_-]+/g, "_")
.replace(/^([^A-Za-z])/, "n_$1")
.replace(/^_+|_+$/g, "")
.slice(0, 48);
const base = ascii || `node_${artifact.versions_id.slice(-6).toLowerCase()}`;
const existing = new Set(schedule.nodes.map((item) => item.node_key));
if (!existing.has(base)) return base;
let index = 2;
while (existing.has(`${base}_${index}`)) index += 1;
return `${base}_${index}`.slice(0, 64);
}
export function edgePath(
source: ScheduleNode,
target: ScheduleNode,
): string {
const x1 = source.position_x + 218;
const y1 = source.position_y + 52;
const x2 = target.position_x;
const y2 = target.position_y + 52;
const curve = Math.max(70, Math.abs(x2 - x1) * 0.45);
return `M ${x1} ${y1} C ${x1 + curve} ${y1}, ${x2 - curve} ${y2}, ${x2} ${y2}`;
}
export function withSuppressedError(action: () => Promise<void>): void {
void action().catch(() => undefined);
}