From 14cd626b0d006c44fe06249afe6bcdcc4d4f0321 Mon Sep 17 00:00:00 2001 From: wuxu Date: Fri, 19 Jun 2026 18:01:20 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E8=BF=87=E6=BB=A4=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webgui/web/assets/js/state.js | 6 +++ webgui/web/assets/js/views/jobs.js | 68 ++++++++++++++++++++---------- 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/webgui/web/assets/js/state.js b/webgui/web/assets/js/state.js index 26aaa86..084022b 100644 --- a/webgui/web/assets/js/state.js +++ b/webgui/web/assets/js/state.js @@ -80,6 +80,12 @@ export function getJobMeta(jobid) { return state.jobMeta.get(Number(jobid)); } +export function listJobMeta() { + return Array.from(state.jobMeta.entries()) + .map(([jobid, meta]) => ({ jobid: Number(jobid), ...meta })) + .sort((a, b) => b.jobid - a.jobid); +} + export function forgetJob(jobid) { state.jobMeta.delete(Number(jobid)); saveJobMeta(); diff --git a/webgui/web/assets/js/views/jobs.js b/webgui/web/assets/js/views/jobs.js index f4423b0..487228a 100644 --- a/webgui/web/assets/js/views/jobs.js +++ b/webgui/web/assets/js/views/jobs.js @@ -7,6 +7,8 @@ import { toast, rememberJob, getJobMeta, + listJobMeta, + forgetJob, formatBytes, formatSpeed, formatDuration, @@ -14,6 +16,7 @@ import { import { t } from "../i18n.js"; let pollTimer = null; +const WEBGUI_JOB_GROUP_PREFIX = "webgui/transfer"; export async function renderJobs() { const app = document.getElementById("app"); @@ -30,8 +33,12 @@ export async function renderJobs() { `; - await refreshJobs(); - startPolling(); + const hasRunning = await refreshJobs(); + if (hasRunning) { + startPolling(); + } else { + stopPolling(); + } } export async function renderNewJob() { @@ -114,7 +121,11 @@ export async function renderNewJob() { const src = srcPath ? `${srcRemote}${srcPath}` : srcRemote; const dst = dstPath ? `${dstRemote}${dstPath}` : dstRemote; try { - const body = { srcFs: src, dstFs: dst }; + const body = { + srcFs: src, + dstFs: dst, + _group: `${WEBGUI_JOB_GROUP_PREFIX}/${action}`, + }; if (action === "move") body.deleteEmptySrcDirs = true; const res = await postAsync(`sync/${action}`, body); const jobid = res && res.jobid; @@ -131,18 +142,10 @@ export async function renderNewJob() { async function refreshJobs() { const card = document.getElementById("jobs-card"); - if (!card) return; + if (!card) return false; - let jobIds = []; - try { - const list = await post("job/list"); - const running = (list && list.jobids) || []; - const finished = (list && list.finishedIds) || []; - jobIds = [...new Set([...running, ...finished])]; - } catch (e) { - card.innerHTML = `

${t("error.couldnt_load_jobs")}

${escapeHtml(e.message)}

`; - return; - } + const trackedJobs = listJobMeta(); + const jobIds = trackedJobs.map((job) => job.jobid); if (jobIds.length === 0) { card.innerHTML = ` @@ -151,23 +154,41 @@ async function refreshJobs() {

${t("jobs.empty_body").replace("New Job", `${t("jobs.new_btn")}`)}

`; - return; + return false; } const statuses = await Promise.all( jobIds.map((id) => - post("job/status", { jobid: id }).catch((e) => ({ - jobid: id, - error: e.message, - finished: true, - })), + post("job/status", { jobid: id }).catch((e) => { + if (e.message.includes("job not found")) { + forgetJob(id); + return null; + } + return { + jobid: id, + error: e.message, + finished: true, + }; + }), ), ); - card.innerHTML = renderJobTable(statuses); + const tracked = statuses.filter(Boolean); + if (tracked.length === 0) { + card.innerHTML = ` +
+

${t("jobs.empty_title")}

+

${t("jobs.empty_body").replace("New Job", `${t("jobs.new_btn")}`)}

+
+ `; + return false; + } + + card.innerHTML = renderJobTable(tracked); card.querySelectorAll("[data-stop]").forEach((btn) => { btn.addEventListener("click", () => onStop(parseInt(btn.dataset.stop, 10))); }); + return tracked.some((status) => !status.finished); } function renderJobTable(statuses) { @@ -275,7 +296,10 @@ function startPolling() { stopPolling(); return; } - await refreshJobs(); + const hasRunning = await refreshJobs(); + if (!hasRunning) { + stopPolling(); + } }, 1500); }