pdf export alarms, fix streams
This commit is contained in:
parent
dab08f3ef5
commit
34554e3b58
2713
package-lock.json
generated
2713
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -23,6 +23,7 @@
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"path": "^0.12.7",
|
||||
"pg": "^8.11.1",
|
||||
"puppeteer": "^21.1.1",
|
||||
"ssh2": "^1.14.0",
|
||||
"ws": "^8.13.0"
|
||||
}
|
||||
|
340
server.js
340
server.js
@ -12,6 +12,7 @@ const axios = require('axios');
|
||||
const moment = require('moment');
|
||||
const bodyParser = require('body-parser');
|
||||
const _ = require('lodash');
|
||||
const puppeteer = require('puppeteer');
|
||||
|
||||
|
||||
const storage = multer.diskStorage({
|
||||
@ -103,17 +104,17 @@ conn.on('error', function(err) {
|
||||
});
|
||||
|
||||
|
||||
// const DB_User = process.env.DB_USER;
|
||||
// const DB_Password = process.env.DB_PASSWORD;
|
||||
// const DB_Host = process.env.DB_HOST;
|
||||
// const DB_Port = process.env.DB_PORT;
|
||||
// const DB_Name = process.env.DB_NAME;
|
||||
const DB_User = process.env.DB_USER;
|
||||
const DB_Password = process.env.DB_PASSWORD;
|
||||
const DB_Host = process.env.DB_HOST;
|
||||
const DB_Port = process.env.DB_PORT;
|
||||
const DB_Name = process.env.DB_NAME;
|
||||
|
||||
const DB_User = "postgres";
|
||||
const DB_Password = process.env.POSTGRES_PASSWORD;
|
||||
const DB_Host = "postgres";
|
||||
const DB_Port = "5432";
|
||||
const DB_Name = "postgres";
|
||||
// const DB_User = "postgres";
|
||||
// const DB_Password = process.env.POSTGRES_PASSWORD;
|
||||
// const DB_Host = "postgres";
|
||||
// const DB_Port = "5432";
|
||||
// const DB_Name = "postgres";
|
||||
|
||||
async function index(req, res) {
|
||||
var templateData = {
|
||||
@ -876,6 +877,37 @@ app.get("/api/devices", async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
async function generatePDF(data) {
|
||||
const browser = await puppeteer.launch({ headless: "new" });
|
||||
const page = await browser.newPage();
|
||||
|
||||
const htmlTemplate = fs.readFileSync('static/templates/reports/pdf.html', 'utf-8');
|
||||
|
||||
const filledTemplate = htmlTemplate
|
||||
.replace(/{{Id}}/g, data.Id)
|
||||
.replace(/{{Organisation}}/g, data.Organisation)
|
||||
.replace(/{{Type}}/g, data.Type)
|
||||
.replace(/{{Speed}}/g, data.Speed)
|
||||
.replace(/{{Date}}/g, data.Date)
|
||||
.replace(/{{Serial}}/g, data.Serial)
|
||||
.replace(/{{Geo}}/g, data.Geo)
|
||||
.replace(/{{PrevLongitude}}/g, data.PrevLongitude)
|
||||
.replace(/{{PrevLatitude}}/g, data.PrevLatitude)
|
||||
.replace(/{{NextLongitude}}/g, data.NextLongitude)
|
||||
.replace(/{{NextLatitude}}/g, data.NextLatitude)
|
||||
.replace(/{{Speeds}}/g, data.Speeds);
|
||||
|
||||
await page.setContent(filledTemplate);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
await page.pdf({ path: 'report.pdf', format: 'A4' });
|
||||
|
||||
await browser.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
app.get('/reports/:id', async (req, res) => {
|
||||
const id = req.params.id;
|
||||
|
||||
@ -1109,6 +1141,30 @@ app.get('/reports/:id', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
let data = {
|
||||
Id: templateData.Id,
|
||||
Organisation: templateData.Organisation,
|
||||
Type: templateData.Type,
|
||||
Speed: templateData.Speed,
|
||||
Date: templateData.Date,
|
||||
Serial: templateData.Serial,
|
||||
Geo: templateData.Geo,
|
||||
PrevLongitude: templateData.PrevLongitude,
|
||||
PrevLatitude: templateData.PrevLatitude,
|
||||
NextLongitude: templateData.NextLongitude,
|
||||
NextLatitude: templateData.NextLatitude,
|
||||
Speeds: templateData.Speeds,
|
||||
};
|
||||
|
||||
generatePDF(data)
|
||||
.then(() => {
|
||||
console.log('PDF создан успешно.');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Ошибка при создании PDF:', error);
|
||||
});
|
||||
|
||||
// console.log(templateData);
|
||||
|
||||
const source = fs.readFileSync("static/templates/reports/report.html", "utf8");
|
||||
@ -1131,6 +1187,270 @@ app.get('/reports/:id', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/generate-pdf/:id', async (req, res) => {
|
||||
const id = req.params.id;
|
||||
|
||||
let templateData = {
|
||||
Organisation: "Название организации",
|
||||
User: "Тестовое Имя",
|
||||
ifDBError: false,
|
||||
Id: id,
|
||||
Type: "",
|
||||
Speed: "",
|
||||
Date: "",
|
||||
Serial: "",
|
||||
Geo: "",
|
||||
Latitude: "",
|
||||
Longitude: "",
|
||||
|
||||
DriverName: "",
|
||||
DriverPhone: "",
|
||||
DriverEmail: "",
|
||||
DriverLicense: "",
|
||||
|
||||
PrevLatitude: "",
|
||||
PrevLongitude: "",
|
||||
NextLatitude: "",
|
||||
NextLongitude: "",
|
||||
Speeds: "",
|
||||
};
|
||||
|
||||
try {
|
||||
const pool = new Pool({
|
||||
user: DB_User,
|
||||
host: DB_Host,
|
||||
database: DB_Name,
|
||||
password: DB_Password,
|
||||
port: DB_Port,
|
||||
});
|
||||
const client = await pool.connect();
|
||||
|
||||
const minuteInMillis = 90 * 1000;
|
||||
|
||||
const query = `
|
||||
WITH PrevNextGeo AS (
|
||||
SELECT
|
||||
a.serial,
|
||||
a.st,
|
||||
a.time,
|
||||
a.geoid,
|
||||
(SELECT g1.latitude FROM alarms a1 LEFT JOIN geo g1 ON a1.geoid = g1.id WHERE a1.evtuuid = a.evtuuid ORDER BY a1.time ASC LIMIT 1) AS prev_latitude,
|
||||
(SELECT g2.longitude FROM alarms a2 LEFT JOIN geo g2 ON a2.geoid = g2.id WHERE a2.evtuuid = a.evtuuid ORDER BY a2.time ASC LIMIT 1) AS prev_longitude,
|
||||
(SELECT g3.latitude FROM alarms a3 LEFT JOIN geo g3 ON a3.geoid = g3.id WHERE a3.evtuuid = a.evtuuid ORDER BY a3.time DESC LIMIT 1) AS next_latitude,
|
||||
(SELECT g4.longitude FROM alarms a4 LEFT JOIN geo g4 ON a4.geoid = g4.id WHERE a4.evtuuid = a.evtuuid ORDER BY a4.time DESC LIMIT 1) AS next_longitude,
|
||||
g.longitude,
|
||||
g.latitude,
|
||||
g.speed,
|
||||
d.name,
|
||||
d.surname,
|
||||
d.card,
|
||||
d.phone,
|
||||
d.email
|
||||
FROM alarms a
|
||||
LEFT JOIN geo g ON a.geoid = g.id
|
||||
LEFT JOIN drivers d ON a.serial = d.transport
|
||||
WHERE a.id = ${id}
|
||||
LIMIT 1
|
||||
),
|
||||
Speeds AS (
|
||||
SELECT
|
||||
g.speed,
|
||||
ROW_NUMBER() OVER (ORDER BY ABS(EXTRACT(EPOCH FROM (a.time - (SELECT time FROM PrevNextGeo)))) ASC) AS row_number
|
||||
FROM alarms a
|
||||
LEFT JOIN geo g ON a.geoid = g.id
|
||||
WHERE g.serial = (SELECT serial FROM PrevNextGeo) -- Ограничиваем результаты только записями с тем же serial
|
||||
)
|
||||
SELECT
|
||||
*,
|
||||
(
|
||||
SELECT array_agg(speed) FROM Speeds
|
||||
WHERE row_number <= 11
|
||||
) AS nearest_speeds
|
||||
FROM PrevNextGeo;
|
||||
`;
|
||||
|
||||
const alarm = (await client.query(query)).rows[0];
|
||||
// console.log(alarm);
|
||||
|
||||
function formatDate(date) {
|
||||
const options = {
|
||||
year: "2-digit",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
};
|
||||
const adjustedDate = new Date(date);
|
||||
adjustedDate.setHours(adjustedDate.getHours() - 3);
|
||||
|
||||
const formattedDate = adjustedDate.toLocaleString("ru-RU", options);
|
||||
return formattedDate.replace(",", "");
|
||||
}
|
||||
|
||||
let type;
|
||||
switch (alarm.st) {
|
||||
case "0":
|
||||
type = "Усталость";
|
||||
break;
|
||||
case "1":
|
||||
type = "Водитель пропал";
|
||||
break;
|
||||
case "2":
|
||||
type = "Разговор по телефону";
|
||||
break;
|
||||
case "3":
|
||||
type = "Курение за рулём";
|
||||
break;
|
||||
case "4":
|
||||
type = "Водитель отвлекся";
|
||||
break;
|
||||
case "5":
|
||||
type = "Выезд с полосы движения";
|
||||
break;
|
||||
case "6":
|
||||
type = "!!! Лобовое столкновение";
|
||||
break;
|
||||
case "7":
|
||||
type = "Скорость превышена";
|
||||
break;
|
||||
case "8":
|
||||
type = "Распознавание номерных знаков";
|
||||
break;
|
||||
case "9":
|
||||
type = "!! Маленькое расстояние спереди";
|
||||
break;
|
||||
case "10":
|
||||
type = "Водитель зевает";
|
||||
break;
|
||||
case "11":
|
||||
type = "!!! Столкновение с пешеходом";
|
||||
break;
|
||||
case "12":
|
||||
type = "Проходы переполнены";
|
||||
break;
|
||||
case "13":
|
||||
type = "!! Посадка/высадка вне остановки";
|
||||
break;
|
||||
case "14":
|
||||
type = "!! Смена полосы с нарушением ПДД";
|
||||
break;
|
||||
case "15":
|
||||
type = "! Включенный телефон у водителя";
|
||||
break;
|
||||
case "16":
|
||||
type = "!!! Ремень безопасности";
|
||||
break;
|
||||
case "17":
|
||||
type = "Проверка не удалась";
|
||||
break;
|
||||
case "18":
|
||||
type = "Слепые зоны справа";
|
||||
break;
|
||||
case "19":
|
||||
type = "!!! Заднее столкновение";
|
||||
break;
|
||||
case "20":
|
||||
type = "!!! Управление без рук";
|
||||
break;
|
||||
case "21":
|
||||
type = "!! Управление одной рукой";
|
||||
break;
|
||||
case "22":
|
||||
type = "Очки, блокирующие инфракрасное излучение";
|
||||
break;
|
||||
case "23":
|
||||
type = "Слепые зоны слева";
|
||||
break;
|
||||
case "24":
|
||||
type = "Помехи для пассажиров";
|
||||
break;
|
||||
case "25":
|
||||
type = "На перекрестке ограничена скорость";
|
||||
break;
|
||||
case "26":
|
||||
type = "Обнаружен перекресток";
|
||||
break;
|
||||
case "27":
|
||||
type = "Пешеходы на переходе";
|
||||
break;
|
||||
case "28":
|
||||
type = "! Неучтивое отношение к пешеходам";
|
||||
break;
|
||||
case "29":
|
||||
type = "Обнаружен пешеходный переход";
|
||||
break;
|
||||
case "30":
|
||||
type = "Водитель матерится";
|
||||
break;
|
||||
default:
|
||||
type = "Неизвестный тип";
|
||||
}
|
||||
|
||||
var actualSpeed;
|
||||
if (alarm.speed > 150) {
|
||||
actualSpeed = alarm.speed / 100
|
||||
} else {
|
||||
actualSpeed = alarm.speed
|
||||
}
|
||||
|
||||
templateData.Type = type;
|
||||
templateData.Speed = actualSpeed;
|
||||
templateData.Date = formatDate(alarm.time);
|
||||
templateData.Serial = alarm.serial;
|
||||
templateData.Geo = alarm.latitude + "," + alarm.longitude;
|
||||
templateData.Latitude = alarm.latitude
|
||||
templateData.Longitude = alarm.longitude
|
||||
|
||||
templateData.DriverName = alarm.name + " " + alarm.surname;
|
||||
templateData.DriverPhone = alarm.phone;
|
||||
templateData.DriverEmail = alarm.email;
|
||||
templateData.DriverLicense = alarm.card;
|
||||
|
||||
templateData.PrevLatitude = alarm.prev_latitude;
|
||||
templateData.PrevLongitude = alarm.prev_longitude;
|
||||
templateData.NextLatitude = alarm.next_latitude;
|
||||
templateData.NextLongitude = alarm.next_longitude;
|
||||
|
||||
templateData.Speeds = alarm.nearest_speeds
|
||||
templateData.Speeds = templateData.Speeds.map(speed => {
|
||||
if (speed > 150) {
|
||||
return speed / 100;
|
||||
} else {
|
||||
return speed;
|
||||
}
|
||||
});
|
||||
|
||||
let data = {
|
||||
Id: templateData.Id,
|
||||
Organisation: templateData.Organisation,
|
||||
Type: templateData.Type,
|
||||
Speed: templateData.Speed,
|
||||
Date: templateData.Date,
|
||||
Serial: templateData.Serial,
|
||||
Geo: templateData.Geo,
|
||||
PrevLongitude: templateData.PrevLongitude,
|
||||
PrevLatitude: templateData.PrevLatitude,
|
||||
NextLongitude: templateData.NextLongitude,
|
||||
NextLatitude: templateData.NextLatitude,
|
||||
Speeds: templateData.Speeds,
|
||||
};
|
||||
|
||||
generatePDF(data)
|
||||
.then(() => {
|
||||
console.log('PDF создан успешно.');
|
||||
res.sendFile(`${__dirname}/report.pdf`);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Ошибка при создании PDF:', error);
|
||||
res.status(500).send('Ошибка при создании PDF');
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).send('Ошибка при получении данных');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
async function devices(req, res) {
|
||||
let templateData = {
|
||||
Organisation: "Название организации",
|
||||
|
@ -1493,7 +1493,7 @@ input[type="datetime-local"] {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.report li {
|
||||
.report-info li {
|
||||
list-style-type: none;
|
||||
margin-bottom: 12px;
|
||||
font-weight: 500;
|
||||
@ -1503,7 +1503,7 @@ input[type="datetime-local"] {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.report li svg {
|
||||
.report-info li svg {
|
||||
text-align: center;
|
||||
margin-right: 20px;
|
||||
margin-bottom: -5px;
|
||||
|
@ -301,7 +301,7 @@
|
||||
type: 'flv',
|
||||
isLive: true,
|
||||
cors: true,
|
||||
url: `${baseURL}?serial=${serial}&channel=${i}&quality=1`,
|
||||
url: `${baseURL}?serial=${serial}&channel=${i}&quality=0`,
|
||||
}, {
|
||||
enableWorker: true,
|
||||
enableStashBuffer: false,
|
||||
@ -492,6 +492,7 @@ var markerObj = L.marker([latitude, longitude], { icon: marker });
|
||||
|
||||
propertiesDiv.style.display = 'inline-flex';
|
||||
} else {
|
||||
propertiesDiv.style.display = 'none';
|
||||
console.log('Устройство с выбранным серийным номером не найдено.');
|
||||
}
|
||||
})
|
||||
|
306
static/templates/reports/pdf.html
Normal file
306
static/templates/reports/pdf.html
Normal file
@ -0,0 +1,306 @@
|
||||
<!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>Отчёт {{Id}}</title>
|
||||
<!-- <link rel="stylesheet" href="../styles/main.css" /> -->
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/Modern-Technologies-SPB/site/static/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>
|
||||
|
||||
<style>
|
||||
body {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-bottom: 50px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<section style="margin-left: 0;" class="main">
|
||||
<div class="name">
|
||||
<span>Отчёт №{{Id}}</span>
|
||||
</div>
|
||||
<section style="height: 100% !important;" class="bg">
|
||||
<section style="position: relative;" class="content">
|
||||
|
||||
<div style="position: absolute; left: 2.5%; width: 95%; height: 275px; margin: 25px auto; border-radius: 30px; border: 2px solid rgba(245, 245, 250, 1);" id="map"></div>
|
||||
|
||||
|
||||
|
||||
<div style="position: absolute; left: 2.5%; width: 95%; height: 250px; margin: 25px auto; border-radius: 30px; border: 2px solid rgba(245, 245, 250, 1); top: 325px;" class="report-info">
|
||||
|
||||
<ul>
|
||||
<li><svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" fill="none" viewBox="0 0 19 17">
|
||||
<path fill="#000" fill-opacity=".75" d="M1.632 16.172c.741 0 1.203-.473 1.203-1.246V1.573C2.835.8 2.373.327 1.632.327.88.327.429.8.429 1.573v13.353c0 .773.45 1.246 1.203 1.246ZM6.896 16h4.264c4.62 0 7.337-2.879 7.337-7.777 0-4.888-2.728-7.724-7.337-7.724H6.896c-.752 0-1.204.473-1.204 1.246v13.009c0 .773.452 1.246 1.204 1.246Zm1.203-2.084V2.572h2.836c3.276 0 5.102 2.02 5.102 5.672 0 3.663-1.815 5.672-5.102 5.672H8.099Z"/>
|
||||
</svg>{{Id}}</li>
|
||||
<li><svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" fill="none" viewBox="0 0 21 23">
|
||||
<path fill="#000" fill-opacity=".75" d="M0 17.614c0 .813.614 1.347 1.656 1.347h4.63c.087 2.147 1.744 4.028 4.059 4.028 2.325 0 3.982-1.87 4.07-4.028h4.629c1.03 0 1.656-.534 1.656-1.346 0-1.113-1.119-2.115-2.062-3.105-.724-.768-.922-2.348-1.01-3.627-.076-4.385-1.195-7.211-4.113-8.28C13.142 1.147 11.968 0 10.345 0c-1.613 0-2.798 1.146-3.16 2.604-2.918 1.068-4.037 3.894-4.113 8.278-.088 1.28-.286 2.86-1.01 3.628C1.108 15.5 0 16.502 0 17.614Zm2.128-.333v-.134c.198-.323.856-.979 1.426-1.624.79-.89 1.163-2.326 1.262-4.496.088-4.862 1.514-6.41 3.39-6.932.274-.067.427-.2.438-.49.033-1.157.691-1.97 1.7-1.97 1.02 0 1.668.813 1.712 1.97.01.29.153.423.428.49 1.886.523 3.313 2.07 3.4 6.932.099 2.17.472 3.605 1.25 4.496.582.645 1.23 1.301 1.427 1.624v.134H2.128Zm5.869 1.68h4.706c-.088 1.513-1.031 2.459-2.358 2.459-1.317 0-2.271-.946-2.348-2.46Z"/>
|
||||
</svg>{{Type}}</li>
|
||||
<li><svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" fill="none" viewBox="0 0 23 23">
|
||||
<path fill="#000" fill-opacity=".75" d="M6.93 17.14c.609 0 1.105-.507 1.105-1.116 0-.608-.496-1.104-1.105-1.104-.608 0-1.104.496-1.104 1.104 0 .61.496 1.116 1.104 1.116ZM4.992 12.6c.62 0 1.116-.496 1.116-1.105a1.109 1.109 0 0 0-2.22 0c0 .609.507 1.105 1.104 1.105Zm1.916-4.53c.608 0 1.104-.508 1.104-1.105 0-.62-.496-1.104-1.104-1.104-.609 0-1.104.484-1.104 1.104 0 .597.495 1.105 1.104 1.105Zm4.575-1.984a1.12 1.12 0 0 0 1.104-1.104c0-.62-.507-1.116-1.104-1.116a1.109 1.109 0 0 0 0 2.22Zm6.491 6.514c.597 0 1.104-.496 1.104-1.105 0-.608-.507-1.104-1.104-1.104-.62 0-1.104.496-1.104 1.104 0 .609.484 1.105 1.104 1.105Zm-1.938 4.541c.608 0 1.104-.507 1.104-1.116 0-.608-.496-1.104-1.104-1.104-.609 0-1.105.496-1.105 1.104 0 .61.496 1.116 1.105 1.116ZM9.69 13.23c.913.89 2.164.687 2.919-.372l4.406-6.153c.406-.563-.191-1.15-.755-.755l-6.209 4.35c-1.07.744-1.262 2.006-.36 2.93Zm1.803 9.759c6.288 0 11.495-5.218 11.495-11.495C22.989 5.206 17.77 0 11.483 0 5.206 0 0 5.206 0 11.494 0 17.771 5.218 22.99 11.494 22.99Zm0-1.916a9.532 9.532 0 0 1-9.567-9.579c0-5.319 4.237-9.578 9.556-9.578 5.319 0 9.59 4.26 9.59 9.578a9.542 9.542 0 0 1-9.579 9.579Z"/>
|
||||
</svg>{{Speed}} км/ч</li>
|
||||
<li><svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" fill="none" viewBox="0 0 23 22">
|
||||
<path fill="#000" fill-opacity=".75" d="M3.624 21.269h15.764c2.412 0 3.612-1.2 3.612-3.577V3.6C23 1.223 21.8.023 19.388.023H3.624C1.212.023 0 1.212 0 3.601v14.09c0 2.39 1.212 3.578 3.624 3.578Zm-.173-1.858c-1.028 0-1.593-.542-1.593-1.616V6.913c0-1.062.565-1.616 1.593-1.616h16.087c1.027 0 1.604.554 1.604 1.616v10.882c0 1.074-.577 1.616-1.604 1.616H3.45Zm5.804-9.97h.681c.404 0 .531-.116.531-.52V8.24c0-.404-.127-.53-.53-.53h-.682c-.404 0-.542.126-.542.53v.68c0 .405.138.52.542.52Zm3.832 0h.68c.405 0 .543-.116.543-.52V8.24c0-.404-.138-.53-.542-.53h-.681c-.404 0-.543.126-.543.53v.68c0 .405.139.52.543.52Zm3.831 0h.681c.404 0 .543-.116.543-.52V8.24c0-.404-.139-.53-.543-.53h-.68c-.405 0-.532.126-.532.53v.68c0 .405.127.52.531.52ZM5.424 13.213h.67c.415 0 .542-.116.542-.52v-.68c0-.404-.127-.52-.543-.52h-.669c-.415 0-.542.116-.542.52v.68c0 .404.127.52.542.52Zm3.831 0h.681c.404 0 .531-.116.531-.52v-.68c0-.404-.127-.52-.53-.52h-.682c-.404 0-.542.116-.542.52v.68c0 .404.138.52.542.52Zm3.832 0h.68c.405 0 .543-.116.543-.52v-.68c0-.404-.138-.52-.542-.52h-.681c-.404 0-.543.116-.543.52v.68c0 .404.139.52.543.52Zm3.831 0h.681c.404 0 .543-.116.543-.52v-.68c0-.404-.139-.52-.543-.52h-.68c-.405 0-.532.116-.532.52v.68c0 .404.127.52.531.52ZM5.424 16.999h.67c.415 0 .542-.127.542-.53v-.682c0-.404-.127-.519-.543-.519h-.669c-.415 0-.542.115-.542.52v.68c0 .404.127.531.542.531Zm3.831 0h.681c.404 0 .531-.127.531-.53v-.682c0-.404-.127-.519-.53-.519h-.682c-.404 0-.542.115-.542.52v.68c0 .404.138.531.542.531Zm3.832 0h.68c.405 0 .543-.127.543-.53v-.682c0-.404-.138-.519-.542-.519h-.681c-.404 0-.543.115-.543.52v.68c0 .404.139.531.543.531Z"/>
|
||||
</svg>{{Date}}</li>
|
||||
<li><svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" fill="none" viewBox="0 0 24 19">
|
||||
<path fill="#000" fill-opacity=".75" d="M4.58 6.143c-.078.341.068.478.44.459 1.562-.108 3.418-.196 6.533-.196 3.125 0 4.98.088 6.543.196.361.02.508-.118.43-.46-.235-1.035-.684-2.382-.997-2.92-.254-.439-.537-.634-1.045-.702-.703-.098-2.304-.166-4.931-.166-2.617 0-4.219.068-4.932.166-.508.068-.79.263-1.045.703-.312.537-.752 1.884-.996 2.92Zm-.068 6.65c.888 0 1.572-.684 1.572-1.572 0-.899-.684-1.573-1.572-1.573-.889 0-1.573.674-1.573 1.573 0 .888.684 1.572 1.573 1.572Zm4.463-.39h5.166c.664 0 1.123-.46 1.123-1.133 0-.665-.46-1.123-1.123-1.123H8.975c-.674 0-1.133.458-1.133 1.123 0 .673.459 1.132 1.133 1.132Zm9.619.39c.898 0 1.572-.684 1.572-1.572 0-.899-.674-1.573-1.572-1.573-.889 0-1.563.674-1.563 1.573 0 .888.674 1.572 1.563 1.572Zm-7.041 2.637c3.281 0 7.646-.166 9.492-.381 1.328-.147 2.06-.88 2.06-2.13v-1.718c0-1.65-.332-2.568-1.23-3.74l-.83-1.065c-.352-1.757-1.006-3.603-1.338-4.326C19.18.967 18.174.313 16.875.137 16.221.049 14.082 0 11.553 0 9.033 0 6.895.059 6.24.137 4.941.293 3.926.967 3.408 2.07c-.342.723-.986 2.569-1.347 4.326l-.82 1.065C.331 8.633 0 9.55 0 11.2v1.719c0 1.25.742 1.982 2.06 2.129 1.856.215 6.211.38 9.493.38Zm0-1.28c-3.32 0-7.578-.156-9.15-.351-.83-.098-1.124-.527-1.124-1.27v-1.328c0-1.338.215-1.963.977-2.959l.996-1.308c.264-1.416.898-3.39 1.318-4.288.313-.673.928-1.093 1.817-1.2.625-.079 2.607-.167 5.166-.167 2.568 0 4.58.088 5.146.166.918.117 1.524.537 1.846 1.201.42.899 1.055 2.872 1.308 4.288l1.006 1.308c.752.996.967 1.621.967 2.96v1.327c0 .742-.293 1.172-1.123 1.27-1.562.195-5.83.351-9.15.351Zm-9.756 3.965h1.142c.733 0 1.3-.566 1.3-1.289v-2.324l-3.741-.537v2.861c0 .723.566 1.29 1.299 1.29Zm18.369 0h1.152a1.27 1.27 0 0 0 1.29-1.289v-2.861l-3.731.537v2.324a1.27 1.27 0 0 0 1.289 1.29Z"/>
|
||||
</svg>{{Serial}}</li>
|
||||
<li><svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" fill="none" viewBox="0 0 8 23">
|
||||
<path fill="#000" fill-opacity=".75" d="M0 3.947a3.913 3.913 0 0 0 2.952 3.81v9.715c0 3.089.555 4.774.974 4.774.429 0 .973-1.675.973-4.774V7.757a3.913 3.913 0 0 0 2.963-3.81A3.94 3.94 0 0 0 3.926 0C1.748 0 0 1.78 0 3.947Zm2.806.22c-.723 0-1.35-.629-1.35-1.361 0-.723.627-1.34 1.35-1.34.732 0 1.34.617 1.34 1.34 0 .732-.608 1.36-1.34 1.36Z"/>
|
||||
</svg>{{Geo}}</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; left: 2.5%; width: 95%; height: 200px; margin: 25px auto; border-radius: 30px; border: 2px solid rgba(245, 245, 250, 1); top: 625px;" class="speedometr">
|
||||
<h1>Скорость</h1>
|
||||
<span>км/ч</span>
|
||||
<div>
|
||||
<canvas id="speed"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
var prevLongitude = {{PrevLongitude}};
|
||||
var prevLatitude = {{PrevLatitude}};
|
||||
var nextLongitude = {{NextLongitude}};
|
||||
var nextLatitude = {{NextLatitude}};
|
||||
|
||||
var startPoint = [prevLatitude, prevLongitude];
|
||||
var endPoint = [nextLatitude, nextLongitude];
|
||||
|
||||
var map = L.map('map').setView(startPoint, 16);
|
||||
|
||||
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}').addTo(map);
|
||||
|
||||
var routeStyle = {
|
||||
color: 'red',
|
||||
weight: 6
|
||||
};
|
||||
|
||||
var routeCoordinates = [startPoint, endPoint];
|
||||
var route = L.polyline(routeCoordinates, routeStyle).addTo(map);
|
||||
|
||||
var markerStyle = {
|
||||
radius: 7,
|
||||
fillColor: 'red',
|
||||
color: 'white',
|
||||
weight: 2,
|
||||
opacity: 1,
|
||||
fillOpacity: 1
|
||||
};
|
||||
|
||||
var startMarker = L.circleMarker(startPoint, markerStyle).addTo(map);
|
||||
var endMarker = L.circleMarker(endPoint, markerStyle).addTo(map);
|
||||
|
||||
// Убрать кнопки приближения/отдаления
|
||||
map.zoomControl.remove();
|
||||
|
||||
// Убрать информационную панель
|
||||
map.attributionControl.remove();
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
function sizeMap() {
|
||||
var mapContainer = document.querySelector('.map');
|
||||
var mapArea = document.getElementById('map');
|
||||
mapArea.style.height = (mapContainer.clientHeight) + 'px';
|
||||
mapArea.style.width = (mapContainer.clientWidth) + 'px';
|
||||
}
|
||||
|
||||
// window.addEventListener('DOMContentLoaded', function() {
|
||||
// sizeMap();
|
||||
// });
|
||||
|
||||
window.addEventListener("resize", function (event) {
|
||||
sizeMap();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<script src="../scripts/jquery.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
|
||||
<script>
|
||||
const video = document.getElementById('my-video');
|
||||
const playPauseButton = document.getElementById('play-pause-btn');
|
||||
const progressBar = document.getElementById('progress-bar');
|
||||
const progress = document.getElementById('progress');
|
||||
const currentTimeDisplay = document.getElementById('current-time');
|
||||
const remainingTimeDisplay = document.getElementById('remaining-time');
|
||||
const playIcon = document.getElementById('play-icon');
|
||||
const pauseIcon = document.getElementById('pause-icon');
|
||||
|
||||
playPauseButton.addEventListener('click', togglePlayPause);
|
||||
progressBar.addEventListener('click', seek);
|
||||
|
||||
function togglePlayPause() {
|
||||
if (video.paused) {
|
||||
video.play();
|
||||
playIcon.style.display = 'none';
|
||||
pauseIcon.style.display = 'inline-block';
|
||||
} else {
|
||||
video.pause();
|
||||
playIcon.style.display = 'inline-block';
|
||||
pauseIcon.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function updateProgressBar() {
|
||||
const progressPercentage = (video.currentTime / video.duration) * 100;
|
||||
progress.style.width = `${progressPercentage}%`;
|
||||
|
||||
const currentTime = formatTime(video.currentTime);
|
||||
currentTimeDisplay.textContent = currentTime;
|
||||
|
||||
const remainingTime = formatTime(video.duration - video.currentTime);
|
||||
remainingTimeDisplay.textContent = `-${remainingTime}`;
|
||||
|
||||
if (video.duration === video.currentTime) {
|
||||
progress.style.borderRadius = '50px';
|
||||
playIcon.style.display = 'inline-block';
|
||||
pauseIcon.style.display = 'none';
|
||||
} else {
|
||||
progress.style.borderRadius = '50px 0 0 50px';
|
||||
}
|
||||
}
|
||||
|
||||
function seek(event) {
|
||||
const progressWidth = progressBar.clientWidth;
|
||||
const clickX = event.offsetX;
|
||||
const seekTime = (clickX / progressWidth) * video.duration;
|
||||
video.currentTime = seekTime;
|
||||
}
|
||||
|
||||
function formatTime(time) {
|
||||
const minutes = Math.floor(time / 60);
|
||||
const seconds = Math.floor(time % 60);
|
||||
const formattedTime = `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
|
||||
return formattedTime;
|
||||
}
|
||||
|
||||
video.addEventListener('timeupdate', updateProgressBar);
|
||||
|
||||
|
||||
</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>
|
@ -88,7 +88,7 @@
|
||||
<h1>Список предупреждений</h1>
|
||||
<div class="export">
|
||||
<img src="../img/share.svg">
|
||||
<span>Экспорт</span>
|
||||
<span onclick="generatePDF();">Экспорт</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -247,9 +247,6 @@
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<script src="../scripts/jquery.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
@ -339,6 +336,13 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
function generatePDF() {
|
||||
const id = {{Id}};
|
||||
window.open(`/generate-pdf/${id}`, '_blank');
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
Chart.defaults.color = "rgba(0, 0, 0, 0.4)";
|
||||
Chart.defaults.font.size = 15;
|
||||
|
Loading…
Reference in New Issue
Block a user