Боковая панель в данных для журнала
This commit is contained in:
@@ -617,4 +617,33 @@
|
|||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
to { transform: rotate(360deg); }
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== БОКОВАЯ ПАНЕЛЬ ===== */
|
||||||
|
.logs-sidebar {
|
||||||
|
flex: 0 0 280px;
|
||||||
|
border-right: 1px solid #00ff88;
|
||||||
|
padding-right: 16px;
|
||||||
|
padding-left: 4px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: visible;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logs-sidebar.hidden {
|
||||||
|
flex: 0 0 0 !important;
|
||||||
|
width: 0 !important;
|
||||||
|
min-width: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
border: none !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logs-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
height: calc(100vh - 120px);
|
||||||
|
overflow: visible;
|
||||||
|
transition: gap 0.3s ease;
|
||||||
}
|
}
|
||||||
@@ -108,6 +108,10 @@ const DOM = {
|
|||||||
eventsContent: document.getElementById('eventsContent') as HTMLElement | null,
|
eventsContent: document.getElementById('eventsContent') as HTMLElement | null,
|
||||||
serverTime: document.getElementById('server-time') as HTMLElement | null,
|
serverTime: document.getElementById('server-time') as HTMLElement | null,
|
||||||
|
|
||||||
|
// Боковая панель
|
||||||
|
logsSidebar: document.querySelector('.logs-sidebar') as HTMLElement | null,
|
||||||
|
logsContainer: document.querySelector('.logs-container') as HTMLElement | null,
|
||||||
|
|
||||||
// Элементы пагинации для данных
|
// Элементы пагинации для данных
|
||||||
dataPageInfo: document.getElementById('dataPageInfo') as HTMLElement | null,
|
dataPageInfo: document.getElementById('dataPageInfo') as HTMLElement | null,
|
||||||
dataPrevPageBtn: document.getElementById('dataPrevPage') as HTMLButtonElement | null,
|
dataPrevPageBtn: document.getElementById('dataPrevPage') as HTMLButtonElement | null,
|
||||||
@@ -127,6 +131,29 @@ const DOM = {
|
|||||||
goToPageBtn: document.getElementById('goToPage') as HTMLButtonElement | null,
|
goToPageBtn: document.getElementById('goToPage') as HTMLButtonElement | null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// УПРАВЛЕНИЕ БОКОВОЙ ПАНЕЛЬЮ
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Показать/скрыть боковую панель в зависимости от активной вкладки
|
||||||
|
*/
|
||||||
|
function toggleSidebar(show: boolean): void {
|
||||||
|
if (!DOM.logsSidebar || !DOM.logsContainer) return;
|
||||||
|
|
||||||
|
if (show) {
|
||||||
|
// Показываем панель
|
||||||
|
DOM.logsSidebar.style.display = 'block';
|
||||||
|
DOM.logsSidebar.style.flex = '0 0 280px';
|
||||||
|
DOM.logsContainer.style.gap = '20px';
|
||||||
|
} else {
|
||||||
|
// Скрываем панель
|
||||||
|
DOM.logsSidebar.style.display = 'none';
|
||||||
|
DOM.logsSidebar.style.flex = '0 0 0';
|
||||||
|
DOM.logsContainer.style.gap = '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// ФУНКЦИИ ЗАГРУЗКИ ДАННЫХ
|
// ФУНКЦИИ ЗАГРУЗКИ ДАННЫХ
|
||||||
// ============================================
|
// ============================================
|
||||||
@@ -206,7 +233,6 @@ async function loadFileData(filename: string, page: number = 1): Promise<void> {
|
|||||||
|
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
|
|
||||||
// Проверяем статус ответа
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||||
}
|
}
|
||||||
@@ -222,7 +248,6 @@ async function loadFileData(filename: string, page: number = 1): Promise<void> {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Logs] Ошибка загрузки данных:', error);
|
console.error('[Logs] Ошибка загрузки данных:', error);
|
||||||
if (DOM.dataTableContainer) {
|
if (DOM.dataTableContainer) {
|
||||||
// Показываем информативное сообщение, а не красную ошибку
|
|
||||||
DOM.dataTableContainer.innerHTML = `
|
DOM.dataTableContainer.innerHTML = `
|
||||||
<div class="empty-state">
|
<div class="empty-state">
|
||||||
<div class="empty-message">Нет данных для отображения</div>
|
<div class="empty-message">Нет данных для отображения</div>
|
||||||
@@ -230,7 +255,6 @@ async function loadFileData(filename: string, page: number = 1): Promise<void> {
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
// Сбрасываем пагинацию
|
|
||||||
if (DOM.dataPageInfo) {
|
if (DOM.dataPageInfo) {
|
||||||
DOM.dataPageInfo.textContent = 'Нет записей';
|
DOM.dataPageInfo.textContent = 'Нет записей';
|
||||||
}
|
}
|
||||||
@@ -319,15 +343,13 @@ function renderData(data: LogDataResponse): void {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// График - показываем все данные (ограничиваем для производительности)
|
// График
|
||||||
if (DOM.logChart && data.total > 0) {
|
if (DOM.logChart && data.total > 0) {
|
||||||
// Для графика загружаем все данные (но не более 5000 точек)
|
|
||||||
// В реальности лучше отдельный API для графика
|
|
||||||
const history = samples.map((s: LogSample) => s.signal);
|
const history = samples.map((s: LogSample) => s.signal);
|
||||||
drawChart(DOM.logChart, history);
|
drawChart(DOM.logChart, history);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Таблица - показываем текущую страницу
|
// Таблица
|
||||||
if (samples.length > 0 && DOM.dataTableContainer) {
|
if (samples.length > 0 && DOM.dataTableContainer) {
|
||||||
let html = `<table class="data-table">
|
let html = `<table class="data-table">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -360,7 +382,12 @@ function renderData(data: LogDataResponse): void {
|
|||||||
html += '</tbody></table>';
|
html += '</tbody></table>';
|
||||||
DOM.dataTableContainer.innerHTML = html;
|
DOM.dataTableContainer.innerHTML = html;
|
||||||
} else if (DOM.dataTableContainer) {
|
} else if (DOM.dataTableContainer) {
|
||||||
DOM.dataTableContainer.innerHTML = '<p>Нет данных</p>';
|
DOM.dataTableContainer.innerHTML = `
|
||||||
|
<div class="empty-state">
|
||||||
|
<div class="empty-message">Нет данных</div>
|
||||||
|
<div class="empty-hint">Выберите файл с данными</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,7 +399,12 @@ function renderEvents(data: EventsResponse): void {
|
|||||||
|
|
||||||
let html = '';
|
let html = '';
|
||||||
if (data.events.length === 0) {
|
if (data.events.length === 0) {
|
||||||
html = '<div style="color:#666;">Нет событий</div>';
|
html = `
|
||||||
|
<div class="empty-state">
|
||||||
|
<div class="empty-message">Нет событий</div>
|
||||||
|
<div class="empty-hint">Журнал событий пуст</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
} else {
|
} else {
|
||||||
data.events.forEach((e: Event, index: number) => {
|
data.events.forEach((e: Event, index: number) => {
|
||||||
const num = index + 1;
|
const num = index + 1;
|
||||||
@@ -514,7 +546,6 @@ function initDataPaginationControls(): void {
|
|||||||
|
|
||||||
let paginationContainer = document.getElementById('dataPaginationContainer');
|
let paginationContainer = document.getElementById('dataPaginationContainer');
|
||||||
if (!paginationContainer) {
|
if (!paginationContainer) {
|
||||||
// Ищем место для вставки (перед таблицей)
|
|
||||||
const tableContainer = document.getElementById('dataTableContainer');
|
const tableContainer = document.getElementById('dataTableContainer');
|
||||||
if (!tableContainer) return;
|
if (!tableContainer) return;
|
||||||
|
|
||||||
@@ -535,7 +566,6 @@ function initDataPaginationControls(): void {
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Вставляем перед таблицей
|
|
||||||
dataTab.insertBefore(paginationContainer, tableContainer);
|
dataTab.insertBefore(paginationContainer, tableContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -648,8 +678,27 @@ function initTabs(): void {
|
|||||||
const targetTab = document.getElementById(`tab-${tabName}`);
|
const targetTab = document.getElementById(`tab-${tabName}`);
|
||||||
if (targetTab) targetTab.classList.add('active');
|
if (targetTab) targetTab.classList.add('active');
|
||||||
|
|
||||||
if (tabName === 'events' && state.currentEvents.length === 0) {
|
// ===== УПРАВЛЕНИЕ БОКОВОЙ ПАНЕЛЬЮ =====
|
||||||
void loadEvents(1);
|
if (tabName === 'data') {
|
||||||
|
// Показываем боковую панель для вкладки "Данные"
|
||||||
|
toggleSidebar(true);
|
||||||
|
// Если данные ещё не загружены, загружаем
|
||||||
|
if (!state.currentData) {
|
||||||
|
const firstFile = document.querySelector('.log-file-item');
|
||||||
|
if (firstFile) {
|
||||||
|
const fileName = firstFile.querySelector('.file-name')?.textContent;
|
||||||
|
if (fileName) {
|
||||||
|
void loadFileData(fileName, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (tabName === 'events') {
|
||||||
|
// Скрываем боковую панель для вкладки "События"
|
||||||
|
toggleSidebar(false);
|
||||||
|
// Загружаем события, если их нет
|
||||||
|
if (state.currentEvents.length === 0) {
|
||||||
|
void loadEvents(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -658,6 +707,9 @@ function initTabs(): void {
|
|||||||
async function init(): Promise<void> {
|
async function init(): Promise<void> {
|
||||||
console.log('[Logs] Инициализация...');
|
console.log('[Logs] Инициализация...');
|
||||||
|
|
||||||
|
// По умолчанию показываем боковую панель
|
||||||
|
toggleSidebar(true);
|
||||||
|
|
||||||
initDataPaginationControls();
|
initDataPaginationControls();
|
||||||
initEventsPaginationControls();
|
initEventsPaginationControls();
|
||||||
initTabs();
|
initTabs();
|
||||||
@@ -669,9 +721,15 @@ async function init(): Promise<void> {
|
|||||||
void updateTime();
|
void updateTime();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
|
// Проверяем, какая вкладка активна при загрузке
|
||||||
|
const dataTab = document.getElementById('tab-data');
|
||||||
const eventsTab = document.getElementById('tab-events');
|
const eventsTab = document.getElementById('tab-events');
|
||||||
|
|
||||||
if (eventsTab && eventsTab.classList.contains('active')) {
|
if (eventsTab && eventsTab.classList.contains('active')) {
|
||||||
|
toggleSidebar(false);
|
||||||
await loadEvents(1);
|
await loadEvents(1);
|
||||||
|
} else {
|
||||||
|
toggleSidebar(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[Logs] Инициализация завершена');
|
console.log('[Logs] Инициализация завершена');
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
<div class="logs-container">
|
<div class="logs-container">
|
||||||
<!-- Боковая панель с файлами -->
|
<!-- Боковая панель с файлами -->
|
||||||
<div class="logs-sidebar">
|
<div class="logs-sidebar" id="logsSidebar">
|
||||||
<h3>Файлы данных</h3>
|
<h3>Файлы данных</h3>
|
||||||
<div id="fileList"></div>
|
<div id="fileList"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user