Improve AI batch progress controls
This commit is contained in:
@@ -19,6 +19,8 @@ const state = {
|
||||
aiJob: null,
|
||||
aiJobOwned: false,
|
||||
aiPolling: null,
|
||||
bulkJob: null,
|
||||
bulkPolling: null,
|
||||
currentUser: null,
|
||||
authenticated: false,
|
||||
pdfZoom: Number(localStorage.getItem("frontPagePdfZoom")) || 110,
|
||||
@@ -425,7 +427,7 @@ function renderFilterActions() {
|
||||
const aiPassedCount = Number(state.status?.ai_passed || 0);
|
||||
const shouldShow = ["review_all", "ai_passed"].includes(filter);
|
||||
button.classList.toggle("is-hidden", !shouldShow);
|
||||
button.disabled = !shouldShow || aiPassedCount <= 0;
|
||||
button.disabled = !shouldShow || aiPassedCount <= 0 || Boolean(state.bulkJob?.running);
|
||||
button.textContent = aiPassedCount > 0 ? `通过所有AI已通过(${aiPassedCount})` : "通过所有AI已通过";
|
||||
}
|
||||
|
||||
@@ -1414,22 +1416,39 @@ function setAiButtonsDisabled(disabled) {
|
||||
});
|
||||
}
|
||||
|
||||
function clearAiProgressPanel() {
|
||||
const panel = $("aiProgressPanel");
|
||||
if (!panel) return;
|
||||
panel.classList.add("is-hidden");
|
||||
$("aiProgressBar").style.width = "0%";
|
||||
$("aiProgressText").textContent = "0/0";
|
||||
$("aiProgressMeta").textContent = "等待开始";
|
||||
$("aiCancelBtn").classList.remove("is-hidden");
|
||||
$("aiCancelBtn").disabled = true;
|
||||
}
|
||||
|
||||
function renderAiProgress(job = state.aiJob) {
|
||||
const panel = $("aiProgressPanel");
|
||||
if (!panel) return;
|
||||
if (!job || (!job.running && !job.total && !job.message)) {
|
||||
panel.classList.add("is-hidden");
|
||||
if (!job || (!job.running && !job.total && !job.message) || (!job.running && job.cancel_requested)) {
|
||||
clearAiProgressPanel();
|
||||
return;
|
||||
}
|
||||
const total = Number(job.total || 0);
|
||||
const processed = Number(job.processed || 0);
|
||||
const percent = total ? Math.min(100, Math.round((processed / total) * 100)) : 0;
|
||||
const isBulkApprove = job.kind === "approve_ai_passed";
|
||||
panel.classList.remove("is-hidden");
|
||||
$("aiProgressTitle").textContent = job.running ? "AI处理中" : displayText(job.message || "AI处理完成");
|
||||
$("aiProgressTitle").textContent = isBulkApprove
|
||||
? (job.running ? "批量通过中" : displayText(job.message || "批量通过完成"))
|
||||
: (job.running ? "AI处理中" : displayText(job.message || "AI处理完成"));
|
||||
$("aiProgressText").textContent = total ? `${processed}/${total}` : "--";
|
||||
$("aiProgressBar").style.width = `${percent}%`;
|
||||
$("aiProgressMeta").textContent = `OK ${job.ok || 0} · 不OK ${job.pending || 0} · 失败 ${job.failed || 0} · 并发 ${job.concurrency || 1}`;
|
||||
$("aiCancelBtn").disabled = !job.running;
|
||||
$("aiProgressMeta").textContent = isBulkApprove
|
||||
? `已通过 ${job.updated || 0} · 剩余 ${Math.max(0, total - processed)} · 失败 ${job.failed || 0}`
|
||||
: `OK ${job.ok || 0} · 不OK ${job.pending || 0} · 失败 ${job.failed || 0} · 并发 ${job.concurrency || 1}`;
|
||||
$("aiCancelBtn").classList.toggle("is-hidden", isBulkApprove);
|
||||
$("aiCancelBtn").disabled = isBulkApprove || !job.running;
|
||||
}
|
||||
|
||||
async function cancelAiReview({ silent = false } = {}) {
|
||||
@@ -1439,6 +1458,9 @@ async function cancelAiReview({ silent = false } = {}) {
|
||||
renderAiProgress(job);
|
||||
setAiButtonsDisabled(Boolean(job.running));
|
||||
if (!silent) showMessage(displayText(job.message || "AI核验正在中断..."));
|
||||
if (job.running && !state.aiPolling) {
|
||||
state.aiPolling = setTimeout(() => pollAiReviewJob().catch((error) => showMessage(error.message, "error")), 1200);
|
||||
}
|
||||
return job;
|
||||
}
|
||||
|
||||
@@ -1448,8 +1470,8 @@ async function pollAiReviewJob() {
|
||||
renderAiProgress(job);
|
||||
const total = job.total || 0;
|
||||
const processed = job.processed || 0;
|
||||
showMessage(`AI处理中:${processed}/${total},并发 ${job.concurrency || 1},OK ${job.ok || 0},不OK ${job.pending || 0},失败 ${job.failed || 0}`, "");
|
||||
if (job.running) {
|
||||
showMessage(`AI处理中:${processed}/${total},并发 ${job.concurrency || 1},OK ${job.ok || 0},不OK ${job.pending || 0},失败 ${job.failed || 0}`, "");
|
||||
if (state.aiJobOwned && !job.cancel_requested && state.activePage !== "review") switchPage("review");
|
||||
setAiButtonsDisabled(true);
|
||||
state.aiPolling = setTimeout(() => pollAiReviewJob().catch((error) => showMessage(error.message, "error")), 2000);
|
||||
@@ -1458,6 +1480,15 @@ async function pollAiReviewJob() {
|
||||
state.aiPolling = null;
|
||||
state.aiJobOwned = false;
|
||||
setAiButtonsDisabled(false);
|
||||
if (job.cancel_requested) {
|
||||
clearAiProgressPanel();
|
||||
showMessage(displayText(job.message || "AI核验已中断"), "ok");
|
||||
await loadStatus().catch(() => null);
|
||||
await loadOverview().catch(() => null);
|
||||
await loadRecords();
|
||||
await refreshSelectedRecord();
|
||||
return;
|
||||
}
|
||||
showMessage(displayText(`${job.message || "AI处理完成"}:OK ${job.ok || 0},不OK ${job.pending || 0},失败 ${job.failed || 0}`), job.failed ? "error" : "ok");
|
||||
await loadStatus().catch(() => null);
|
||||
await loadOverview().catch(() => null);
|
||||
@@ -1502,19 +1533,37 @@ async function approveAiNoIssueRecords() {
|
||||
button.disabled = true;
|
||||
showMessage("正在批量确认历史 AI 已通过记录...");
|
||||
try {
|
||||
const data = await api("/api/ai/review/approve-no-issue", { method: "POST" });
|
||||
state.status = data.status_snapshot || state.status;
|
||||
showMessage(`已批量确认 ${data.updated || 0} 条历史 AI 已通过记录`, "ok");
|
||||
await loadStatus().catch(() => null);
|
||||
await loadOverview().catch(() => null);
|
||||
await loadRecords();
|
||||
const job = await api("/api/ai/review/approve-no-issue", { method: "POST" });
|
||||
state.bulkJob = job;
|
||||
renderAiProgress(job);
|
||||
await pollApproveAiNoIssueJob();
|
||||
} catch (error) {
|
||||
showMessage(error.message, "error");
|
||||
} finally {
|
||||
button.disabled = false;
|
||||
renderFilterActions();
|
||||
}
|
||||
}
|
||||
|
||||
async function pollApproveAiNoIssueJob() {
|
||||
const job = await api("/api/ai/review/approve-no-issue/status");
|
||||
state.bulkJob = job;
|
||||
renderAiProgress(job);
|
||||
const total = Number(job.total || 0);
|
||||
const processed = Number(job.processed || 0);
|
||||
if (job.running) {
|
||||
showMessage(`批量通过AI已通过:${processed}/${total},已更新 ${job.updated || 0} 条`, "");
|
||||
renderFilterActions();
|
||||
state.bulkPolling = setTimeout(() => pollApproveAiNoIssueJob().catch((error) => showMessage(error.message, "error")), 1200);
|
||||
return;
|
||||
}
|
||||
state.bulkPolling = null;
|
||||
showMessage(displayText(job.message || `批量通过完成,已更新 ${job.updated || 0} 条`), job.failed ? "error" : "ok");
|
||||
await loadStatus().catch(() => null);
|
||||
await loadOverview().catch(() => null);
|
||||
await loadRecords();
|
||||
renderFilterActions();
|
||||
}
|
||||
|
||||
async function submitReviewedRecords() {
|
||||
if (!window.confirm("确认提交所有已人工复核项目?提交后这些项目将不再出现在复核工作台列表中。")) return;
|
||||
const button = $("submitReviewedBtn");
|
||||
@@ -1857,9 +1906,15 @@ async function loadWorkspace() {
|
||||
state.aiJobOwned = false;
|
||||
renderAiProgress(job);
|
||||
}
|
||||
const bulkJob = await api("/api/ai/review/approve-no-issue/status").catch(() => null);
|
||||
if (bulkJob?.running) {
|
||||
state.bulkJob = bulkJob;
|
||||
renderAiProgress(bulkJob);
|
||||
}
|
||||
const page = canOpenPage(state.activePage) ? state.activePage : firstAllowedPage();
|
||||
switchPage(job?.running && state.aiJobOwned && canOpenPage("review") ? "review" : page);
|
||||
if (job?.running) pollAiReviewJob().catch((error) => showMessage(error.message, "error"));
|
||||
if (bulkJob?.running) pollApproveAiNoIssueJob().catch((error) => showMessage(error.message, "error"));
|
||||
}
|
||||
|
||||
boot();
|
||||
|
||||
Reference in New Issue
Block a user