From f7859a7aa77fe03b46632eed83fdea081a9fe78d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80?= <78488229+viktor138irk@users.noreply.github.com> Date: Thu, 7 May 2026 07:04:21 +0900 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=B8=D0=BD=D0=B0=D0=BC=D0=B8=D1=87?= =?UTF-8?q?=D0=B5=D1=81=D0=BA=D0=B8=D0=B9=20=D0=BF=D1=80=D0=BE=D0=B3=D1=80?= =?UTF-8?q?=D0=B5=D1=81=D1=81=20=D0=BE=D0=BF=D0=B5=D1=80=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=B8=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20VM?= =?UTF-8?q?=20=D0=B1=D1=8B=D0=BB=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/templates/operation_detail.html | 94 +++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 17 deletions(-) diff --git a/web/templates/operation_detail.html b/web/templates/operation_detail.html index f1f57f4..71b7c13 100644 --- a/web/templates/operation_detail.html +++ b/web/templates/operation_detail.html @@ -22,7 +22,7 @@
-
+

Состояние

{{ operation.status }} @@ -76,34 +76,94 @@ const messageEl = document.getElementById('operation-message'); const logEl = document.getElementById('operation-log'); + let serverProgress = Number(card.dataset.initialProgress || 0); + let visualProgress = serverProgress; + let lastServerProgress = serverProgress; + let currentStatus = statusEl.textContent.trim() || 'queued'; + let lastProgressAt = Date.now(); + function statusClass(status) { if (status === 'success') return 'status ok'; if (status === 'error') return 'status err'; return 'status warn'; } - async function refreshOperation() { - const response = await fetch(`/api/operations/${operationId}`, {cache: 'no-store'}); - if (!response.ok) return; - const payload = await response.json(); - if (!payload.ok) return; + function setProgress(value) { + const safeValue = Math.max(0, Math.min(100, Math.round(value))); + progressBar.style.width = `${safeValue}%`; + progressText.textContent = `${safeValue}%`; + } - const op = payload.operation; - statusEl.textContent = op.status; - statusEl.className = statusClass(op.status); - progressBar.style.width = `${op.progress || 0}%`; - progressText.textContent = `${op.progress || 0}%`; - messageEl.textContent = op.message || ''; - logEl.textContent = op.log_tail || ''; - logEl.scrollTop = logEl.scrollHeight; + function interpolateProgress() { + if (currentStatus === 'success') { + visualProgress = 100; + setProgress(100); + clearInterval(window.virtualitySmoothProgressTimer); + return; + } + if (currentStatus === 'error') { + visualProgress = Math.max(visualProgress, serverProgress || 100); + setProgress(visualProgress); + clearInterval(window.virtualitySmoothProgressTimer); + return; + } + if (currentStatus !== 'queued' && currentStatus !== 'running') return; - if (op.status === 'success' || op.status === 'error') { - clearInterval(window.virtualityOperationTimer); + const ceiling = currentStatus === 'queued' ? 12 : 95; + const ageSeconds = Math.max(1, Math.floor((Date.now() - lastProgressAt) / 1000)); + const baseStep = currentStatus === 'queued' ? 0.35 : 0.75; + const slowDown = visualProgress > 80 ? 0.28 : visualProgress > 60 ? 0.45 : 1; + const step = Math.min(2.5, baseStep * slowDown * ageSeconds); + + if (visualProgress < ceiling) { + visualProgress = Math.min(ceiling, Math.max(visualProgress, serverProgress) + step); + setProgress(visualProgress); } } - window.virtualityOperationTimer = setInterval(refreshOperation, 1800); + async function refreshOperation() { + try { + const response = await fetch(`/api/operations/${operationId}`, {cache: 'no-store'}); + if (!response.ok) return; + const payload = await response.json(); + if (!payload.ok) return; + + const op = payload.operation; + currentStatus = op.status || currentStatus; + serverProgress = Number(op.progress || 0); + statusEl.textContent = currentStatus; + statusEl.className = statusClass(currentStatus); + + if (serverProgress !== lastServerProgress || currentStatus === 'success' || currentStatus === 'error') { + lastProgressAt = Date.now(); + lastServerProgress = serverProgress; + } + + if (currentStatus === 'success') { + visualProgress = 100; + } else if (currentStatus === 'error') { + visualProgress = Math.max(visualProgress, serverProgress); + } else { + visualProgress = Math.max(visualProgress, serverProgress); + } + + setProgress(visualProgress); + messageEl.textContent = op.message || ''; + logEl.textContent = op.log_tail || ''; + logEl.scrollTop = logEl.scrollHeight; + + if (currentStatus === 'success' || currentStatus === 'error') { + clearInterval(window.virtualityOperationTimer); + } + } catch (error) { + messageEl.textContent = 'Ожидаем ответ операции...'; + } + } + + window.virtualityOperationTimer = setInterval(refreshOperation, 1500); + window.virtualitySmoothProgressTimer = setInterval(interpolateProgress, 1000); refreshOperation(); + interpolateProgress();