feat: finish met nonlinear ui workflow

This commit is contained in:
Codex
2026-05-06 01:54:51 +00:00
parent 2dc46ce056
commit 17fb816d33
8 changed files with 42 additions and 16 deletions
+11 -4
View File
@@ -74,9 +74,16 @@ function fmtClock(value: Date): string {
function fmtDuration(seconds: number): string {
if (!Number.isFinite(seconds)) return "--";
if (seconds < 60) return `${seconds}s`;
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
return `${Math.floor(seconds / 3600)}h ${Math.floor((seconds % 3600) / 60)}m`;
const safeSeconds = Math.max(0, seconds);
if (safeSeconds === 0) return "0s";
if (safeSeconds < 0.01) return "<0.01s";
if (safeSeconds < 0.1) return `${safeSeconds.toFixed(2)}s`;
if (safeSeconds < 1) return `${safeSeconds.toFixed(1)}s`;
if (safeSeconds < 10 && !Number.isInteger(safeSeconds)) return `${safeSeconds.toFixed(1)}s`;
if (safeSeconds < 60) return `${Math.round(safeSeconds)}s`;
const wholeSeconds = Math.floor(safeSeconds);
if (wholeSeconds < 3600) return `${Math.floor(wholeSeconds / 60)}m ${wholeSeconds % 60}s`;
return `${Math.floor(wholeSeconds / 3600)}h ${Math.floor((wholeSeconds % 3600) / 60)}m`;
}
function fmtBytes(value: any): string {
@@ -125,7 +132,7 @@ function taskElapsedSeconds(task: any): number | null {
const terminal = ["succeeded", "failed"].includes(String(task?.status || "").toLowerCase());
const ended = terminal ? timeMs(task?.updatedAt) : Date.now();
if (ended === null) return null;
return Math.max(0, Math.floor((ended - started) / 1000));
return Math.max(0, (ended - started) / 1000);
}
function taskFailureReason(task: any): string {
+17 -1
View File
@@ -27,6 +27,7 @@ function fmtPercent(value: any): string {
}
function fmtDuration(seconds: any): string {
if (seconds === null || seconds === undefined || seconds === "") return "--";
const value = Number(seconds);
if (!Number.isFinite(value)) return "--";
if (value < 60) return `${Math.max(0, Math.round(value))}s`;
@@ -129,6 +130,21 @@ function jobDuration(job: any): string {
return job.startedAt && job.finishedAt ? fmtDuration((Date.parse(job.finishedAt) - Date.parse(job.startedAt)) / 1000) : "--";
}
function jobEtaSeconds(job: any): number | null {
const progress = job.progress || {};
if (progress.etaSeconds !== null && progress.etaSeconds !== undefined && progress.etaSeconds !== "") {
const reported = Number(progress.etaSeconds);
if (Number.isFinite(reported)) return Math.max(0, reported);
}
const currentEpoch = Number(progress.currentEpoch);
const epochTarget = Number(progress.epochTarget ?? job.epochTarget);
const startedAt = Date.parse(job.startedAt || "");
if (!Number.isFinite(currentEpoch) || currentEpoch <= 0 || !Number.isFinite(epochTarget) || epochTarget <= currentEpoch || !Number.isFinite(startedAt)) return null;
const elapsedSeconds = Math.max(0, (Date.now() - startedAt) / 1000);
if (elapsedSeconds <= 0) return null;
return Math.max(0, (elapsedSeconds / currentEpoch) * (epochTarget - currentEpoch));
}
function statusLabel(status: string): string {
if (status === "staged") return "待启动";
if (status === "queued") return "排队中";
@@ -291,7 +307,7 @@ export function MetNonlinearPage({ microservices, onRaw, apiBaseUrl = "/api" }:
h("span", null, `${progress.currentEpoch ?? "--"} / ${progress.epochTarget ?? job.epochTarget ?? "--"}`),
h("div", { className: "met-progress" }, h("span", { style: { width: fmtPercent(progress.progressPercent) } })),
),
h("td", null, job.status === "succeeded" || job.status === "failed" || job.status === "canceled" ? jobDuration(job) : fmtDuration(progress.etaSeconds)),
h("td", null, job.status === "succeeded" || job.status === "failed" || job.status === "canceled" ? jobDuration(job) : (job.status === "running" ? `ETA ${fmtDuration(jobEtaSeconds(job))}` : "--")),
h("td", null, job.gpuName || "--"),
h("td", null, job.exitCode ?? "--"),
h("td", null, fmtDate(job.updatedAt)),