<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Экспорт записей</title> <link rel="stylesheet" href="../styles/main.css" /> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" /> <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script> </head> <body> <header> <h1>Аргус</h1> <h2><span>/</span> {{Organisation}}</h2> </header> <section class="account-info"> <div id="account-main"> <img id="person" src="../img/person.svg"> <span>{{User}}</span> <img id="down" src="../img/down.svg"> <img id="up" src="../img/up.svg"> </div> <a href="/login"><div id="account-additional" class="additional">Выйти</div></a> </section> <section class="navigation"> <a href="/"> <div><img src="../img/chart.svg">Главная</div> </a> <a href="/devices"> <div><img src="../img/cloud.svg">Устройства</div> </a> <a href="/reports"> <div><img src="../img/bubble.svg">Отчёты</div> </a> <a href="/live"> <div><img src="../img/waves.svg">Трансляция</div> </a> <a href="/videos"> <div class="selected"><img src="../img/play.svg">Записи</div> </a> <a class="settings" href="/settings"> <div><img src="../img/gear.svg">Настройки</div> </a> </section> <section class="main"> {{#if ifDBError}} <section class="dberror"> <div class="erorr-container"> <img src="../img/warning.svg"> <br> <h1>Ошибка </h1> <br> <span>Не удалось получить данные из БД</span> <button type="button" onclick="location.reload();">Повторить попытку</button> </div> </section> {{/if}} <div class="name"> <span>Управление записями</span> </div> <nav> <a href="/videos">Воспроизведение</a> <a class="selected" href="/videos/export">Экспорт</a> </nav> <section class="bg"> <section class="content"> <section class="for-table"> <section class="organisation"> <h1>Организация</h1> <ul class="area"> <li class="area-name"><img src="../img/ul.svg"><input type="checkbox" id="name-1" class="checkbox-input" hidden checked><label for="name-1" class="checkbox-label">Группа 1</label> <ul class="area-devices" id="devices-1"> {{#each Registrars}} <li class="device"> <img> <input type="radio" name="camera-serial" id="radio-{{this.serial}}" class="radio-input" value="{{this.serial}}" hidden> <label for="radio-{{this.serial}}" class="radio-label"> {{this.serial}} </label> </li> {{/each}} </ul> </li> </ul> </section> <section class="table" style="position: relative;"> <div class="map"> <div id="properties" class="properties" style="display: none;"> <div class="propert"><h1>Группа</h1><br><h2 id="propert-group">Автобусы</h2></div> <div class="propert"><h1>Скорость</h1><br><h2 id="propert-speed"> км/ч</h2></div> <div class="propert"><h1>Номерной знак</h1><br><h2 id="propert-plate"></h2></div> <div class="propert"><h1>Геопозиция</h1><br><h2 id="propert-geo"></h2></div> </div> <div id="map"></div> </div> <div class="cameras"> <div class="video-container"> <div id="camera-1" onclick="playVideo(1);"> <img src="../../img/play-circle.svg"> <span>Экспортировать 1 камеру</span> </div> <div id="camera-2" onclick="playVideo(2);"> <img src="../../img/play-circle.svg"> <span>Экспортировать 2 камеру</span> </div> <div id="camera-3" onclick="playVideo(3);"> <img src="../../img/play-circle.svg"> <span>Экспортировать 3 камеру</span> </div> <div id="camera-4" onclick="playVideo(4);"> <img src="../../img/play-circle.svg"> <span>Экспортировать 4 камеру</span> </div> <div id="camera-5" onclick="playVideo(5);"> <img src="../../img/play-circle.svg"> <span>Экспортировать 5 камеру</span> </div> <div id="camera-6" onclick="playVideo(6);"> <img src="../../img/play-circle.svg"> <span>Экспортировать 6 камеру</span> </div> <div id="camera-7" onclick="playVideo(7);"> <img src="../../img/play-circle.svg"> <span>Экспортировать 7 камеру</span> </div> <div id="camera-8" onclick="playVideo(8);"> <img src="../../img/play-circle.svg"> <span>Экспортировать 8 камеру</span> </div> <div id="camera-9" onclick="playVideo(9);"> <img src="../../img/play-circle.svg"> <span>Экспортировать 9 камеру</span> </div> <div id="camera-10" onclick="playVideo(10);"> <img src="../../img/play-circle.svg"> <span>Экспортировать 10 камеру</span> </div> <div id="camera-11" onclick="playVideo(11);"> <img src="../../img/play-circle.svg"> <span>Экспортировать 11 камеру</span> </div> <div id="camera-12" onclick="playVideo(12);"> <img src="../../img/play-circle.svg"> <span>Экспортировать 12 камеру</span> </div> </div> <div class="video-container-right"> <div id="camera-13" onclick="playVideo(13);"> <img src="../../img/play-circle.svg"> <span>Экспортировать 13 камеру</span> </div> <div id="camera-14" onclick="playVideo(14);"> <img src="../../img/play-circle.svg"> <span>Экспортировать 14 камеру</span> </div> <div id="camera-15" onclick="playVideo(15);"> <img src="../../img/play-circle.svg"> <span>Экспортировать 15 камеру</span> </div> <div id="camera-16" onclick="playVideo(16);"> <img src="../../img/play-circle.svg"> <span>Экспортировать 16 камеру</span> </div> </div> </div> <div class="calendar"> <div class="calendar-header"> <button id="prevMonth"></button> <h2 id="monthYear"></h2> <button id="nextMonth"></button> </div> <div class="daysOfWeek"> <div>Пн</div> <div>Вт</div> <div>Ср</div> <div>Чт</div> <div>Пт</div> <div>Сб</div> <div>Вс</div> </div> <div class="dates" id="dates"> </div> </div> <input type="hidden" id="selectedDate" name="selectedDate" hidden> <div class="speedometr"> <div style="display: none;" id="speed-bg" class="speed-bg"> <h1>Данных для выбранного периода нет.</h1> </div> <h1>Скорость</h1> <span>км/ч</span> <span style="float: right; font-size: 18px;">1 ч</span> <div> <canvas id="speed"></canvas> </div> </div> <div class="video-time"> <span style="margin-left: 15px;">с</span> <input name="videoTime" type="time" id="video-time" step="1"> <span>до</span> <input name="videoEndTime" type="time" id="video-end-time" step="1"> </div> </section> </section> </section> </section> </section> <style> .table { background-color:rgba(0, 0, 0, 0.02) !important; } .speedometr { position: absolute; right: 0; bottom: 62px; width: calc(75% - 300px - 10px - 32px); height: 220px; border-radius: 15px; background-color: white; padding-right: 32px; } .speedometr div { height: 200px; } </style> <script src="../scripts/jquery.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> function combineDateTime(dateString, timeString) { const date = new Date(dateString); const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, "0"); const day = String(date.getDate()).padStart(2, "0"); // Разбиение времени на часы, минуты и секунды const timeParts = timeString.split(":"); const hours = timeParts[0].padStart(2, "0"); const minutes = timeParts[1].padStart(2, "0"); const seconds = timeParts[2].padStart(2, "0"); // Собираем дату и время в нужном формате const combinedDateTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; return combinedDateTime; } let HasData; let recordID; var selectedChannel = 1; async function sendPostRequest() { // Получение данных из полей ввода const selectedDate = document.getElementById("selectedDate").value; const videoTime = document.getElementById("video-time").value; const videoEndTime = document.getElementById("video-end-time").value; const selectedSerial = document.querySelector('input[name="camera-serial"]:checked').value; // Объединяем дату и время и преобразуем в нужный формат const combinedDateTime = combineDateTime(selectedDate, videoTime); const response = await fetch(`/getData?serial=${selectedSerial}&selectedDate=${formatDate(selectedDate)}&selectedTime=${formatTime(videoTime)}&selectedChannel=${selectedChannel}`); const data = await response.json(); HasData = data.success; if (data.success) { console.log(`Данные доступны. DATAID: ${data.dataId}`) recordID = data.dataId; const requestData = { serial: selectedSerial, datetime: combinedDateTime, }; fetch("/getspeedarchive", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(requestData), }) .then((response) => response.json()) .then((data) => { const existingChart = Chart.getChart("speed"); if (existingChart) { existingChart.destroy(); } const numberOfLabels = data.speeds.length; const labels = Array.from({ length: numberOfLabels }, () => ""); // Обновление данных графика const chart = new Chart("speed", { type: "line", data: { labels: labels, datasets: [ { label: "Скорость", borderColor: "#8086F9", fill: false, data: data.speeds, pointStyle: false, pointRadius: 25, pointHoverRadius: 25, tension: 0.1, }, ], }, options: { plugins: { legend: { display: false, }, }, labelStep: "3", responsive: true, maintainAspectRatio: false, scales: { y: { stacked: true, grid: { display: true, color: "#D9D9D9", }, ticks: { stepSize: 10, }, }, x: { grid: { display: false, }, }, }, }, }); const geoData = data.geo; // Очищаем все слои на карте map.eachLayer(layer => { if (layer !== map) { map.removeLayer(layer); } }); // Добавляем слой с плитками OpenStreetMap L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}').addTo(map); // Создаем слой для маршрута const routeLayer = L.layerGroup().addTo(map); // Создаем слой для маркеров const markerLayer = L.layerGroup().addTo(map); // Преобразуем координаты точек маршрута const routePoints = geoData.map(point => [point.latitude, point.longitude]); // Создаем линию маршрута const route = L.polyline(routePoints, { color: 'red',weight: 5 }).addTo(routeLayer); }) .catch((error) => { console.error("Ошибка при отправке запроса:", error); }); const endResponse = await fetch(`/getData?serial=${selectedSerial}&selectedDate=${formatDate(selectedDate)}&selectedTime=${formatTime(videoEndTime)}&selectedChannel=${selectedChannel}`); const endData = await endResponse.json(); if (endData.success) { console.log(`Конечные данные доступны. DATAID: ${endData.dataId}`) const speedBG = document.getElementById("speed-bg"); speedBG.style.display = 'none'; HasData = true; if (data.dataId != endData.dataId) { HasData = false; var h1Element = document.querySelector('.speedometr h1'); h1Element.textContent = 'Временной диапазон находится в разных видео сессиях. Измените время.'; const speedBG = document.getElementById("speed-bg"); speedBG.style.display = 'flex'; } } else { HasData = false; const speedBG = document.getElementById("speed-bg"); speedBG.style.display = 'flex'; var h1Element = document.querySelector('.speedometr h1'); h1Element.textContent = 'Данных для выбранного периода нет.'; } } else { var h1Element = document.querySelector('.speedometr h1'); h1Element.textContent = 'Данных для выбранного периода нет.'; const speedBG = document.getElementById("speed-bg"); speedBG.style.display = 'flex'; } } const radioInputs = document.querySelectorAll(".radio-input"); radioInputs.forEach((input) => { input.addEventListener("change", sendPostRequest); }); const selectedDateInput = document.getElementById("selectedDate"); selectedDateInput.addEventListener("change", sendPostRequest); const videoTimeInput = document.getElementById("video-time"); videoTimeInput.addEventListener("change", sendPostRequest); const endVideoTimeInput = document.getElementById("video-end-time"); endVideoTimeInput.addEventListener("change", sendPostRequest); </script> <script> var now = new Date(); now.setHours(now.getHours() - 1); var formattedTime = now.toISOString().substr(11, 8); document.getElementById("video-time").value = formattedTime; const startTimeInput = document.getElementById('video-time'); const endTimeInput = document.getElementById('video-end-time'); const startTime = new Date(`1970-01-01T${startTimeInput.value}Z`); startTime.setHours(startTime.getHours() - 3); startTime.setSeconds(startTime.getSeconds() + 10); const hours = startTime.getHours().toString().padStart(2, '0'); const minutes = startTime.getMinutes().toString().padStart(2, '0'); const seconds = startTime.getSeconds().toString().padStart(2, '0'); const endTimeString = `${hours}:${minutes}:${seconds}`; endTimeInput.value = endTimeString; </script> <script> document.addEventListener("DOMContentLoaded", function () { const prevMonthBtn = document.getElementById("prevMonth"); const nextMonthBtn = document.getElementById("nextMonth"); const monthYear = document.getElementById("monthYear"); const datesContainer = document.getElementById("dates"); const dateForm = document.getElementById("dateForm"); const selectedDateInput = document.getElementById("selectedDate"); let currentDate = new Date(); let selectedDate = currentDate; selectedDateInput.value = selectedDate.toISOString(); function renderCalendar() { // Очистить предыдущий календарь datesContainer.innerHTML = ""; // Установить заголовок с месяцем и годом const options = { year: "numeric", month: "long" }; const formattedDate = currentDate.toLocaleDateString("ru-RU", options); const formattedMonthYear = formattedDate.charAt(0).toUpperCase() + formattedDate.slice(1); monthYear.textContent = formattedMonthYear.replace('г.', ''); // Найти первый день текущего месяца const firstDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1); // Определить день недели, с которого начнется месяц const startingDay = firstDayOfMonth.getDay() - 1; // Определить количество дней в текущем месяце const daysInMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0).getDate(); // Создать дни месяца for (let i = 0; i < startingDay; i++) { const emptyDay = document.createElement("div"); emptyDay.classList.add("empty-day"); datesContainer.appendChild(emptyDay); } for (let day = 1; day <= daysInMonth; day++) { const dateCell = document.createElement("div"); dateCell.textContent = day; dateCell.classList.add("date"); if (selectedDate.getDate() === day && selectedDate.getMonth() === currentDate.getMonth() && selectedDate.getFullYear() === currentDate.getFullYear()) { dateCell.classList.add("selected"); } dateCell.addEventListener("click", () => { selectedDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), day); renderCalendar(); selectedDateInput.value = selectedDate.toISOString(); sendPostRequest(); }); datesContainer.appendChild(dateCell); } } // Перейти на предыдущий месяц prevMonthBtn.addEventListener("click", () => { currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1); renderCalendar(); }); // Перейти на следующий месяц nextMonthBtn.addEventListener("click", () => { currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1); renderCalendar(); }); // Инициализировать календарь renderCalendar(); }); </script> <script> function formatDate(selectedDate) { const date = new Date(selectedDate); const year = date.getFullYear(); const month = (date.getMonth() + 1).toString().padStart(2, '0'); const day = date.getDate().toString().padStart(2, '0'); return `${year}${month}${day}`; } function formatTime(videoTime) { const parts = videoTime.split(':'); const hours = parts[0].toString().padStart(2, '0'); const minutes = parts[1].toString().padStart(2, '0'); const seconds = parts[2].toString().padStart(2, '0'); return `${hours}${minutes}${seconds}`; } async function playVideo(channel) { const selectedDevice = document.querySelector('input[name="camera-serial"]:checked'); if (!selectedDevice) { alert('Пожалуйста, выберите устройство из списка.'); return; } if (!HasData) { alert('Пожалуйста, выберите другой временной период.'); return; } if (videoTimeInput.value > endVideoTimeInput.value) { alert('Конечное время не может быть больше начального.'); return; } const startTimeInput = document.getElementById('video-time'); const endTimeInput = document.getElementById('video-end-time'); const selectedDateInput = document.getElementById('selectedDate'); const startTime = formatTime(startTimeInput.value); const endTime = formatTime(endTimeInput.value); const selectedDate = formatDate(selectedDateInput.value); selectedChannel = channel; const reqDate = document.getElementById("selectedDate").value; const reqTime = document.getElementById("video-time").value; const reqSerial = document.querySelector('input[name="camera-serial"]:checked').value; const finalResponse = await fetch(`/getData?serial=${reqSerial}&selectedDate=${formatDate(reqDate)}&selectedTime=${formatTime(reqTime)}&selectedChannel=${selectedChannel}`); const resData = await finalResponse.json(); const serial = selectedDevice.value; const url = `http://localhost:8081/export?url=http%3A%2F%2Fkrbl.ru%3A8080%2Fhttp%2Fdownload.flv%3Fserial%3D${serial}%26channel%3D${channel}%26queryTime%3D${selectedDate}%26startTime%3D${startTime}%26endTime%3D${endTime}%26recordID%3D${resData.dataId}`; window.open(url, '_blank'); } </script> <script> window.addEventListener('DOMContentLoaded', function() { var mapContainer = document.querySelector('.map'); var mapArea = document.getElementById('map'); mapArea.style.height = (mapContainer.clientHeight) + 'px'; mapArea.style.width = (mapContainer.clientWidth) + 'px'; }); window.addEventListener("resize", function (event) { var mapContainer = document.querySelector('.map'); var mapArea = document.getElementById('map'); mapArea.style.height = (mapContainer.clientHeight) + 'px'; mapArea.style.width = (mapContainer.clientWidth) + 'px'; }); var startPoint = [0, 0]; var endPoint = [0, 0]; let map; // Создаем карту Leaflet map = L.map('map').setView([59.855198, 30.282995], 10); // Добавляем базовый слой OpenStreetMap L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}').addTo(map); // Создаем маркеры var startMarker = L.marker(startPoint, { icon: L.divIcon({ className: 'custom-icon', html: '<div style="background-color: red; width: 14px; height: 14px; border: 2px solid white; border-radius: 50%;"></div>' }) }).addTo(map); var endMarker = L.marker(endPoint, { icon: L.divIcon({ className: 'custom-icon', html: '<div style="background-color: red; width: 14px; height: 14px; border: 2px solid white; border-radius: 50%;"></div>' }) }).addTo(map); // Создаем линию маршрута var route = L.polyline([startPoint, endPoint], { color: 'red', weight: 6 }).addTo(map); // Скрыть кнопки приближения/отдаления map.zoomControl.remove(); // Скрыть информационную панель map.attributionControl.remove(); </script> <script> // Скрытие/Показ дополнительных меню аккаунта const accountMain = document.getElementById('account-main'); const accountAdditional = document.getElementById('account-additional'); const accountUp = document.getElementById('up'); const accountDown = document.getElementById('down'); accountAdditional.style.display = 'none'; accountUp.style.display = 'none'; accountMain.addEventListener('click', () => { if (accountAdditional.style.display === 'none') { accountAdditional.style.display = 'flex'; accountUp.style.display = 'unset'; accountDown.style.display = 'none'; } else { accountAdditional.style.display = 'none'; accountUp.style.display = 'none'; accountDown.style.display = 'unset'; } }); </script> <script> Chart.defaults.color = "rgba(0, 0, 0, 0.4)"; Chart.defaults.font.size = 15; Chart.defaults.font.weight = 400; var speedData = { labels: [ "", "", "", "", "", "", "", "", "", "", "", ], datasets: [ { label: "Скорость", borderColor: "#8086F9", fill: false, data: [ {{Speeds}} ], pointStyle: false, pointRadius: 25, pointHoverRadius: 25, tension: 0.1, }, ], }; var speedOptions = { plugins: { legend: { display: false, }, }, labelStep: "3", responsive: true, maintainAspectRatio: false, scales: { y: { stacked: true, grid: { display: true, color: "#D9D9D9", }, ticks: { stepSize: 10, }, }, x: { grid: { display: false, }, }, }, }; new Chart("speed", { type: "line", data: speedData, options: speedOptions, }); </script> </body> </html>