feat: 新增任务管理页与失败任务重试
This commit is contained in:
+72
-3
@@ -78,15 +78,30 @@ function escapeHtml(value) {
|
||||
}
|
||||
|
||||
function badge(status) {
|
||||
const value = String(status).toLowerCase();
|
||||
const className =
|
||||
status === "ready" || status === "completed" || status === "ok"
|
||||
value === "ready" || value === "completed" || value === "ok"
|
||||
? "ok"
|
||||
: status === "stopped" || status === "error"
|
||||
: value === "stopped" || value === "error" || value === "failed"
|
||||
? "error"
|
||||
: "warn";
|
||||
return `<span class="badge ${className}">${escapeHtml(status)}</span>`;
|
||||
}
|
||||
|
||||
function formatTime(value) {
|
||||
if (!value) return "";
|
||||
const date = new Date(value);
|
||||
return Number.isNaN(date.getTime()) ? value : date.toLocaleString();
|
||||
}
|
||||
|
||||
function formatElapsed(value) {
|
||||
if (!value) return "";
|
||||
const seconds = Math.floor((Date.now() - new Date(value).getTime()) / 1000);
|
||||
if (Number.isNaN(seconds) || seconds < 0) return "";
|
||||
if (seconds < 60) return `${seconds} 秒`;
|
||||
return `${Math.floor(seconds / 60)} 分钟`;
|
||||
}
|
||||
|
||||
async function loadHealth() {
|
||||
try {
|
||||
await api("/health");
|
||||
@@ -235,6 +250,51 @@ async function loadWorkflows() {
|
||||
: '<tr><td colspan="5">暂无工作流</td></tr>';
|
||||
}
|
||||
|
||||
async function loadRuns() {
|
||||
const tbody = document.getElementById("runList");
|
||||
if (!tbody) return;
|
||||
const runs = await api("/api/runs");
|
||||
tbody.innerHTML = runs.length
|
||||
? runs
|
||||
.map((run) => {
|
||||
const percent = Math.round((run.progress || 0) * 100);
|
||||
const error = run.error || "";
|
||||
const statusHtml =
|
||||
run.status === "RUNNING"
|
||||
? `${badge(run.status)} <span class="muted">已运行 ${formatElapsed(run.updated_at)}</span>`
|
||||
: run.status === "QUEUED"
|
||||
? `${badge(run.status)} <span class="muted">排队中</span>`
|
||||
: badge(run.status);
|
||||
const actions =
|
||||
run.status === "FAILED"
|
||||
? `<button class="danger" data-retry-run="${escapeHtml(run.id)}">重试</button>`
|
||||
: "";
|
||||
return `
|
||||
<tr>
|
||||
<td>${escapeHtml(run.id)}</td>
|
||||
<td>${escapeHtml(run.workflow_id)}</td>
|
||||
<td>${statusHtml}</td>
|
||||
<td>${escapeHtml(run.current_node_id || "-")}</td>
|
||||
<td>${percent}%</td>
|
||||
<td>${escapeHtml(formatTime(run.created_at))}</td>
|
||||
<td title="${escapeHtml(error)}">${error ? escapeHtml(error.slice(0, 60)) : "-"}</td>
|
||||
<td>${actions}</td>
|
||||
</tr>`;
|
||||
})
|
||||
.join("")
|
||||
: '<tr><td colspan="8">暂无任务</td></tr>';
|
||||
}
|
||||
|
||||
async function retryRun(runId) {
|
||||
try {
|
||||
const result = await api(`/api/runs/${runId}/retry`, { method: "POST" });
|
||||
alert(`任务 ${result.id} 已重新排队`);
|
||||
await loadRuns();
|
||||
} catch (error) {
|
||||
alert(`重试失败:${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function createWorkflow() {
|
||||
const workflowId = document.getElementById("workflowId").value.trim();
|
||||
const name = document.getElementById("workflowName").value.trim();
|
||||
@@ -306,7 +366,7 @@ async function uploadVideo() {
|
||||
}
|
||||
|
||||
async function pollRun(runId, progress, downloads) {
|
||||
for (let i = 0; i < 600; i += 1) {
|
||||
for (let i = 0; i < 3600; i += 1) {
|
||||
const run = await api(`/api/runs/${runId}`);
|
||||
const percent = Math.round((run.progress || 0) * 100);
|
||||
progress.textContent = `状态:${run.status} | 当前节点:${run.current_node_id || "-"} | 进度:${percent}%`;
|
||||
@@ -354,6 +414,11 @@ document.addEventListener("click", (event) => {
|
||||
const publishWorkflowButton = event.target.closest("[data-publish-workflow]");
|
||||
if (publishWorkflowButton) {
|
||||
publishWorkflow(publishWorkflowButton.dataset.publishWorkflow);
|
||||
return;
|
||||
}
|
||||
const retryButton = event.target.closest("[data-retry-run]");
|
||||
if (retryButton) {
|
||||
retryRun(retryButton.dataset.retryRun);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -406,4 +471,8 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
if (document.getElementById("workflowList")) {
|
||||
await loadWorkflows();
|
||||
}
|
||||
if (document.getElementById("runList")) {
|
||||
await loadRuns();
|
||||
setInterval(loadRuns, 5000);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user