Fix detail modal: live progress + logs from DB
Docker Build and Push / check-secrets (push) Failing after 4s
Docker Build and Push / build-and-push (cpu, latest) (push) Has been skipped
Docker Build and Push / build-and-push (cuda, 11.8) (push) Has been skipped
Docker Build and Push / build-and-push (cuda, 12.6) (push) Has been skipped
Docker Build and Push / build-and-push (directml, latest) (push) Has been skipped
Docker Build and Push / build-and-push (cuda, 12.8) (push) Has been skipped
Build Windows CPU / build (push) Has been cancelled
Build Windows CUDA 11.8 / build (push) Has been cancelled
Build Windows CUDA 12.6 / build (push) Has been cancelled
Build Windows CUDA 12.8 / build (push) Has been cancelled
Build Windows DirectML / build (push) Has been cancelled

- Track S.detailTaskId; on task_update via WebSocket, re-render detail
  content if that task's modal is open (progress bars now update live)
- Detail logs: async load from DB via /api/tasks/{id}/logs, then append
  real-time WebSocket logs on top (de-duplicated)
- Remove log button from task list (logs now in detail modal)
- Clear S.detailTaskId on modal close

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
liuchen
2026-06-27 15:12:56 +08:00
co-authored by Claude Haiku 4.5
parent 27b4a2c1bf
commit ea1784a10f
+55 -5
View File
@@ -884,6 +884,10 @@ function connectWS() {
} else if (msg.type === 'task_update') {
S.tasks[msg.task.id] = { ...S.tasks[msg.task.id], ...msg.task };
renderTasks(); updateMetrics();
// 详情弹窗打开时同步刷新
if (S.detailTaskId === msg.task.id && document.getElementById('modal-detail').classList.contains('open')) {
renderDetail(msg.task.id);
}
} else if (msg.type === 'log') {
appendLog(msg.task_id, msg.message);
}
@@ -944,7 +948,6 @@ function renderTasks() {
${t.status==='done' ? `
<button class="button secondary" style="min-height:28px;padding:0 10px;font-size:12px" onclick="event.stopPropagation();playVideo('${t.id}')">▶ 播放</button>
` : ''}
<button class="button secondary" style="min-height:28px;padding:0 10px;font-size:12px" onclick="event.stopPropagation();openLogs('${t.id}')">日志</button>
<button class="button secondary" style="min-height:28px;padding:0 10px;font-size:12px;color:var(--bad)" onclick="event.stopPropagation();deleteTask('${t.id}')">✕</button>
</div>
</td>
@@ -999,6 +1002,12 @@ function progressCell(t) {
}
function openDetail(id) {
S.detailTaskId = id;
renderDetail(id);
openModal('modal-detail');
}
function renderDetail(id) {
const t = S.tasks[id];
if (!t) return;
const { cls, label } = taskStatus(t);
@@ -1062,14 +1071,39 @@ function openDetail(id) {
</div>
<div>
<h3 style="font-size:11px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);margin-bottom:8px">日志</h3>
<div style="
<div id="detailLogs" style="
background:#f7f9fc;border:1px solid var(--line);border-radius:var(--radius-sm);
padding:12px 14px;max-height:240px;overflow-y:auto;
font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;line-height:1.7;
">${(t.logs||[]).map(l => `<div>${esc(l)}</div>`).join('') || '<div style="color:var(--muted)">暂无日志(点「日志」按钮从数据库加载完整记录)</div>'}</div>
"><div style="color:var(--muted)">加载中…</div></div>
</div>
`;
openModal('modal-detail');
// 异步从 DB 加载日志
loadDetailLogs(id);
}
async function loadDetailLogs(taskId) {
const el = document.getElementById('detailLogs');
if (!el) return;
try {
const d = await api(`/api/tasks/${taskId}/logs`);
// 合并 DB 历史 + 内存实时日志(去重)
const dbMsgs = d.logs?.map(l => l.message) || [];
const memMsgs = (S.logs[taskId] || []).map(l => l.message);
const extra = memMsgs.filter(m => !dbMsgs.includes(m));
const all = [...dbMsgs, ...extra];
if (!all.length) {
el.innerHTML = '<div style="color:var(--muted)">暂无日志</div>';
} else {
el.innerHTML = all.map(m => {
const isErr = /error|failed|exception/i.test(m);
return `<div style="${isErr ? 'color:var(--bad)' : ''}">${esc(m)}</div>`;
}).join('');
el.scrollTop = el.scrollHeight;
}
} catch(e) {
if (el) el.innerHTML = '<div style="color:var(--muted)">日志加载失败</div>';
}
}
// ── Submit ─────────────────────────────────────────────────────
@@ -1315,10 +1349,23 @@ function stopPlayer() {
function appendLog(taskId, message) {
if (!S.logs[taskId]) S.logs[taskId] = [];
S.logs[taskId].push({ ts: new Date(), message });
// 如果日志 Modal 当前打开且匹配该任务,实时追加
// 日志 Modal 实时追加
if (S.logsTaskId === taskId) {
appendModalLogLine({ ts: new Date(), message });
}
// 详情 Modal 实时追加
if (S.detailTaskId === taskId && document.getElementById('modal-detail').classList.contains('open')) {
const el = document.getElementById('detailLogs');
if (el) {
const isErr = /error|failed|exception/i.test(message);
const div = document.createElement('div');
if (isErr) div.style.color = 'var(--bad)';
div.textContent = message;
if (el.querySelector('[style*="加载中"], [style*="暂无"]')) el.innerHTML = '';
el.appendChild(div);
el.scrollTop = el.scrollHeight;
}
}
}
function appendModalLogLine(entry) {
@@ -1379,6 +1426,9 @@ function closeModal(id) {
if (id === 'modal-new') {
document.getElementById('modalAlert').style.display = 'none';
}
if (id === 'modal-detail') {
S.detailTaskId = null;
}
}
function showModalAlert(type, msg) {
const el = document.getElementById('modalAlert');