841 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			841 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <!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">
 | ||
|       <section style="display: none;" class="dberror" id="exportLoading" >
 | ||
|         <div class="erorr-container">
 | ||
|           <div id="loader" class="loader">
 | ||
|             <div class="square" id="sq1"></div>
 | ||
|             <div class="square" id="sq2"></div>
 | ||
|             <div class="square" id="sq3"></div>
 | ||
|             <div class="square" id="sq4"></div>
 | ||
|             <div class="square" id="sq5"></div>
 | ||
|             <div class="square" id="sq6"></div>
 | ||
|             <div class="square" id="sq7"></div>
 | ||
|             <div class="square" id="sq8"></div>
 | ||
|             <div class="square" id="sq9"></div>
 | ||
|           </div>
 | ||
|                 <h1>Подготовка видео</h1> <br>
 | ||
|                 <span id="status">Пожалуйста, подождите..</span>
 | ||
|         </div>
 | ||
|     </section>
 | ||
|       {{#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 style="min-height: 800px;" class="for-table">
 | ||
| 
 | ||
|                     <section class="organisation">
 | ||
|                         <h1>Организация</h1>
 | ||
| 
 | ||
|                         <ul class="area">
 | ||
|                           {{#each Groups}}
 | ||
|                           <li class="area-name"><img src="../img/ul.svg"><input type="checkbox" id="{{name}}" class="checkbox-input" hidden checked><label for="{{name}}" class="checkbox-label">{{name}}</label>
 | ||
|                             <ul class="area-devices" id="devices-1">
 | ||
|                               {{#each devices}}
 | ||
|                               <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>
 | ||
|                           {{/each}}
 | ||
|                           </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 id="cameras" 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>
 | ||
|       document.addEventListener('DOMContentLoaded', function () {
 | ||
|         const areaNames = document.querySelectorAll('.area-name');
 | ||
|       
 | ||
|         areaNames.forEach(function (areaName) {
 | ||
|           const areaCheckbox = areaName.querySelector('.checkbox-input');
 | ||
|           const deviceCheckboxes = areaName.querySelectorAll('.device .checkbox-input');
 | ||
|           const deviceList = areaName.querySelector('.area-devices');
 | ||
|       
 | ||
|           // Функция для скрытия/показа дочерних элементов
 | ||
|           function toggleChildDevices(show) {
 | ||
|             if (show) {
 | ||
|               deviceList.style.display = 'block';
 | ||
|             } else {
 | ||
|               deviceList.style.display = 'none';
 | ||
|             }
 | ||
|           }
 | ||
|       
 | ||
|           // Инициализация состояния чекбоксов и скрытия/показа дочерних элементов
 | ||
|           toggleChildDevices(areaCheckbox.checked);
 | ||
|           deviceCheckboxes.forEach(function (deviceCheckbox) {
 | ||
|             deviceCheckbox.checked = areaCheckbox.checked;
 | ||
|           });
 | ||
|       
 | ||
|           areaCheckbox.addEventListener('change', function () {
 | ||
|             const isChecked = areaCheckbox.checked;
 | ||
|             deviceCheckboxes.forEach(function (deviceCheckbox) {
 | ||
|               deviceCheckbox.checked = isChecked;
 | ||
|             });
 | ||
|             toggleChildDevices(isChecked);
 | ||
|           });
 | ||
|       
 | ||
|           deviceCheckboxes.forEach(function (deviceCheckbox) {
 | ||
|             deviceCheckbox.addEventListener('change', function () {
 | ||
|               const allUnchecked = Array.from(deviceCheckboxes).every(function (checkbox) {
 | ||
|                 return !checkbox.checked;
 | ||
|               });
 | ||
|       
 | ||
|               if (allUnchecked) {
 | ||
|                 areaCheckbox.checked = false;
 | ||
|                 toggleChildDevices(false);
 | ||
|               } else {
 | ||
|                 areaCheckbox.checked = true;
 | ||
|                 toggleChildDevices(true);
 | ||
|               }
 | ||
|             });
 | ||
|           });
 | ||
|         });
 | ||
|       });
 | ||
|       
 | ||
|       </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() {
 | ||
|   document.getElementById("cameras").style.opacity = "30%";
 | ||
|   // Получение данных из полей ввода
 | ||
|   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) => {
 | ||
| 
 | ||
|                       document.getElementById("cameras").style.opacity = "100%";
 | ||
| 
 | ||
|                       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) {
 | ||
|     document.getElementById("exportLoading").style.display = 'flex';
 | ||
|     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);
 | ||
| 
 | ||
|     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=${channel}`);
 | ||
| 
 | ||
|     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}`;
 | ||
|     document.getElementById("exportLoading").style.display = 'none';
 | ||
|     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> |