SSE-обработка: интерактивный прогресс с таймерами, итоги по времени/байтам
This commit is contained in:
@@ -76,12 +76,11 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if unprocessed %}
|
||||
<form method="POST" action="/process/{{ contract.id }}" style="margin-top:12px;" onsubmit="startTimer()">
|
||||
<button type="submit" class="btn btn-primary" style="width:100%;justify-content:center;">
|
||||
<div id="process-area">
|
||||
<button class="btn btn-primary" onclick="startProcessing('{{ contract.id }}')" style="width:100%;justify-content:center;">
|
||||
<i data-lucide="play" style="width:16px;height:16px;"></i> Обработать (LLM)
|
||||
</button>
|
||||
<div id="timer" style="text-align:center;margin-top:8px;color:var(--muted);font-size:13px;"></div>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -118,11 +117,83 @@
|
||||
|
||||
<script>
|
||||
lucide.createIcons();
|
||||
function startTimer() {
|
||||
var t = document.getElementById('timer'), s = 0;
|
||||
if (!t) return;
|
||||
t.textContent = '⏳ Обработка LLM... 0с';
|
||||
setInterval(function(){ s++; t.textContent = '⏳ Обработка LLM... ' + s + 'с'; }, 1000);
|
||||
|
||||
function formatBytes(b) {
|
||||
if (!b) return '0 B';
|
||||
var u = ['B','KB','MB','GB'], i = 0;
|
||||
while (b >= 1024 && i < 3) { b /= 1024; i++; }
|
||||
return (Math.round(b * 10) / 10) + ' ' + u[i];
|
||||
}
|
||||
|
||||
function startProcessing(cid) {
|
||||
var area = document.getElementById('process-area');
|
||||
area.innerHTML = '<div style="padding:12px;" id="proc-list"></div>';
|
||||
var list = document.getElementById('proc-list');
|
||||
|
||||
var timers = {};
|
||||
var timerInterval = setInterval(function() {
|
||||
var now = Date.now();
|
||||
var els = list.querySelectorAll('.proc-timer');
|
||||
els.forEach(function(el) {
|
||||
var start = parseInt(el.dataset.start);
|
||||
if (start) { el.textContent = Math.round((now - start) / 1000) + 'с'; }
|
||||
});
|
||||
}, 500);
|
||||
|
||||
var es = new EventSource('/process/' + cid);
|
||||
|
||||
es.addEventListener('error', function() {
|
||||
clearInterval(timerInterval);
|
||||
es.close();
|
||||
});
|
||||
|
||||
es.onmessage = function(e) {
|
||||
var d = JSON.parse(e.data);
|
||||
|
||||
if (d.type === 'file_start') {
|
||||
var div = document.createElement('div');
|
||||
div.className = 'queue-item';
|
||||
div.id = 'f_' + d.name.replace(/[^a-zA-Z0-9]/g, '_');
|
||||
div.innerHTML = '<span class="badge badge-err">⏳</span>' +
|
||||
'<span class="name">' + d.name + '</span>' +
|
||||
'<span class="size proc-timer" data-start="' + Date.now() + '">0с</span>';
|
||||
list.appendChild(div);
|
||||
lucide.createIcons();
|
||||
}
|
||||
|
||||
else if (d.type === 'file_done') {
|
||||
var el = document.getElementById('f_' + d.name.replace(/[^a-zA-Z0-9]/g, '_'));
|
||||
if (el) {
|
||||
el.querySelector('.badge').className = 'badge badge-ok';
|
||||
el.querySelector('.badge').textContent = '✓';
|
||||
el.querySelector('.proc-timer').textContent = d.time_s + 'с · ' + d.rows + ' строк';
|
||||
}
|
||||
}
|
||||
|
||||
else if (d.type === 'file_error') {
|
||||
var el = document.getElementById('f_' + d.name.replace(/[^a-zA-Z0-9]/g, '_'));
|
||||
if (el) {
|
||||
el.querySelector('.badge').className = 'badge badge-err';
|
||||
el.querySelector('.badge').textContent = '✗';
|
||||
el.querySelector('.proc-timer').textContent = d.error;
|
||||
}
|
||||
}
|
||||
|
||||
else if (d.type === 'summary') {
|
||||
clearInterval(timerInterval);
|
||||
es.close();
|
||||
var sum = document.createElement('div');
|
||||
sum.style.cssText = 'margin-top:12px;padding:12px;border-radius:6px;background:rgba(34,197,94,.05);border:1px solid var(--green);';
|
||||
sum.innerHTML = '<strong>✓ Готово:</strong> ' + d.files_processed + ' файлов, ' +
|
||||
d.total_rows + ' строк, ' + formatBytes(d.total_bytes) + ', всего <strong>' + d.total_time_s + 'с</strong>';
|
||||
list.appendChild(sum);
|
||||
|
||||
var btnDiv = document.createElement('div');
|
||||
btnDiv.style.cssText = 'margin-top:12px;';
|
||||
btnDiv.innerHTML = '<a href="/?id=' + cid + '" class="btn btn-primary" style="width:100%;justify-content:center;">Смотреть результаты</a>';
|
||||
list.appendChild(btnDiv);
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user