// DOM элементы const uploadArea = document.getElementById('uploadArea'); const fileInput = document.getElementById('fileInput'); const selectFileBtn = document.getElementById('selectFileBtn'); const previewArea = document.getElementById('previewArea'); const previewImg = document.getElementById('previewImg'); const removeImageBtn = document.getElementById('removeImageBtn'); const predictBtn = document.getElementById('predictBtn'); const resultsArea = document.getElementById('resultsArea'); const loading = document.getElementById('loading'); const errorMessage = document.getElementById('errorMessage'); let currentFile = null; // API базовый URL const API_URL = window.location.origin + '/api/v1'; // Обработчики событий selectFileBtn.addEventListener('click', () => fileInput.click()); removeImageBtn.addEventListener('click', clearImage); predictBtn.addEventListener('click', predictImage); fileInput.addEventListener('change', handleFileSelect); // Drag & Drop uploadArea.addEventListener('dragover', (e) => { e.preventDefault(); uploadArea.classList.add('drag-over'); }); uploadArea.addEventListener('dragleave', () => { uploadArea.classList.remove('drag-over'); }); uploadArea.addEventListener('drop', (e) => { e.preventDefault(); uploadArea.classList.remove('drag-over'); const file = e.dataTransfer.files[0]; if (file && file.type.startsWith('image/')) { handleFile(file); } else { showError('Пожалуйста, загрузите изображение'); } }); uploadArea.addEventListener('click', () => fileInput.click()); function handleFileSelect(e) { const file = e.target.files[0]; if (file) { handleFile(file); } } function handleFile(file) { // Проверка размера (10 MB) if (file.size > 10 * 1024 * 1024) { showError('Файл слишком большой. Максимум 10 MB'); return; } currentFile = file; // Предпросмотр const reader = new FileReader(); reader.onload = (e) => { previewImg.src = e.target.result; uploadArea.style.display = 'none'; previewArea.style.display = 'block'; resultsArea.style.display = 'none'; hideError(); }; reader.readAsDataURL(file); } function clearImage() { currentFile = null; fileInput.value = ''; previewArea.style.display = 'none'; uploadArea.style.display = 'block'; resultsArea.style.display = 'none'; hideError(); } async function predictImage() { if (!currentFile) { showError('Сначала выберите изображение'); return; } // Показываем загрузку loading.style.display = 'block'; resultsArea.style.display = 'none'; hideError(); // Создаем FormData const formData = new FormData(); formData.append('file', currentFile); try { // Отправляем запрос const response = await fetch(`${API_URL}/predict`, { method: 'POST', body: formData }); const data = await response.json(); if (!response.ok) { throw new Error(data.error?.message || 'Ошибка при обработке'); } // Отображаем результаты displayResults(data.data); } catch (error) { console.error('Error:', error); showError(error.message || 'Ошибка при отправке запроса'); } finally { loading.style.display = 'none'; } } function displayResults(data) { const resultClass = document.getElementById('resultClass'); const resultConfidence = document.getElementById('resultConfidence'); const probabilitiesDiv = document.getElementById('probabilities'); // Определяем эмодзи для класса let emoji = ''; if (data.class === 'pered') emoji = '🚂 Передняя часть'; else if (data.class === 'zad') emoji = '🚂 Задняя часть'; else emoji = '⭕ Вагон не обнаружен'; // Отображаем основной результат resultClass.innerHTML = `${emoji}
${data.class_name}`; resultConfidence.innerHTML = `Уверенность: ${(data.confidence * 100).toFixed(1)}%`; // Отображаем все вероятности probabilitiesDiv.innerHTML = '

Распределение вероятностей:

'; const classNames = { 'pered': 'Передняя часть', 'zad': 'Задняя часть', 'none': 'Вагон не обнаружен' }; for (const [cls, prob] of Object.entries(data.probabilities)) { const percent = (prob * 100).toFixed(1); const isPredicted = cls === data.class; const probItem = document.createElement('div'); probItem.className = 'prob-item'; probItem.innerHTML = `
${classNames[cls] || cls}
${percent}%
`; probabilitiesDiv.appendChild(probItem); } // Показываем результаты resultsArea.style.display = 'block'; // Прокручиваем к результатам resultsArea.scrollIntoView({ behavior: 'smooth' }); } function showError(message) { errorMessage.textContent = message; errorMessage.style.display = 'block'; setTimeout(() => { errorMessage.style.display = 'none'; }, 5000); } function hideError() { errorMessage.style.display = 'none'; } // Проверка здоровья API при загрузке async function checkHealth() { try { const response = await fetch(`${API_URL}/health`); const data = await response.json(); if (data.status === 'healthy') { console.log('✅ API готов к работе'); } else { console.warn('⚠️ API не здоров:', data); showError('Сервис временно недоступен'); } } catch (error) { console.error('❌ API недоступен:', error); showError('Не удалось подключиться к серверу'); } } // Запускаем проверку при загрузке checkHealth();