- | Имя | Изменён | Размер | Статус | |
+ | Имя | Изменён | Размер | Статус | |
@@ -85,6 +85,7 @@
let fileQueue = [];
function formatSize(bytes) {
+ if (!bytes || bytes === 0) return '—';
if (bytes < 1024) return bytes + ' B';
if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
return (bytes / 1048576).toFixed(1) + ' MB';
@@ -107,7 +108,7 @@
return '
' +
'| ' + f.name + ' | ' +
'' + formatDate(f.lastModified) + ' | ' +
- '' + (f.size ? formatSize(f.size) : '—') + ' | ' +
+ '' + formatSize(f.size) + ' | ' +
' | ' +
' | ' +
'
';
@@ -118,7 +119,6 @@
function removeFile(index) {
var f = fileQueue[index];
if (!f) return;
- // Если удаляем ZIP — удалить и всех его детей
if (!f.isZipChild && f.file && (f.file.type === 'application/zip' || f.name.toLowerCase().endsWith('.zip'))) {
for (var i = fileQueue.length - 1; i >= 0; i--) {
if (fileQueue[i].zipName === f.name) fileQueue.splice(i, 1);
@@ -144,7 +144,6 @@
file: f
});
- // ZIP: показать содержимое
if (f.type === 'application/zip' || f.name.toLowerCase().endsWith('.zip')) {
try {
var zip = await JSZip.loadAsync(f);
@@ -152,10 +151,11 @@
if (!zipEntry.dir) {
var ext = relativePath.toLowerCase().split('.').pop();
if (['docx','doc','pdf'].indexOf(ext) === -1) return;
+ var zsize = (zipEntry._data && zipEntry._data.uncompressedSize) ? zipEntry._data.uncompressedSize : 0;
fileQueue.push({
name: relativePath,
lastModified: zipEntry.date ? zipEntry.date.getTime() : 0,
- size: 0,
+ size: zsize,
isZipChild: true,
zipName: f.name
});
@@ -171,6 +171,51 @@
renderTable();
});
+ // ── Загрузка по одному файлу ──────────────────────────────
+
+ function uploadOne(file, cid, row) {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ var url = '/' + (cid ? '?cid=' + cid : '');
+ xhr.open('POST', url);
+
+ xhr.upload.onprogress = function(e) {
+ if (e.lengthComputable && row) {
+ var pct = Math.round(e.loaded / e.total * 100);
+ row.querySelector('.status-cell').innerHTML = '
↑ ' + pct + '%';
+ }
+ };
+
+ xhr.onload = function() {
+ if (xhr.status === 200) {
+ try {
+ resolve(JSON.parse(xhr.responseText));
+ } catch(e) {
+ reject(new Error('Bad JSON'));
+ }
+ } else {
+ reject(new Error('HTTP ' + xhr.status));
+ }
+ };
+
+ xhr.onerror = function() {
+ reject(new Error('Сеть'));
+ };
+
+ xhr.ontimeout = function() {
+ reject(new Error('Таймаут'));
+ };
+
+ xhr.timeout = 60000;
+
+ var fd = new FormData();
+ fd.append('files', file);
+ xhr.send(fd);
+ });
+ }
+
+ // ── Парсинг ────────────────────────────────────────────────
+
parseBtn.addEventListener('click', async function() {
if (fileQueue.length === 0) return;
@@ -180,21 +225,39 @@
parseBtn.disabled = true;
parseBtn.innerHTML = '
Загрузка...';
- var formData = new FormData();
- realFiles.forEach(function(f) { formData.append('files', f.file); });
+ var contractId = null;
- var contractId;
- try {
- var uploadResp = await fetch('/', { method: 'POST', body: formData });
- var uploadJson = await uploadResp.json();
- if (uploadJson.error) { alert(uploadJson.error); resetBtn(); return; }
- contractId = uploadJson.contract_id;
- } catch(e) {
- alert('Ошибка загрузки: ' + e.message);
+ // Загружаем каждый файл отдельно
+ for (var i = 0; i < realFiles.length; i++) {
+ var f = realFiles[i];
+ var row = findRowByName(f.name);
+
+ if (row) {
+ row.querySelector('.status-cell').innerHTML = '
↑ 0%';
+ }
+
+ try {
+ var resp = await uploadOne(f.file, contractId, row);
+ contractId = resp.contract_id;
+
+ if (row) {
+ row.querySelector('.status-cell').innerHTML = '
✓ загружен';
+ }
+ } catch(e) {
+ if (row) {
+ row.querySelector('.status-cell').innerHTML = '
✗ ' + e.message + '';
+ }
+ resetBtn();
+ return;
+ }
+ }
+
+ if (!contractId) {
resetBtn();
return;
}
+ // Фаза парсинга — SSE
parseBtn.innerHTML = '
Парсинг...';
var es = new EventSource('/parse/' + contractId);
@@ -213,6 +276,13 @@
var row = findRowByName(d.name);
if (row) {
row.querySelector('.status-cell').innerHTML = '
⏳ 0с';
+ // Обновить размер если был неизвестен
+ if (d.bytes > 0) {
+ var sizeCell = row.querySelector('.num-cell');
+ if (sizeCell && sizeCell.textContent.trim() === '—') {
+ sizeCell.textContent = formatSize(d.bytes);
+ }
+ }
}
}
@@ -246,7 +316,7 @@
summaryRow.className = 'summary-row';
summaryRow.innerHTML = '
✓ Готово: ' + d.files_processed + ' файлов | ' +
'
| ' +
- '
' + formatSize(d.total_bytes) + ' | ' +
+ '
' + formatSize(d.total_bytes) + ' | ' +
'
' + d.total_time_s + 'с | ' +
'
| ';
fileTable.appendChild(summaryRow);