// runner.js — 新建评估视图:列出场景、触发评估、轮询任务状态与日志。 const Runner = { selectedScenario: null, pollTimer: null, // 绑定运行按钮。 init() { document.getElementById("run-btn").addEventListener("click", () => Runner.trigger()); document.getElementById("view-report-btn").addEventListener("click", () => { if (Runner.lastRunId) { App.currentRunId = Runner.lastRunId; App.enableReportNav(); App.switchView("report"); } }); }, // 加载并渲染可触发的场景列表。 async loadScenarios() { const list = document.getElementById("scenario-list"); list.innerHTML = '

加载中…

'; try { const data = await API.scenarios(); const scenarios = data.scenarios || []; if (scenarios.length === 0) { list.innerHTML = '

未在 scenarios/ 下找到场景文件。

'; return; } list.innerHTML = ""; scenarios.forEach((sc) => list.appendChild(Runner.renderScenarioItem(sc))); } catch (err) { list.innerHTML = `

加载失败:${App.escape(err.message)}

`; } }, // 构造单个场景条目。 renderScenarioItem(sc) { const item = document.createElement("div"); const invalid = !!sc.error; item.className = "scenario-item" + (invalid ? " invalid" : ""); const modeTag = sc.mode ? `${App.escape(sc.mode)}` : ""; const metricCount = (sc.metrics || []).length; item.innerHTML = `
${App.escape(sc.scenario_name || sc.path)}
${App.escape(sc.path)}
${sc.error ? `
${App.escape(sc.error)}
` : ""}
${modeTag} ${metricCount} 指标
`; if (!invalid) { item.addEventListener("click", () => { document.querySelectorAll(".scenario-item").forEach((el) => el.classList.remove("selected")); item.classList.add("selected"); Runner.selectedScenario = sc.path; document.getElementById("selected-scenario").textContent = sc.path; document.getElementById("run-btn").disabled = false; }); } return item; }, // 触发评估并开始轮询。 async trigger() { if (!Runner.selectedScenario) return; const runBtn = document.getElementById("run-btn"); runBtn.disabled = true; const panel = document.getElementById("task-panel"); const logBox = document.getElementById("task-log"); const statusBadge = document.getElementById("task-status"); const reportBtn = document.getElementById("view-report-btn"); panel.hidden = false; reportBtn.hidden = true; logBox.textContent = ""; Runner._setStatus(statusBadge, "queued"); try { const resp = await API.triggerEvaluation(Runner.selectedScenario); Runner.poll(resp.task_id); } catch (err) { Runner._setStatus(statusBadge, "failed"); logBox.textContent = `触发失败:${err.message}`; runBtn.disabled = false; } }, // 周期性轮询任务状态,刷新日志与徽标。 poll(taskId) { const logBox = document.getElementById("task-log"); const statusBadge = document.getElementById("task-status"); const reportBtn = document.getElementById("view-report-btn"); const runBtn = document.getElementById("run-btn"); if (Runner.pollTimer) clearInterval(Runner.pollTimer); Runner.pollTimer = setInterval(async () => { try { const status = await API.taskStatus(taskId); logBox.textContent = (status.logs || []).join("\n"); logBox.scrollTop = logBox.scrollHeight; Runner._setStatus(statusBadge, status.status); if (status.status === "completed" || status.status === "failed") { clearInterval(Runner.pollTimer); runBtn.disabled = false; if (status.status === "completed" && status.run_id) { Runner.lastRunId = status.run_id; reportBtn.hidden = false; } } } catch (err) { clearInterval(Runner.pollTimer); logBox.textContent += `\n轮询失败:${err.message}`; runBtn.disabled = false; } }, 1200); }, // 更新状态徽标的文本与配色类。 _setStatus(badge, status) { badge.textContent = status; badge.className = "badge " + status; }, };