v1.0.175: виртуальные группы + classify_raw + stepper

This commit is contained in:
2026-06-24 17:06:25 +04:00
parent b5d35f57fc
commit 475d0b8dc7
7 changed files with 114 additions and 25 deletions
+46
View File
@@ -29,6 +29,26 @@ function syncDB() {
body: JSON.stringify({keep_ids: keepIds})
}).catch(function(){});
}
// ── Pipeline stepper ─────────────────────────────────────────
function stepDone(id) {
var el = document.getElementById(id);
if (el) { el.innerHTML = el.innerHTML.replace('○','✓'); el.style.color = 'var(--green)'; }
}
function stepActive(id) {
var el = document.getElementById(id);
if (el) { el.innerHTML = el.innerHTML.replace('○','⏳').replace('✓','⏳'); el.style.color = 'var(--brand-primary)'; }
}
function resetStepper(fromId) {
var steps = ['stepUpload','stepClassify','stepGroups','stepCompare'];
var reset = false;
steps.forEach(function(id) {
if (id === fromId) reset = true;
if (reset) {
var el = document.getElementById(id);
if (el) { el.innerHTML = el.innerHTML.replace('✓','○').replace('⏳','○'); el.style.color = 'var(--muted)'; }
}
});
}
// Утилиты (formatSize, formatDate, moveUp, moveDown, escHtml, fmtDate) —
// вынесены в app_utils.js для облегчения анализа и отладки.
@@ -67,6 +87,8 @@ fileInput.addEventListener('change', async function() {
renderTable();
// Разблокировать кнопку классификации — состав файлов изменился
showClassifyBtn();
// Сбросить прогресс пайплайна
resetStepper('stepUpload');
for (var i = 0; i < newFiles.length; i++) {
var f = newFiles[i];
@@ -186,6 +208,8 @@ fileInput.addEventListener('change', async function() {
}
await refreshSupps();
stepDone('stepUpload');
stepActive('stepClassify');
// Синхронизировать БД с таблицей — удалить всё, чего нет в fileQueue
syncDB();
@@ -220,6 +244,7 @@ async function runClassify() {
var btn = document.getElementById('classifyBtn');
btn.disabled = true;
btn.innerHTML = '<span class="spinner"></span> Классификация... 0с';
stepActive('stepClassify');
var start = Date.now();
var timer = setInterval(function() {
@@ -253,6 +278,8 @@ async function runClassify() {
clearInterval(pollTimer);
if (data.ok) {
btn.innerHTML = '✓ ' + data.done + '/' + data.total + ' классифицировано (' + Math.round((Date.now() - start) / 1000) + 'с)';
stepDone('stepClassify');
stepActive('stepGroups');
loadGroups();
} else {
btn.innerHTML = '✗ ' + (data.error || 'ошибка');
@@ -277,6 +304,7 @@ async function loadGroups() {
var realGroups = data.groups.filter(function(g) { return g.contract_number !== '__unresolved__'; });
var unresolvedGroup = data.groups.find(function(g) { return g.contract_number === '__unresolved__'; });
diffBody.innerHTML = '<div style="font-weight:600;margin-bottom:8px;">📋 Найдено групп: ' + realGroups.length + (unresolvedGroup ? ' | ⚠ ' + unresolvedGroup.documents.length + ' файлов не распознано' : '') + '</div>';
stepDone('stepGroups');
// Сохраняем группы для compare
window._groupsData = data.groups;
@@ -346,6 +374,7 @@ window.runCompareForGroup = async function(gi) {
var cid = ad.contract_ids[0];
btn.innerHTML = '<span class="spinner"></span> Сравнение...';
stepActive('stepCompare');
var diffCard = document.getElementById('diffCard');
var diffBody = document.getElementById('diffBody');
@@ -539,6 +568,7 @@ document.getElementById('llmBtn').addEventListener('click', function() {
es.close();
diffStatus.textContent = '✓ ' + d.total_time_s + 'с';
btn.innerHTML = '<i data-lucide="check" style="width:16px;height:16px;"></i> ✓ Готово';
stepDone('stepCompare');
if (d.total_unresolved > 0) {
diffBody.innerHTML += '<div style="margin-top:8px;color:#eab308;">⚠ ' + d.total_unresolved + ' неразрешённых строк — <a href="/resolve.cfm?contract_id=' + contractId + '">разобрать</a></div>';
}
@@ -688,6 +718,22 @@ async function showText(i) {
if (f.supp_id) {
rightHtml += '<div class="kv"><span class="k">Тип ДС</span><span class="v">' + (f.supp_type || '—') + '</span></div>';
}
// ── Результаты классификации ──────────────────────────
if (qData.classify_status === 'classified' || qData.classify_raw) {
rightHtml += '<div style="margin-top:12px;font-weight:600;font-size:12px;border-top:1px solid var(--brand-gray);padding-top:8px;">🏷️ Классификация LLM</div>';
if (qData.doc_type) rightHtml += '<div class="kv"><span class="k">Тип</span><span class="v">' + qData.doc_type + '</span></div>';
if (qData.own_number) rightHtml += '<div class="kv"><span class="k">Номер</span><span class="v">' + qData.own_number + '</span></div>';
if (qData.parent_number) rightHtml += '<div class="kv"><span class="k">Родительский</span><span class="v">' + qData.parent_number + '</span></div>';
if (qData.doc_date) rightHtml += '<div class="kv"><span class="k">Дата</span><span class="v">' + qData.doc_date + '</span></div>';
if (qData.counterparty) rightHtml += '<div class="kv"><span class="k">Контрагент</span><span class="v">' + qData.counterparty + '</span></div>';
if (qData.classify_raw) {
var rawId = 'raw_' + docId.replace(/-/g,'');
rightHtml += '<details style="margin-top:8px;"><summary style="cursor:pointer;font-size:11px;color:var(--brand-primary);">Сырой ответ LLM</summary>';
rightHtml += '<pre style="font-size:10px;max-height:200px;overflow:auto;background:var(--brand-grey-light);padding:6px;border-radius:4px;margin-top:4px;">' + escHtml(qData.classify_raw) + '</pre>';
rightHtml += '</details>';
}
}
} catch(e) {
leftHtml = '<span style="color:var(--destructive);">Ошибка</span>';
}