diff --git a/public/app.js b/public/app.js index 8f5cfe9..12f120b 100644 --- a/public/app.js +++ b/public/app.js @@ -123,7 +123,7 @@ async function selectTable(schema, tableName) { } table = new Tabulator("#table", { - selectableRows: 1, + selectableRows: true, // ✅ Множественное выделение (вместо selectableRows: 1) columns: columns, layout: "fitColumns", resizableColumnFit: true, @@ -188,7 +188,14 @@ async function selectTable(schema, tableName) { }, rowClick: function(e, row) { - row.toggleSelect(); + // ✅ Ctrl/Cmd + Click для множественного выбора + if (e.ctrlKey || e.metaKey) { + row.toggleSelect(); + } else { + // Обычный клик - снимаем выделение с других и выделяем текущую + table.deselectRow(); + row.toggleSelect(); + } }, cellEdited: function(cell) { @@ -247,6 +254,7 @@ async function selectTable(schema, tableName) { + // CRUD кнопки document.getElementById('btnInsert').addEventListener('click', async () => { if (!currentSchema || !currentTable || !currentMeta) { @@ -487,22 +495,39 @@ document.getElementById('btnDelete').addEventListener('click', async () => { if (!table || !currentSchema || !currentTable) return; const selected = table.getSelectedData(); if (selected.length === 0) { - alert('Ничего не выбрано'); + alert('Выберите строки для удаления (используйте Ctrl+Click для выбора нескольких)'); return; } - if (!confirm(`Удалить ${selected.length} строк(и)?`)) return; + + const confirmMsg = selected.length === 1 + ? 'Удалить выбранную строку?' + : `Удалить ${selected.length} выбранных строк?`; + + if (!confirm(confirmMsg)) return; try { + let deleted = 0; for (const row of selected) { await api('/api/table/delete', 'POST', { schema: currentSchema, table: currentTable, row }); + deleted++; } await table.replaceData(); + alert(`✓ Удалено строк: ${deleted}`); } catch (e) { console.error(e); alert('Ошибка удаления: ' + e.message); } }); +// ✅ Снятие выделения +document.getElementById('btnDeselectAll').addEventListener('click', () => { + if (table) { + table.deselectRow(); + console.log('Выделение снято'); + } +}); + + // ========== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ CSV ========== // Функция автоопределения разделителя diff --git a/public/index.html b/public/index.html index 3682609..b22bcbd 100644 --- a/public/index.html +++ b/public/index.html @@ -14,7 +14,7 @@ font-family: sans-serif; display: flex; height: 100vh; - overflow: hidden; /* ✅ Предотвращаем скролл body */ + overflow: hidden; } #sidebar { width: 250px; @@ -22,25 +22,25 @@ padding: 8px; box-sizing: border-box; overflow-y: auto; - flex-shrink: 0; /* ✅ Sidebar не сжимается */ + flex-shrink: 0; } #main { flex: 1; display: flex; flex-direction: column; - min-width: 0; /* ✅ Позволяет flex-элементу сжиматься */ - overflow: hidden; /* ✅ Контролируем overflow */ + min-width: 0; + overflow: hidden; } #toolbar { padding: 8px; border-bottom: 1px solid #ccc; - flex-shrink: 0; /* ✅ Toolbar не сжимается */ + flex-shrink: 0; } #table { flex: 1; position: relative; - overflow: auto; /* ✅ Скролл только внутри таблицы */ - min-height: 0; /* ✅ Важно для flex-контейнера */ + overflow: auto; + min-height: 0; } .schema { font-weight: bold; margin-top: 8px; cursor: pointer; } .table { margin-left: 12px; cursor: pointer; } @@ -48,7 +48,7 @@ padding: 8px; border-bottom: 1px solid #ccc; background: #f7f7f7; - flex-shrink: 0; /* ✅ Login panel не сжимается */ + flex-shrink: 0; } #csvFileInput { display: none; } @@ -64,31 +64,73 @@ } .tabulator .tabulator-tableholder { - overflow-x: auto !important; /* ✅ Горизонтальный скролл при необходимости */ + overflow-x: auto !important; } - .fk-modal { - font-family: sans-serif; + /* ✅ ИСПРАВЛЕНИЕ: Размер шрифта при редактировании ячеек */ + .tabulator-cell input, + .tabulator-cell select, + .tabulator-cell textarea { + font-size: 14px !important; + font-family: sans-serif !important; + padding: 4px !important; + border: 1px solid #4CAF50 !important; + box-sizing: border-box !important; + width: 100% !important; + min-height: 24px !important; + } + + .tabulator-cell input:focus, + .tabulator-cell select:focus, + .tabulator-cell textarea:focus { + outline: none !important; + border-color: #45a049 !important; + box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2) !important; + } + + /* ✅ Стили для выделенных строк */ + .tabulator-row.tabulator-selected { + background-color: #d4e9ff !important; + } + + .tabulator-row.tabulator-selected:hover { + background-color: #c0dcf5 !important; + } + + /* ✅ Стили для модальных окон FK */ + .fk-modal { + font-family: sans-serif; } .fk-modal select, .fk-modal input { - border: 1px solid #ccc; - border-radius: 4px; + border: 1px solid #ccc; + border-radius: 4px; } .fk-modal select:focus, .fk-modal input:focus { - outline: none; - border-color: #4CAF50; - box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2); + outline: none; + border-color: #4CAF50; + box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2); } .fk-modal button:hover { - opacity: 0.9; + opacity: 0.9; + } + + /* ✅ Подсказка для множественного выбора */ + #toolbar::after { + content: "💡 Используйте Ctrl+Click для выбора нескольких строк"; + display: inline-block; + margin-left: 20px; + font-size: 12px; + color: #666; + font-style: italic; } +
-