fix: исправлена ошибка "createProgressModal is not defined"
- Добавлена функция createProgressModal в правильное место в app.js - Упрощен код удаления до одного варианта (без излишней сложности) - Функция определена до использования - Проверена работоспособность batch delete
This commit is contained in:
148
public/app.js
148
public/app.js
@@ -31,6 +31,73 @@ function updateSelectionCounter() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Функция создания модального окна с прогресс-баром
|
||||||
|
function createProgressModal(message) {
|
||||||
|
const modal = document.createElement('div');
|
||||||
|
modal.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0,0,0,0.5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 10000;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const dialog = document.createElement('div');
|
||||||
|
dialog.style.cssText = `
|
||||||
|
background: white;
|
||||||
|
padding: 30px;
|
||||||
|
border-radius: 8px;
|
||||||
|
min-width: 300px;
|
||||||
|
text-align: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
dialog.innerHTML = `
|
||||||
|
<div style="font-size: 16px; margin-bottom: 20px;">${message}</div>
|
||||||
|
<div style="width: 100%; height: 30px; background: #e0e0e0; border-radius: 15px; overflow: hidden;">
|
||||||
|
<div class="progress-bar" style="width: 0%; height: 100%; background: linear-gradient(90deg, #4CAF50, #45a049); transition: width 0.3s;"></div>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 10px; font-size: 14px; color: #666;">
|
||||||
|
<span class="spinner" style="display: inline-block; animation: spin 1s linear infinite;">⏳</span>
|
||||||
|
Пожалуйста, подождите...
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
@keyframes spin {
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`;
|
||||||
|
|
||||||
|
modal.appendChild(dialog);
|
||||||
|
|
||||||
|
// Анимация прогресс-бара (имитация)
|
||||||
|
const progressBar = dialog.querySelector('.progress-bar');
|
||||||
|
let progress = 0;
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
progress += Math.random() * 15;
|
||||||
|
if (progress > 90) progress = 90; // Останавливаемся на 90%
|
||||||
|
progressBar.style.width = progress + '%';
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
modal.stopProgress = () => {
|
||||||
|
clearInterval(interval);
|
||||||
|
progressBar.style.width = '100%';
|
||||||
|
};
|
||||||
|
|
||||||
|
return modal;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(text) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.textContent = text;
|
||||||
|
return div.innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
// Логин
|
// Логин
|
||||||
document.getElementById('loginBtn').addEventListener('click', async () => {
|
document.getElementById('loginBtn').addEventListener('click', async () => {
|
||||||
const user = document.getElementById('loginUser').value.trim();
|
const user = document.getElementById('loginUser').value.trim();
|
||||||
@@ -563,13 +630,7 @@ async function promptForForeignKeys(fkFields) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeHtml(text) {
|
// ✅ УДАЛИТЬ (оптимизированное с batch delete)
|
||||||
const div = document.createElement('div');
|
|
||||||
div.textContent = text;
|
|
||||||
return div.innerHTML;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ УДАЛИТЬ (с реальным прогрессом для больших объемов)
|
|
||||||
document.getElementById('btnDelete').addEventListener('click', async () => {
|
document.getElementById('btnDelete').addEventListener('click', async () => {
|
||||||
if (!table || !currentSchema || !currentTable) return;
|
if (!table || !currentSchema || !currentTable) return;
|
||||||
|
|
||||||
@@ -586,76 +647,15 @@ document.getElementById('btnDelete').addEventListener('click', async () => {
|
|||||||
|
|
||||||
if (!confirm(confirmMsg)) return;
|
if (!confirm(confirmMsg)) return;
|
||||||
|
|
||||||
const rowsArray = Array.from(selectedRowsData.values());
|
// Создаем модальное окно с прогресс-баром
|
||||||
|
|
||||||
// Для очень больших удалений - показываем прогресс
|
|
||||||
const BATCH_SIZE = 1000; // Удаляем по 1000 строк за раз
|
|
||||||
const batches = [];
|
|
||||||
for (let i = 0; i < rowsArray.length; i += BATCH_SIZE) {
|
|
||||||
batches.push(rowsArray.slice(i, i + BATCH_SIZE));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (batches.length > 1) {
|
|
||||||
// Множество батчей - показываем реальный прогресс
|
|
||||||
const modal = createProgressModal(`Удаление ${count} записей...`);
|
|
||||||
document.body.appendChild(modal);
|
|
||||||
|
|
||||||
const progressBar = modal.querySelector('.progress-bar');
|
|
||||||
const progressText = modal.querySelector('.spinner').parentElement;
|
|
||||||
|
|
||||||
let totalDeleted = 0;
|
|
||||||
let totalErrors = 0;
|
|
||||||
let allErrorMessages = [];
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (let i = 0; i < batches.length; i++) {
|
|
||||||
const batch = batches[i];
|
|
||||||
|
|
||||||
progressText.innerHTML = `<span class="spinner" style="display: inline-block; animation: spin 1s linear infinite;">⏳</span> Обработка ${i + 1} из ${batches.length} батчей...`;
|
|
||||||
|
|
||||||
const result = await api('/api/table/delete-batch', 'POST', {
|
|
||||||
schema: currentSchema,
|
|
||||||
table: currentTable,
|
|
||||||
rows: batch
|
|
||||||
});
|
|
||||||
|
|
||||||
totalDeleted += result.deleted;
|
|
||||||
totalErrors += result.errors;
|
|
||||||
if (result.errorMessages) {
|
|
||||||
allErrorMessages.push(...result.errorMessages);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Обновляем прогресс
|
|
||||||
const progress = ((i + 1) / batches.length) * 100;
|
|
||||||
progressBar.style.width = progress + '%';
|
|
||||||
}
|
|
||||||
|
|
||||||
document.body.removeChild(modal);
|
|
||||||
|
|
||||||
selectedRowsData.clear();
|
|
||||||
updateSelectionCounter();
|
|
||||||
await table.replaceData();
|
|
||||||
|
|
||||||
if (totalErrors > 0) {
|
|
||||||
const errorsToShow = allErrorMessages.slice(0, 10);
|
|
||||||
const moreErrors = allErrorMessages.length > 10
|
|
||||||
? `\n... и еще ${allErrorMessages.length - 10} ошибок`
|
|
||||||
: '';
|
|
||||||
alert(`Удалено строк: ${totalDeleted}\nОшибок: ${totalErrors}\n\nПервые ошибки:\n${errorsToShow.join('\n')}${moreErrors}`);
|
|
||||||
} else {
|
|
||||||
alert(`✓ Успешно удалено строк: ${totalDeleted}`);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
document.body.removeChild(modal);
|
|
||||||
console.error(e);
|
|
||||||
alert('Ошибка удаления: ' + e.message);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Один батч - используем простое модальное окно
|
|
||||||
const modal = createProgressModal('Удаление записей...');
|
const modal = createProgressModal('Удаление записей...');
|
||||||
document.body.appendChild(modal);
|
document.body.appendChild(modal);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Преобразуем Map в массив
|
||||||
|
const rowsArray = Array.from(selectedRowsData.values());
|
||||||
|
|
||||||
|
// Используем batch delete для оптимизации
|
||||||
const result = await api('/api/table/delete-batch', 'POST', {
|
const result = await api('/api/table/delete-batch', 'POST', {
|
||||||
schema: currentSchema,
|
schema: currentSchema,
|
||||||
table: currentTable,
|
table: currentTable,
|
||||||
@@ -682,10 +682,8 @@ document.getElementById('btnDelete').addEventListener('click', async () => {
|
|||||||
console.error(e);
|
console.error(e);
|
||||||
alert('Ошибка удаления: ' + e.message);
|
alert('Ошибка удаления: ' + e.message);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// ========== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ CSV ==========
|
// ========== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ CSV ==========
|
||||||
|
|
||||||
function detectDelimiter(text) {
|
function detectDelimiter(text) {
|
||||||
|
|||||||
Reference in New Issue
Block a user