add table edit feature with auto saving on focus loose

This commit is contained in:
Mikhail Chusavitin
2026-01-21 15:47:01 +03:00
parent ee02d4cc28
commit 227d2c3442

View File

@@ -185,7 +185,7 @@ async function selectTable(schema, tableName) {
currentSchema = schema;
currentTable = tableName;
lastEditedRow = null;
selectedRowsDataGlobal.clear(); // Очищаем при смене таблицы
selectedRowsDataGlobal.clear();
updateSelectionCounter();
if (enterHandler) {
@@ -221,6 +221,10 @@ async function selectTable(schema, tableName) {
table = null;
}
// ✅ Переменная для отслеживания несохраненных изменений
let pendingSave = null;
let saveTimeout = null;
table = new Tabulator("#table", {
selectableRows: true,
columns: columns,
@@ -270,12 +274,10 @@ async function selectTable(schema, tableName) {
};
},
// Обновляем счетчик при изменении выделения
rowSelectionChanged: function(data, rows) {
updateSelectionCounter();
},
// Восстанавливаем выделение для глобально выделенных строк
dataLoaded: function(data) {
if (selectedRowsDataGlobal.size > 0) {
const rows = this.getRows();
@@ -291,36 +293,95 @@ async function selectTable(schema, tableName) {
}
},
// ✅ При начале редактирования
cellEditing: function(cell) {
const row = cell.getRow();
row.getElement().style.backgroundColor = '#fff9e6';
},
// ✅ При изменении ячейки
cellEdited: function(cell) {
const row = cell.getRow();
lastEditedRow = row.getData();
const rowData = row.getData();
// Подсвечиваем измененную строку
row.getElement().style.backgroundColor = '#fffae6';
// Сохраняем данные для отложенного сохранения
pendingSave = {
cell: cell,
row: rowData,
rowElement: row
};
// ✅ Автосохранение через 1.5 секунды после последнего изменения
clearTimeout(saveTimeout);
saveTimeout = setTimeout(async () => {
if (pendingSave) {
await saveRowData(pendingSave.row, pendingSave.rowElement);
pendingSave = null;
}
}, 1500);
},
// ✅ При отмене редактирования (потеря фокуса, Esc)
cellEditCancelled: function(cell) {
// Если были несохраненные изменения - сохраняем их
if (pendingSave && pendingSave.cell === cell) {
clearTimeout(saveTimeout);
saveRowData(pendingSave.row, pendingSave.rowElement).then(() => {
pendingSave = null;
});
}
},
headerFilterLiveFilterDelay: 800
});
// ✅ Функция сохранения строки
async function saveRowData(rowData, rowElement) {
if (!currentSchema || !currentTable) return;
try {
await api('/api/table/update', 'POST', {
schema: currentSchema,
table: currentTable,
row: rowData
});
// Убираем подсветку после успешного сохранения
if (rowElement && rowElement.getElement) {
rowElement.getElement().style.backgroundColor = '#e8f5e9'; // Зеленоватый фон
setTimeout(() => {
rowElement.getElement().style.backgroundColor = '';
}, 1000);
}
console.log('✓ Изменения сохранены:', rowData);
} catch (err) {
console.error('Ошибка сохранения:', err);
// Красная подсветка при ошибке
if (rowElement && rowElement.getElement) {
rowElement.getElement().style.backgroundColor = '#ffebee';
}
alert('Ошибка сохранения: ' + err.message);
}
}
// ✅ Обработчик Enter - сохраняет немедленно
enterHandler = async function(e) {
if (e.key === 'Enter' && lastEditedRow && currentSchema && currentTable) {
if (e.key === 'Enter' && pendingSave && currentSchema && currentTable) {
e.preventDefault();
try {
const res = await api('/api/table/update', 'POST', {
schema: currentSchema,
table: currentTable,
row: lastEditedRow
});
lastEditedRow = null;
table.getRows().forEach(r => {
r.getElement().style.backgroundColor = '';
});
await table.replaceData();
} catch (err) {
console.error('Ошибка сохранения:', err);
alert('Ошибка: ' + err.message);
clearTimeout(saveTimeout);
await saveRowData(pendingSave.row, pendingSave.rowElement);
pendingSave = null;
// Снимаем фокус с ячейки
if (document.activeElement) {
document.activeElement.blur();
}
}
};
@@ -328,6 +389,7 @@ async function selectTable(schema, tableName) {
document.addEventListener('keydown', enterHandler);
}
document.getElementById('btnSelectAll').addEventListener('click', async () => {
if (!currentSchema || !currentTable || !table) {
alert('Сначала выберите таблицу');