v1.0.109: ↕ arrows replace Базовый radio, order param in SSE, setInitial removed
This commit is contained in:
@@ -119,6 +119,12 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
f"JOIN documents d ON s.document_id=d.id "
|
f"JOIN documents d ON s.document_id=d.id "
|
||||||
f"WHERE s.contract_id='{cid}' AND d.elements_json IS NOT NULL ORDER BY s.created_at"
|
f"WHERE s.contract_id='{cid}' AND d.elements_json IS NOT NULL ORDER BY s.created_at"
|
||||||
)
|
)
|
||||||
|
# Reorder if client specified &order=...
|
||||||
|
order_ids = (params.get("order", [None])[0] or "").strip()
|
||||||
|
if order_ids:
|
||||||
|
order_list = [x.strip() for x in order_ids.split(",") if x.strip()]
|
||||||
|
order_map = {oid: i for i, oid in enumerate(order_list)}
|
||||||
|
supps.sort(key=lambda s: order_map.get(s["id"], 999999))
|
||||||
if not supps:
|
if not supps:
|
||||||
self._sse({"type": "error", "message": "Нет распарсенных файлов"})
|
self._sse({"type": "error", "message": "Нет распарсенных файлов"})
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -39,6 +39,10 @@
|
|||||||
.info-btn { cursor: pointer; color: var(--muted); background: none; border: none; padding: 2px 4px; font-size: 14px; display: none; }
|
.info-btn { cursor: pointer; color: var(--muted); background: none; border: none; padding: 2px 4px; font-size: 14px; display: none; }
|
||||||
.info-btn:hover { color: var(--brand-primary); }
|
.info-btn:hover { color: var(--brand-primary); }
|
||||||
.info-btn.visible { display: inline; }
|
.info-btn.visible { display: inline; }
|
||||||
|
.first-row td { background: rgba(37,99,235,.06); font-weight: 600; }
|
||||||
|
.arrow-btn { cursor: pointer; color: var(--muted); background: none; border: none; padding: 2px; font-size: 12px; line-height: 1; }
|
||||||
|
.arrow-btn:hover { color: var(--brand-primary); }
|
||||||
|
.arrow-btn:disabled { opacity: .3; cursor: default; color: var(--muted); }
|
||||||
.modal-overlay { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,.4); z-index: 100; justify-content: center; align-items: center; }
|
.modal-overlay { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,.4); z-index: 100; justify-content: center; align-items: center; }
|
||||||
.modal-overlay.open { display: flex; }
|
.modal-overlay.open { display: flex; }
|
||||||
.modal { background: var(--background); border-radius: 12px; border: 1px solid var(--brand-gray); box-shadow: 0 4px 24px rgba(0,0,0,.15); max-width: 520px; width: 90%; max-height: 80vh; display: flex; flex-direction: column; }
|
.modal { background: var(--background); border-radius: 12px; border: 1px solid var(--brand-gray); box-shadow: 0 4px 24px rgba(0,0,0,.15); max-width: 520px; width: 90%; max-height: 80vh; display: flex; flex-direction: column; }
|
||||||
@@ -71,7 +75,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="topbar">
|
<div class="topbar">
|
||||||
<img src="/nubes-logo.svg" alt="Nubes">
|
<img src="/nubes-logo.svg" alt="Nubes">
|
||||||
<span class="title">Сверка договоров — LLM AI-driven Event Sourcing <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.0.108 — Lucee</span></span>
|
<span class="title">Сверка договоров — LLM AI-driven Event Sourcing <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.0.109 — Lucee</span></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@@ -86,7 +90,7 @@
|
|||||||
<div class="table-wrap">
|
<div class="table-wrap">
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr><th>Имя</th><th style="width:130px;">Изменён</th><th style="width:90px;">Размер</th><th style="width:115px;">Парсинг</th><th style="width:30px;">Базовый</th><th style="width:30px;">Текст</th><th style="width:30px;">✕</th></tr>
|
<tr><th>Имя</th><th style="width:130px;">Изменён</th><th style="width:90px;">Размер</th><th style="width:115px;">Парсинг</th><th style="width:30px;">↕</th><th style="width:30px;">Текст</th><th style="width:30px;">✕</th></tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="fileTable"></tbody>
|
<tbody id="fileTable"></tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -172,16 +176,16 @@ function renderTable() {
|
|||||||
fileTable.innerHTML = '<tr class="empty-row"><td colspan="7">Нет файлов — выберите .docx / .pdf / .zip</td></tr>';
|
fileTable.innerHTML = '<tr class="empty-row"><td colspan="7">Нет файлов — выберите .docx / .pdf / .zip</td></tr>';
|
||||||
} else {
|
} else {
|
||||||
fileTable.innerHTML = fileQueue.map(function(f, i) {
|
fileTable.innerHTML = fileQueue.map(function(f, i) {
|
||||||
var radio = '';
|
var isFirst = (i === 0);
|
||||||
if (f.supp_id) {
|
var isLast = (i === fileQueue.length - 1);
|
||||||
radio = '<input type="radio" name="base_doc" value="' + f.supp_id + '"' + (f.supp_type === 'initial' ? ' checked' : '') + ' onchange="setInitial(\'' + f.supp_id + '\')" title="Выбрать как базовый договор">';
|
var arrows = '<button class="arrow-btn" onclick="moveUp(' + i + ')" ' + (isFirst ? 'disabled' : '') + ' title="Вверх">▲</button>' +
|
||||||
}
|
'<button class="arrow-btn" onclick="moveDown(' + i + ')" ' + (isLast ? 'disabled' : '') + ' title="Вниз">▼</button>';
|
||||||
return '<tr id="row_' + i + '">' +
|
return '<tr id="row_' + i + '" class="' + (isFirst ? 'first-row' : '') + '">' +
|
||||||
'<td class="name-cell">' + f.name + '</td>' +
|
'<td class="name-cell">' + f.name + '</td>' +
|
||||||
'<td style="font-size:12px;color:var(--muted);">' + formatDate(f.lastModified) + '</td>' +
|
'<td style="font-size:12px;color:var(--muted);">' + formatDate(f.lastModified) + '</td>' +
|
||||||
'<td class="num-cell" style="font-size:12px;color:var(--muted);">' + formatSize(f.size) + '</td>' +
|
'<td class="num-cell" style="font-size:12px;color:var(--muted);">' + formatSize(f.size) + '</td>' +
|
||||||
'<td class="status-cell">' + (f.status || '') + '</td>' +
|
'<td class="status-cell">' + (f.status || '') + '</td>' +
|
||||||
'<td style="text-align:center;">' + radio + '</td>' +
|
'<td style="text-align:center;">' + arrows + '</td>' +
|
||||||
'<td><button class="info-btn' + (f.doc_id ? ' visible' : '') + '" onclick="showText(' + i + ')" title="Текст / Распарсено"><i data-lucide="file-text" style="width:16px;height:16px;"></i></button></td>' +
|
'<td><button class="info-btn' + (f.doc_id ? ' visible' : '') + '" onclick="showText(' + i + ')" title="Текст / Распарсено"><i data-lucide="file-text" style="width:16px;height:16px;"></i></button></td>' +
|
||||||
'<td><button class="remove-btn" onclick="removeFile(' + i + ')" title="Удалить"><i data-lucide="trash-2" style="width:16px;height:16px;"></i></button></td>' +
|
'<td><button class="remove-btn" onclick="removeFile(' + i + ')" title="Удалить"><i data-lucide="trash-2" style="width:16px;height:16px;"></i></button></td>' +
|
||||||
'</tr>';
|
'</tr>';
|
||||||
@@ -197,6 +201,19 @@ function removeFile(i) {
|
|||||||
}
|
}
|
||||||
window.removeFile = removeFile;
|
window.removeFile = removeFile;
|
||||||
|
|
||||||
|
function moveUp(i) {
|
||||||
|
if (i <= 0) return;
|
||||||
|
var tmp = fileQueue[i]; fileQueue[i] = fileQueue[i-1]; fileQueue[i-1] = tmp;
|
||||||
|
renderTable();
|
||||||
|
}
|
||||||
|
function moveDown(i) {
|
||||||
|
if (i >= fileQueue.length - 1) return;
|
||||||
|
var tmp = fileQueue[i]; fileQueue[i] = fileQueue[i+1]; fileQueue[i+1] = tmp;
|
||||||
|
renderTable();
|
||||||
|
}
|
||||||
|
window.moveUp = moveUp;
|
||||||
|
window.moveDown = moveDown;
|
||||||
|
|
||||||
// ── Автозагрузка при выборе файлов ──────────────────────────
|
// ── Автозагрузка при выборе файлов ──────────────────────────
|
||||||
fileInput.addEventListener('change', async function() {
|
fileInput.addEventListener('change', async function() {
|
||||||
var newFiles = Array.from(fileInput.files);
|
var newFiles = Array.from(fileInput.files);
|
||||||
@@ -337,7 +354,8 @@ document.getElementById('llmBtn').addEventListener('click', function() {
|
|||||||
diffStatus.textContent = '⏳ ' + ((Date.now() - llmStart) / 1000).toFixed(1) + 'с';
|
diffStatus.textContent = '⏳ ' + ((Date.now() - llmStart) / 1000).toFixed(1) + 'с';
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
var es = new EventSource('https://contracts.kube5s.ru/process-v2?contract_id=' + contractId);
|
var order = fileQueue.map(function(f) { return f.supp_id; }).filter(Boolean).join(',');
|
||||||
|
var es = new EventSource('https://contracts.kube5s.ru/process-v2?contract_id=' + contractId + (order ? '&order=' + encodeURIComponent(order) : ''));
|
||||||
var sections = {}; // supplement_id → DOM element
|
var sections = {}; // supplement_id → DOM element
|
||||||
|
|
||||||
es.onmessage = function(e) {
|
es.onmessage = function(e) {
|
||||||
@@ -444,12 +462,6 @@ async function refreshSupps() {
|
|||||||
} catch(e) { console.log('refreshSupps error:', e); }
|
} catch(e) { console.log('refreshSupps error:', e); }
|
||||||
}
|
}
|
||||||
|
|
||||||
function setInitial(suppId) {
|
|
||||||
fetch('/api.cfm?action=set_initial&supplement_id=' + suppId)
|
|
||||||
.then(function() { refreshSupps(); });
|
|
||||||
}
|
|
||||||
window.setInitial = setInitial;
|
|
||||||
|
|
||||||
// ── Чат ──────────────────────────────────────────────────────
|
// ── Чат ──────────────────────────────────────────────────────
|
||||||
document.getElementById('chatSend').addEventListener('click', function() {
|
document.getElementById('chatSend').addEventListener('click', function() {
|
||||||
var input = document.getElementById('chatInput');
|
var input = document.getElementById('chatInput');
|
||||||
|
|||||||
Reference in New Issue
Block a user