fix: исправлена критическая ошибка - rowSelectionChanged не добавлял строки в selectedRowsData
- Изменена логика rowSelectionChanged: теперь используется параметр data вместо rows - Параметр data содержит ВСЕ выделенные строки, а rows - только измененные - При клике на header checkbox параметр rows пустой, поэтому selectedRowsData не обновлялся - Добавлена правильная синхронизация выделенных строк между Tabulator и selectedRowsData - Упрощена логика: полная пересинхронизация при каждом изменении выделения
This commit is contained in:
184
public/app.js
184
public/app.js
@@ -282,33 +282,187 @@ async function selectTable(schema, tableName) {
|
||||
};
|
||||
},
|
||||
|
||||
// ✅ ИСПРАВЛЕНО: Простая и правильная обработка выделения
|
||||
async function selectTable(schema, tableName) {
|
||||
currentSchema = schema;
|
||||
currentTable = tableName;
|
||||
lastEditedRow = null;
|
||||
selectedRowsData.clear();
|
||||
updateSelectionCounter();
|
||||
|
||||
if (enterHandler) {
|
||||
document.removeEventListener('keydown', enterHandler);
|
||||
enterHandler = null;
|
||||
}
|
||||
|
||||
currentMeta = await api(
|
||||
`/api/table/meta?schema=${encodeURIComponent(schema)}&table=${encodeURIComponent(tableName)}`
|
||||
);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
formatter: "rowSelection",
|
||||
titleFormatter: "rowSelection",
|
||||
hozAlign: "center",
|
||||
headerSort: false,
|
||||
width: 40,
|
||||
cellClick: function(e, cell) {
|
||||
cell.getRow().toggleSelect();
|
||||
}
|
||||
},
|
||||
...currentMeta.columns.map(col => ({
|
||||
title: col.COLUMN_NAME,
|
||||
field: col.COLUMN_NAME,
|
||||
editor: "input",
|
||||
headerFilter: "input"
|
||||
}))
|
||||
];
|
||||
|
||||
if (table) {
|
||||
table.destroy();
|
||||
table = null;
|
||||
}
|
||||
|
||||
table = new Tabulator("#table", {
|
||||
selectableRows: true,
|
||||
columns: columns,
|
||||
layout: "fitColumns",
|
||||
resizableColumnFit: true,
|
||||
|
||||
pagination: true,
|
||||
paginationMode: "remote",
|
||||
paginationSize: 50,
|
||||
paginationSizeSelector: [25, 50, 100, 200],
|
||||
|
||||
filterMode: "remote",
|
||||
sortMode: "remote",
|
||||
|
||||
ajaxURL: "/api/table/data",
|
||||
ajaxConfig: "POST",
|
||||
ajaxContentType: "json",
|
||||
|
||||
ajaxParams: function () {
|
||||
const headerFilters = this.getHeaderFilters ? this.getHeaderFilters() : [];
|
||||
const filters = (headerFilters || []).map(f => ({
|
||||
field: f.field,
|
||||
value: f.value
|
||||
})).filter(f => f.value !== null && f.value !== '');
|
||||
|
||||
const sorters = this.getSorters ? this.getSorters() : [];
|
||||
const sort = (sorters && sorters.length > 0) ? {
|
||||
field: sorters[0].field,
|
||||
dir: sorters[0].dir
|
||||
} : null;
|
||||
|
||||
return {
|
||||
schema: currentSchema,
|
||||
table: currentTable,
|
||||
filters: filters,
|
||||
sort: sort,
|
||||
columns: currentMeta.columns,
|
||||
page: this.getPage ? this.getPage() : 1,
|
||||
pageSize: this.getPageSize ? this.getPageSize() : 50
|
||||
};
|
||||
},
|
||||
|
||||
ajaxResponse: function (url, params, response) {
|
||||
return {
|
||||
last_page: response.last_page || 1,
|
||||
data: response.data || []
|
||||
};
|
||||
},
|
||||
|
||||
// ✅ ИСПРАВЛЕНО: Используем параметр data, который содержит ВСЕ выделенные строки
|
||||
rowSelectionChanged: function(data, rows) {
|
||||
console.log('🔔 rowSelectionChanged:', {
|
||||
dataLength: data.length,
|
||||
rowsLength: rows.length,
|
||||
selectedRowsDataSizeBefore: selectedRowsData.size
|
||||
console.log('🔔 rowSelectionChanged called:', {
|
||||
'data (all selected)': data.length,
|
||||
'rows (changed)': rows.length,
|
||||
'selectedRowsData before': selectedRowsData.size
|
||||
});
|
||||
|
||||
// Обрабатываем только изменившиеся строки
|
||||
// ВАЖНО: параметр data содержит массив данных ВСЕХ выделенных строк
|
||||
// параметр rows содержит только Row объекты измененных строк
|
||||
|
||||
// Получаем все выделенные строки напрямую из Tabulator
|
||||
const allSelectedData = data; // это уже массив данных всех выделенных строк
|
||||
|
||||
// Удаляем из selectedRowsData все строки текущей страницы
|
||||
const currentPageRows = this.getRows();
|
||||
currentPageRows.forEach(row => {
|
||||
const rowData = row.getData();
|
||||
const key = getRowKey(rowData);
|
||||
selectedRowsData.delete(key);
|
||||
console.log(` 🧹 Removed from selectedRowsData: ${key}`);
|
||||
});
|
||||
|
||||
// Добавляем все выделенные строки обратно
|
||||
allSelectedData.forEach(rowData => {
|
||||
const key = getRowKey(rowData);
|
||||
selectedRowsData.set(key, rowData);
|
||||
console.log(` ✅ Added to selectedRowsData: ${key}`);
|
||||
});
|
||||
|
||||
console.log(` 📊 selectedRowsData after: ${selectedRowsData.size}`);
|
||||
updateSelectionCounter();
|
||||
},
|
||||
|
||||
dataLoaded: function(data) {
|
||||
console.log('📄 dataLoaded, восстанавливаем выделение для', selectedRowsData.size, 'строк');
|
||||
|
||||
if (selectedRowsData.size > 0) {
|
||||
const rows = this.getRows();
|
||||
let restoredCount = 0;
|
||||
|
||||
rows.forEach(row => {
|
||||
const rowData = row.getData();
|
||||
const key = getRowKey(rowData);
|
||||
const isSelected = row.isSelected();
|
||||
|
||||
console.log(` Row key: ${key}, isSelected: ${isSelected}`);
|
||||
|
||||
if (isSelected) {
|
||||
selectedRowsData.set(key, rowData);
|
||||
} else {
|
||||
selectedRowsData.delete(key);
|
||||
if (selectedRowsData.has(key)) {
|
||||
row.select();
|
||||
restoredCount++;
|
||||
}
|
||||
});
|
||||
|
||||
console.log(' ✅ selectedRowsData size after:', selectedRowsData.size);
|
||||
updateSelectionCounter();
|
||||
console.log(' ✅ Восстановлено выделение для', restoredCount, 'строк на странице');
|
||||
}
|
||||
},
|
||||
|
||||
cellEdited: function(cell) {
|
||||
const row = cell.getRow();
|
||||
lastEditedRow = row.getData();
|
||||
row.getElement().style.backgroundColor = '#fffae6';
|
||||
},
|
||||
|
||||
headerFilterLiveFilterDelay: 800
|
||||
});
|
||||
|
||||
enterHandler = async function(e) {
|
||||
if (e.key === 'Enter' && lastEditedRow && 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', enterHandler);
|
||||
}
|
||||
|
||||
// После загрузки данных восстанавливаем выделение
|
||||
dataLoaded: function(data) {
|
||||
console.log('📄 dataLoaded, восстанавливаем выделение для', selectedRowsData.size, 'строк');
|
||||
|
||||
Reference in New Issue
Block a user