unlimited reports rows and pages with filters
This commit is contained in:
parent
fb3dcf80e3
commit
34bbbd0c1a
376
server.js
376
server.js
@ -885,6 +885,7 @@ async function reports(req, res) {
|
|||||||
ifDBError: false,
|
ifDBError: false,
|
||||||
Registrars: [],
|
Registrars: [],
|
||||||
Groups: [],
|
Groups: [],
|
||||||
|
Count: 0,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const pool = new Pool({
|
const pool = new Pool({
|
||||||
@ -927,7 +928,7 @@ async function reports(req, res) {
|
|||||||
FROM geo
|
FROM geo
|
||||||
) AS g ON a.serial = g.serial AND g.row_num = 1
|
) AS g ON a.serial = g.serial AND g.row_num = 1
|
||||||
ORDER BY a.time DESC
|
ORDER BY a.time DESC
|
||||||
LIMIT 100;
|
LIMIT 14;
|
||||||
`;
|
`;
|
||||||
const alarms = await client.query(query, templateData.isAdmin ? [] : [serialValues]);
|
const alarms = await client.query(query, templateData.isAdmin ? [] : [serialValues]);
|
||||||
|
|
||||||
@ -1104,6 +1105,27 @@ const formattedDate = `${("0" + day).slice(-2)}.${("0" + month).slice(-2)}.${yea
|
|||||||
numbers: groupedNumbers[groupName],
|
numbers: groupedNumbers[groupName],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
const countQueryText = `
|
||||||
|
SELECT COUNT(*) AS total
|
||||||
|
FROM (
|
||||||
|
SELECT DISTINCT a.evtuuid
|
||||||
|
FROM alarms AS a
|
||||||
|
LEFT JOIN registrars AS r ON a.serial = r.serial
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT *,
|
||||||
|
ROW_NUMBER() OVER (PARTITION BY serial ORDER BY ABS(EXTRACT(EPOCH FROM (time - NOW())))) AS row_num
|
||||||
|
FROM geo
|
||||||
|
) AS g ON a.serial = g.serial AND g.row_num = 1
|
||||||
|
WHERE a.alarmtype = 56
|
||||||
|
${!templateData.isAdmin ? 'AND a.serial = ANY($1)' : ''}
|
||||||
|
) AS unique_events
|
||||||
|
`;
|
||||||
|
|
||||||
|
const countResult = await pool.query(countQueryText, templateData.isAdmin ? [] : [serialValues]);
|
||||||
|
templateData.Count = countResult.rows[0].total;
|
||||||
|
|
||||||
|
|
||||||
const source = fs.readFileSync(
|
const source = fs.readFileSync(
|
||||||
"static/templates/reports/index.html",
|
"static/templates/reports/index.html",
|
||||||
"utf8"
|
"utf8"
|
||||||
@ -1127,6 +1149,358 @@ const formattedDate = `${("0" + day).slice(-2)}.${("0" + month).slice(-2)}.${yea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.post('/getreports', async (req, res) => {
|
||||||
|
if (req.session.userId === undefined) {
|
||||||
|
return res.redirect("/signin?page=reports");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const pool = new Pool({
|
||||||
|
user: DB_User,
|
||||||
|
host: DB_Host,
|
||||||
|
database: DB_Name,
|
||||||
|
password: DB_Password,
|
||||||
|
port: DB_Port,
|
||||||
|
});
|
||||||
|
|
||||||
|
let serialValues = [];
|
||||||
|
if (req.session.userId != 'admin') {
|
||||||
|
|
||||||
|
const userDevicesQuery = `
|
||||||
|
SELECT devices
|
||||||
|
FROM users
|
||||||
|
WHERE id = $1
|
||||||
|
`;
|
||||||
|
const userDevicesResult = await pool.query(userDevicesQuery, [req.session.userId]);
|
||||||
|
|
||||||
|
if (userDevicesResult.rows[0].devices.length > 0) {
|
||||||
|
serialValues = userDevicesResult.rows[0].devices;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { page, timeRangeStart, timeRangeEnd, serials, searchText } = req.body;
|
||||||
|
|
||||||
|
let timeRangeStartCheck = timeRangeStart;
|
||||||
|
let timeRangeEndCheck = timeRangeEnd;
|
||||||
|
let serialsCheck = serials;
|
||||||
|
|
||||||
|
console.log(req.body);
|
||||||
|
|
||||||
|
if (!timeRangeStartCheck || !timeRangeEndCheck || serialsCheck.length < 1) {
|
||||||
|
const minMaxSerialQuery = `
|
||||||
|
SELECT
|
||||||
|
MIN(time) AS min_date,
|
||||||
|
MAX(time) AS max_date,
|
||||||
|
ARRAY_AGG(DISTINCT serial) AS unique_serials
|
||||||
|
FROM
|
||||||
|
alarms;
|
||||||
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
const minMaxDateResult = await pool.query(minMaxSerialQuery);
|
||||||
|
|
||||||
|
if (!timeRangeStartCheck) {
|
||||||
|
timeRangeStartCheck = minMaxDateResult.rows[0].min_date;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!timeRangeEndCheck) {
|
||||||
|
timeRangeEndCheck = minMaxDateResult.rows[0].max_date;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialsCheck.length < 1) {
|
||||||
|
if (req.session.userId != 'admin') {
|
||||||
|
serialsCheck = serialValues;
|
||||||
|
} else {
|
||||||
|
serialsCheck = minMaxDateResult.rows[0].unique_serials;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const violationsMapping = {
|
||||||
|
0: "усталость",
|
||||||
|
1: "водитель пропал",
|
||||||
|
2: "разговор по телефону",
|
||||||
|
3: "курение за рулём",
|
||||||
|
4: "водитель отвлекся",
|
||||||
|
5: "выезд с полосы движения",
|
||||||
|
6: "!!! лобовое столкновение",
|
||||||
|
7: "скорость превышена",
|
||||||
|
8: "распознавание номерных знаков",
|
||||||
|
9: "!! маленькое расстояние спереди",
|
||||||
|
10: "водитель зевает",
|
||||||
|
11: "!!! столкновение с пешеходом",
|
||||||
|
12: "проходы переполнены",
|
||||||
|
13: "!! посадка/высадка вне остановки",
|
||||||
|
14: "!! смена полосы с нарушением ПДД",
|
||||||
|
15: "! включенный телефон у водителя",
|
||||||
|
16: "!!! ремень безопасности",
|
||||||
|
17: "проверка не удалась",
|
||||||
|
18: "слепые зоны справа",
|
||||||
|
19: "!!! заднее столкновение",
|
||||||
|
20: "!!! управление без рук",
|
||||||
|
21: "!! управление одной рукой",
|
||||||
|
22: "очки, блокирующие инфракрасное излучение",
|
||||||
|
23: "слепые зоны слева",
|
||||||
|
24: "помехи для пассажиров",
|
||||||
|
25: "на перекрестке ограничена скорость",
|
||||||
|
26: "обнаружен перекресток",
|
||||||
|
27: "пешеходы на переходе",
|
||||||
|
28: "! неучтивое отношение к пешеходам",
|
||||||
|
29: "обнаружен пешеходный переход",
|
||||||
|
30: "водитель матерится"
|
||||||
|
};
|
||||||
|
|
||||||
|
const idList = Object.entries(violationsMapping)
|
||||||
|
.filter(([id, violation]) => violation.includes(searchText.toLowerCase()))
|
||||||
|
.map(([id, violation]) => id);
|
||||||
|
|
||||||
|
console.log(idList);
|
||||||
|
|
||||||
|
const searchConditions = [
|
||||||
|
'a.evtuuid::TEXT ILIKE $4',
|
||||||
|
'a.id::TEXT ILIKE $4',
|
||||||
|
'r.plate::TEXT ILIKE $4',
|
||||||
|
'a.serial::TEXT ILIKE $4',
|
||||||
|
'g.latitude::TEXT ILIKE $4',
|
||||||
|
'g.longitude::TEXT ILIKE $4',
|
||||||
|
];
|
||||||
|
|
||||||
|
const countQueryText = `
|
||||||
|
SELECT COUNT(*) AS total
|
||||||
|
FROM (
|
||||||
|
SELECT DISTINCT a.evtuuid
|
||||||
|
FROM alarms AS a
|
||||||
|
LEFT JOIN registrars AS r ON a.serial = r.serial
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT *,
|
||||||
|
ROW_NUMBER() OVER (PARTITION BY serial ORDER BY ABS(EXTRACT(EPOCH FROM (time - NOW())))) AS row_num
|
||||||
|
FROM geo
|
||||||
|
) AS g ON a.serial = g.serial AND g.row_num = 1
|
||||||
|
WHERE a.alarmtype = 56
|
||||||
|
AND a.time >= $1::timestamp
|
||||||
|
AND a.time <= $2::timestamp
|
||||||
|
AND a.serial = ANY($3)
|
||||||
|
${searchText ? `AND (${idList.length > 0 ? 'st = ANY($5) OR' : ''} (${searchConditions.join(' OR ')}))` : ''}
|
||||||
|
) AS unique_events;
|
||||||
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
const countValues = [
|
||||||
|
timeRangeStartCheck,
|
||||||
|
timeRangeEndCheck,
|
||||||
|
serialsCheck,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (searchText.length > 0) {
|
||||||
|
countValues.push(`%${searchText}%`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idList.length > 0 && idList.length !== 31) {
|
||||||
|
countValues.push(idList);
|
||||||
|
}
|
||||||
|
|
||||||
|
const countResult = await pool.query(countQueryText, countValues);
|
||||||
|
const totalCount = countResult.rows[0].total;
|
||||||
|
|
||||||
|
const queryConditions = [
|
||||||
|
'a.evtuuid::TEXT ILIKE $6',
|
||||||
|
'a.id::TEXT ILIKE $6',
|
||||||
|
'r.plate::TEXT ILIKE $6',
|
||||||
|
'a.serial::TEXT ILIKE $6',
|
||||||
|
'g.latitude::TEXT ILIKE $6',
|
||||||
|
'g.longitude::TEXT ILIKE $6',
|
||||||
|
];
|
||||||
|
|
||||||
|
const queryText = `
|
||||||
|
SELECT a.evtuuid, a.id, a.cmdno, a.time, a.serial, a.st, r.plate, g.latitude, g.longitude, r.number
|
||||||
|
FROM (
|
||||||
|
SELECT DISTINCT ON (evtuuid) evtuuid, id, cmdno, time, serial, st
|
||||||
|
FROM alarms
|
||||||
|
WHERE alarmtype = 56
|
||||||
|
AND time >= $1::timestamp
|
||||||
|
AND time <= $2::timestamp
|
||||||
|
ORDER BY evtuuid, time DESC
|
||||||
|
) AS a
|
||||||
|
LEFT JOIN registrars AS r ON a.serial = r.serial
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT *,
|
||||||
|
ROW_NUMBER() OVER (PARTITION BY serial ORDER BY ABS(EXTRACT(EPOCH FROM (time - NOW())))) AS row_num
|
||||||
|
FROM geo
|
||||||
|
) AS g ON a.serial = g.serial AND g.row_num = 1
|
||||||
|
WHERE a.time >= $1::timestamp
|
||||||
|
AND a.time <= $2::timestamp
|
||||||
|
AND a.serial = ANY($3)
|
||||||
|
${searchText ? `AND (${idList.length > 0 ? 'st = ANY($7) OR' : ''} (${queryConditions.join(' OR ')}))` : ``}
|
||||||
|
ORDER BY a.time DESC
|
||||||
|
OFFSET $4 LIMIT $5;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const values = [
|
||||||
|
timeRangeStartCheck,
|
||||||
|
timeRangeEndCheck,
|
||||||
|
serialsCheck,
|
||||||
|
(page - 1) * 14,
|
||||||
|
14,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (searchText.length > 0) {
|
||||||
|
values.push(`%${searchText}%`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idList.length > 0 && idList.length !== 31) {
|
||||||
|
values.push(idList);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await pool.query(queryText, values);
|
||||||
|
|
||||||
|
function formatDate(date) {
|
||||||
|
const options = {
|
||||||
|
year: "2-digit",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
hour12: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const dateString = date.toISOString().replace("T", " ").slice(0, 19);
|
||||||
|
|
||||||
|
const [datePart, timePart] = dateString.split(' ');
|
||||||
|
const [year, month, day] = datePart.split('-');
|
||||||
|
const [hour, minute, second] = timePart.split(':');
|
||||||
|
|
||||||
|
const formattedDate = `${("0" + day).slice(-2)}.${("0" + month).slice(-2)}.${year.slice(-2)} ${("0" + hour).slice(-2)}:${("0" + minute).slice(-2)}`;
|
||||||
|
|
||||||
|
return formattedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const Alarms = result.rows.map((alarm) => {
|
||||||
|
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 = "Неизвестный тип";
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: alarm.id,
|
||||||
|
cmdno: alarm.cmdno,
|
||||||
|
time: formatDate(alarm.time),
|
||||||
|
number: alarm.number,
|
||||||
|
serial: alarm.serial,
|
||||||
|
st: alarm.st,
|
||||||
|
type: type,
|
||||||
|
plate: alarm.plate,
|
||||||
|
latitude: (alarm.latitude).toFixed(6),
|
||||||
|
longitude: (alarm.longitude).toFixed(6),
|
||||||
|
geo: (alarm.latitude).toFixed(6) + "," + (alarm.longitude).toFixed(6),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
total: totalCount,
|
||||||
|
data: Alarms,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error handling request:', error);
|
||||||
|
res.status(500).send('Internal Server Error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
app.get("/api/devices", async (req, res) => {
|
app.get("/api/devices", async (req, res) => {
|
||||||
if (req.session.userId === undefined) {
|
if (req.session.userId === undefined) {
|
||||||
return res.redirect("/signin?page=live");
|
return res.redirect("/signin?page=live");
|
||||||
|
@ -1,25 +1,11 @@
|
|||||||
|
|
||||||
// Получаем высоту таблицы и определяем, сколько строк помещается на странице
|
|
||||||
let currentPage = 1;
|
|
||||||
let tableHeight = document.getElementById("table-area").offsetHeight;
|
|
||||||
let rowHeight = 60;
|
|
||||||
let rowsPerPage = Math.floor(tableHeight / rowHeight) - 3;
|
|
||||||
let filteredDevices = [...devices];
|
|
||||||
let timeRangeStart = null;
|
|
||||||
let timeRangeEnd = null;
|
|
||||||
|
|
||||||
const createTable = () => {
|
const createTable = () => {
|
||||||
const table = document.getElementById("deviceTable");
|
const table = document.getElementById("deviceTable");
|
||||||
const tbody = table.querySelector("tbody");
|
const tbody = table.querySelector("tbody");
|
||||||
// Очищаем таблицу
|
// Очищаем таблицу
|
||||||
tbody.innerHTML = "";
|
tbody.innerHTML = "";
|
||||||
|
|
||||||
// Добавляем строки таблицы
|
|
||||||
const startIndex = (currentPage - 1) * rowsPerPage;
|
|
||||||
const endIndex = startIndex + rowsPerPage;
|
|
||||||
const devicesToDisplay = filteredDevices.slice(startIndex, endIndex);
|
|
||||||
|
|
||||||
devicesToDisplay.forEach((device) => {
|
devices.forEach((device) => {
|
||||||
const row = document.createElement("tr");
|
const row = document.createElement("tr");
|
||||||
|
|
||||||
// Добавляем ячейки с данными
|
// Добавляем ячейки с данными
|
||||||
@ -57,137 +43,5 @@ const createTable = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("resize", function (event) {
|
|
||||||
tableHeight = document.getElementById("table-area").offsetHeight;
|
|
||||||
rowHeight = 60;
|
|
||||||
rowsPerPage = Math.floor(tableHeight / rowHeight) - 3;
|
|
||||||
createTable();
|
|
||||||
createPagination();
|
|
||||||
});
|
|
||||||
|
|
||||||
const createPagination = () => {
|
|
||||||
const count = document.getElementById("count");
|
|
||||||
count.textContent = `Всего результатов: ${filteredDevices.length}`;
|
|
||||||
|
|
||||||
const pagination = document.getElementById("pagination");
|
|
||||||
pagination.innerHTML = "";
|
|
||||||
|
|
||||||
const pageCount = Math.ceil(filteredDevices.length / rowsPerPage);
|
|
||||||
for (let i = 1; i <= pageCount; i++) {
|
|
||||||
const pageLink = document.createElement("a");
|
|
||||||
pageLink.href = "#";
|
|
||||||
if (i === currentPage) {
|
|
||||||
pageLink.classList.add("active");
|
|
||||||
}
|
|
||||||
pageLink.textContent = i;
|
|
||||||
pageLink.addEventListener("click", (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
currentPage = i - 1;
|
|
||||||
currentPage = i;
|
|
||||||
createTable();
|
|
||||||
createPagination();
|
|
||||||
});
|
|
||||||
pagination.appendChild(pageLink);
|
|
||||||
}
|
|
||||||
|
|
||||||
// var currentPageSpan = document.createElement("span");
|
|
||||||
// currentPageSpan.textContent = currentPage;
|
|
||||||
// pagination.appendChild(currentPageSpan);
|
|
||||||
|
|
||||||
// Добавляем кнопки "Next" и "Previous"
|
|
||||||
|
|
||||||
// const prevButton = document.createElement("button");
|
|
||||||
// prevButton.innerText = "Previous";
|
|
||||||
// prevButton.onclick = () => {
|
|
||||||
// if (currentPage === 1) return;
|
|
||||||
// currentPage--;
|
|
||||||
// createTable();
|
|
||||||
// };
|
|
||||||
// pagination.appendChild(prevButton);
|
|
||||||
|
|
||||||
// const nextButton = document.createElement("button");
|
|
||||||
// nextButton.innerText = "Next";
|
|
||||||
// nextButton.onclick = () => {
|
|
||||||
// if (currentPage === pageCount) return;
|
|
||||||
// currentPage++;
|
|
||||||
// createTable();
|
|
||||||
// };
|
|
||||||
// pagination.appendChild(nextButton);
|
|
||||||
};
|
|
||||||
|
|
||||||
const applyFilterAndSearch = () => {
|
|
||||||
const searchValue = searchInput.value.toLowerCase();
|
|
||||||
const groupFilters = Array.from(
|
|
||||||
document.querySelectorAll('input[type="checkbox"].device-filter:checked')
|
|
||||||
).map((checkbox) => checkbox.value);
|
|
||||||
|
|
||||||
filteredDevices = devices.filter((device) => {
|
|
||||||
const searchString =
|
|
||||||
`${device.group} ${device.name} ${device.id} ${device.place} ${device.numberTS} ${device.time} ${device.place} ${device.geo} ${device.serial}`.toLowerCase();
|
|
||||||
const matchGroup =
|
|
||||||
groupFilters.length === 0 || groupFilters.includes(device.group) || groupFilters.includes(device.serial);
|
|
||||||
const matchSearch = !searchValue || searchString.includes(searchValue);
|
|
||||||
|
|
||||||
// Фильтр по временному диапазону
|
|
||||||
let matchTimeRange = true;
|
|
||||||
if (timeRangeStart) {
|
|
||||||
const startTimestamp = new Date(timeRangeStart).getTime();
|
|
||||||
const deviceTimestamp = parseTableTime(device.time); // Преобразование времени из таблицы
|
|
||||||
matchTimeRange = startTimestamp <= deviceTimestamp;
|
|
||||||
}
|
|
||||||
if (timeRangeEnd) {
|
|
||||||
const endTimestamp = new Date(timeRangeEnd).getTime();
|
|
||||||
const deviceTimestamp = parseTableTime(device.time); // Преобразование времени из таблицы
|
|
||||||
matchTimeRange = matchTimeRange && deviceTimestamp <= endTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchGroup && matchSearch && matchTimeRange;
|
|
||||||
});
|
|
||||||
|
|
||||||
currentPage = 1;
|
|
||||||
createTable();
|
|
||||||
createPagination();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Функция для преобразования времени из таблицы в миллисекунды
|
|
||||||
function parseTableTime(tableTime) {
|
|
||||||
// Парсинг даты и времени из строки формата "12.03.23 17:33"
|
|
||||||
const parts = tableTime.split(" ");
|
|
||||||
const dateParts = parts[0].split(".");
|
|
||||||
const timeParts = parts[1].split(":");
|
|
||||||
const year = 2000 + parseInt(dateParts[2]);
|
|
||||||
const month = parseInt(dateParts[1]) - 1; // Месяцы в JavaScript начинаются с 0
|
|
||||||
const day = parseInt(dateParts[0]);
|
|
||||||
const hours = parseInt(timeParts[0]);
|
|
||||||
const minutes = parseInt(timeParts[1]);
|
|
||||||
return new Date(year, month, day, hours, minutes).getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
const searchInput = document.getElementById("table-search");
|
|
||||||
searchInput.addEventListener("input", applyFilterAndSearch);
|
|
||||||
|
|
||||||
const filterCheckboxes = Array.from(
|
|
||||||
document.querySelectorAll('input[type="checkbox"].device-filter')
|
|
||||||
);
|
|
||||||
filterCheckboxes.forEach((checkbox) => {
|
|
||||||
checkbox.addEventListener("change", applyFilterAndSearch);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Обработчик изменения значения в поле начала временного диапазона
|
|
||||||
const timeRangeStartInput = document.getElementById("timeRangeStart");
|
|
||||||
timeRangeStartInput.addEventListener("change", () => {
|
|
||||||
timeRangeStart = timeRangeStartInput.value;
|
|
||||||
console.log(timeRangeStart);
|
|
||||||
applyFilterAndSearch();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Обработчик изменения значения в поле конца временного диапазона
|
|
||||||
const timeRangeEndInput = document.getElementById("timeRangeEnd");
|
|
||||||
timeRangeEndInput.addEventListener("change", () => {
|
|
||||||
timeRangeEnd = timeRangeEndInput.value;
|
|
||||||
console.log(timeRangeEnd);
|
|
||||||
applyFilterAndSearch();
|
|
||||||
});
|
|
||||||
|
|
||||||
createTable();
|
createTable();
|
||||||
createPagination();
|
|
||||||
|
@ -20,6 +20,16 @@ body {
|
|||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type="number"]::-webkit-inner-spin-button,
|
||||||
|
input[type="number"]::-webkit-outer-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="number"] {
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -1687,10 +1697,24 @@ input[type="datetime-local"] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.time-range input:hover,
|
.time-range input:hover,
|
||||||
.time-range input:focus {
|
.time-range input:focus,
|
||||||
|
#pagination input:hover,
|
||||||
|
#pagination input:focus {
|
||||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pagination input {
|
||||||
|
width: 20px;
|
||||||
|
height: 15px;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
outline: none;
|
||||||
|
color: rgba(0, 0, 0, 0.9);
|
||||||
|
transition: 0.2s;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.return-name {
|
.return-name {
|
||||||
color: #8086f9 !important;
|
color: #8086f9 !important;
|
||||||
}
|
}
|
||||||
@ -1933,6 +1957,29 @@ input[type="datetime-local"] {
|
|||||||
background-color: rgba(0, 0, 0, 0.1);
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pagination #left-slider {
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 4px;
|
||||||
|
transition: 0.2s;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pagination #left-slider:hover,
|
||||||
|
#pagination #right-slider:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pagination #right-slider {
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 4px;
|
||||||
|
transition: 0.2s;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
.signals-list .search {
|
.signals-list .search {
|
||||||
float: left;
|
float: left;
|
||||||
width: calc(305px - 30px);
|
width: calc(305px - 30px);
|
||||||
|
@ -74,17 +74,17 @@
|
|||||||
<section class="bg">
|
<section class="bg">
|
||||||
<section class="content">
|
<section class="content">
|
||||||
|
|
||||||
<section class="for-table">
|
<section style="height: 945px !important;" class="for-table">
|
||||||
|
|
||||||
<section class="organisation">
|
<section class="organisation">
|
||||||
<h1>Организация</h1>
|
<h1>Организация</h1>
|
||||||
|
|
||||||
<ul class="area">
|
<ul class="area">
|
||||||
{{#each Groups}}
|
{{#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 checkbox-name"><span class="text">{{name}}</span></label>
|
<li class="area-name"><img src="../img/ul.svg"><input type="checkbox" id="{{name}}" class="checkbox-input" onchange="requestUpdate()" hidden checked><label for="{{name}}" class="checkbox-label checkbox-name"><span class="text">{{name}}</span></label>
|
||||||
<ul class="area-devices" id="devices-1">
|
<ul class="area-devices" id="devices-1">
|
||||||
{{#each serials}}
|
{{#each serials}}
|
||||||
<li class="device"><img><input type="checkbox" id="{{this}}" class="checkbox-input device-filter" value="{{this}}" hidden checked><label for="{{this}}" class="checkbox-label">
|
<li class="device"><img><input type="checkbox" id="{{this}}" class="checkbox-input device-filter" value="{{this}}" onchange="requestUpdate()" hidden checked><label for="{{this}}" class="checkbox-label">
|
||||||
{{#if ../numbers}}
|
{{#if ../numbers}}
|
||||||
{{#if (lookup ../numbers @index)}}
|
{{#if (lookup ../numbers @index)}}
|
||||||
<div class="checkmark"></div>{{lookup ../numbers @index}}
|
<div class="checkmark"></div>{{lookup ../numbers @index}}
|
||||||
@ -102,8 +102,8 @@
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="area-time-range">
|
<div class="area-time-range">
|
||||||
<div class="time-range"><label for="timeRangeStart">с</label><input id="timeRangeStart" name="timeRangeStart" type="datetime-local"></div>
|
<div class="time-range"><label for="timeRangeStart">с</label><input id="timeRangeStart" name="timeRangeStart" onblur="requestUpdate()" type="datetime-local"></div>
|
||||||
<div class="time-range"><label for="timeRangeEnd">до</label><input id="timeRangeEnd" name="timeRangeEnd" type="datetime-local"></div>
|
<div class="time-range"><label for="timeRangeEnd">до</label><input id="timeRangeEnd" name="timeRangeEnd" onblur="requestUpdate()" type="datetime-local"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
@ -112,7 +112,7 @@
|
|||||||
|
|
||||||
<section id="table-area" class="table">
|
<section id="table-area" class="table">
|
||||||
<h1>Список предупреждений</h1>
|
<h1>Список предупреждений</h1>
|
||||||
<input id="table-search" class="search" type="text" placeholder="Поиск">
|
<input id="table-search" class="search" type="text" onblur="requestUpdate()" placeholder="Поиск">
|
||||||
|
|
||||||
|
|
||||||
<table id="deviceTable">
|
<table id="deviceTable">
|
||||||
@ -135,12 +135,44 @@
|
|||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div id="count">
|
<div id="count">Всего результатов: <span id="count-value">{{Count}}</span></div>
|
||||||
<!-- Сюда добавится итоговое количество результатов -->
|
<!-- Сюда добавится итоговое количество результатов -->
|
||||||
</div>
|
|
||||||
<div id="pagination">
|
<div id="pagination">
|
||||||
|
<svg id="left-slider" xmlns="http://www.w3.org/2000/svg" width="11" height="19" fill="none" viewBox="0 0 11 19" onclick="decrementPage()">
|
||||||
|
<path fill="#000" fill-opacity=".75" d="M0 9.495c0 .273.101.514.315.722l8.92 8.477a.981.981 0 0 0 .73.295c.585 0 1.035-.427 1.035-.995 0-.285-.124-.525-.304-.711L2.508 9.495l8.188-7.789c.18-.186.304-.437.304-.71C11 .425 10.55 0 9.965 0c-.292 0-.54.098-.73.284L.314 8.773A.955.955 0 0 0 0 9.495Z"/>
|
||||||
|
</svg>
|
||||||
|
<input id="page-number" type="number" onblur="requestUpdate()" value="1">
|
||||||
|
<svg id="right-slider" xmlns="http://www.w3.org/2000/svg" width="11" height="19" fill="none" viewBox="0 0 11 19" onclick="incrementPage()">
|
||||||
|
<path fill="#000" fill-opacity=".75" d="M11 9.495a.967.967 0 0 0-.326-.722L1.766.284A1.062 1.062 0 0 0 1.024 0C.45 0 0 .427 0 .995c0 .274.112.525.292.711l8.189 7.789-8.189 7.788c-.18.186-.292.426-.292.71 0 .57.45.996 1.024.996.292 0 .54-.098.742-.295l8.908-8.477c.213-.208.326-.449.326-.722Z"/>
|
||||||
|
</svg>
|
||||||
<!-- Сюда будут добавляться ссылки для переключения между страницами -->
|
<!-- Сюда будут добавляться ссылки для переключения между страницами -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var pageNumberInput = document.getElementById('page-number');
|
||||||
|
var countMax = Math.ceil({{Count}} / 14);
|
||||||
|
|
||||||
|
function decrementPage() {
|
||||||
|
var currentPage = parseInt(pageNumberInput.value, 10);
|
||||||
|
if (currentPage > 1) {
|
||||||
|
pageNumberInput.value = currentPage - 1;
|
||||||
|
requestUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function incrementPage() {
|
||||||
|
var currentPage = parseInt(pageNumberInput.value, 10);
|
||||||
|
if (currentPage === countMax || currentPage > countMax) {
|
||||||
|
pageNumberInput.value = countMax;
|
||||||
|
requestUpdate();
|
||||||
|
}
|
||||||
|
if (currentPage < countMax) {
|
||||||
|
pageNumberInput.value = currentPage + 1;
|
||||||
|
requestUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<!-- <br>
|
<!-- <br>
|
||||||
<br>
|
<br>
|
||||||
<span style="opacity:50%">Временное ограничение: 100 последних предупреждений</span> -->
|
<span style="opacity:50%">Временное ограничение: 100 последних предупреждений</span> -->
|
||||||
@ -154,7 +186,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const devices = [
|
let devices = [
|
||||||
{{#each Alarms}}
|
{{#each Alarms}}
|
||||||
{
|
{
|
||||||
id: "{{this.id}}",
|
id: "{{this.id}}",
|
||||||
@ -188,6 +220,78 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
function requestUpdate() {
|
||||||
|
|
||||||
|
document.getElementById("deviceTable").style.filter = "brightness(0.85)";
|
||||||
|
|
||||||
|
const requestData = {
|
||||||
|
page: parseInt(document.getElementById("page-number").value),
|
||||||
|
timeRangeStart: document.getElementById("timeRangeStart").value,
|
||||||
|
timeRangeEnd: document.getElementById("timeRangeEnd").value,
|
||||||
|
serials: Array.from(
|
||||||
|
document.querySelectorAll('input[type="checkbox"].device-filter:checked')
|
||||||
|
).map((checkbox) => checkbox.value),
|
||||||
|
searchText: document.getElementById("table-search").value,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(requestData);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fetch("/getreports", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(requestData),
|
||||||
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => {
|
||||||
|
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
console.log(devices);
|
||||||
|
|
||||||
|
devices.splice(0, devices.length);
|
||||||
|
|
||||||
|
devices.push(...data.data.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
cmdno: item.cmdno,
|
||||||
|
number: item.number,
|
||||||
|
time: item.time,
|
||||||
|
serial: item.serial,
|
||||||
|
type: item.type,
|
||||||
|
geo: item.geo,
|
||||||
|
plate: item.plate,
|
||||||
|
})));
|
||||||
|
|
||||||
|
console.log(devices);
|
||||||
|
createTable();
|
||||||
|
|
||||||
|
document.getElementById("count-value").innerHTML = data.total;
|
||||||
|
|
||||||
|
countMax = Math.ceil(data.total / 14);
|
||||||
|
|
||||||
|
var currentPage = parseInt(pageNumberInput.value, 10);
|
||||||
|
if (currentPage === countMax || currentPage > countMax) {
|
||||||
|
pageNumberInput.value = countMax;
|
||||||
|
requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("deviceTable").style.filter = "brightness(1)";
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
document.getElementById("dataLoading").style.display = 'none';
|
||||||
|
h1Element.textContent = 'Ошибка отправки запроса.';
|
||||||
|
console.error("Ошибка при отправке запроса:", error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
<script>
|
<script>
|
||||||
// Скрытие/Показ дополнительных меню аккаунта
|
// Скрытие/Показ дополнительных меню аккаунта
|
||||||
const accountMain = document.getElementById('account-main');
|
const accountMain = document.getElementById('account-main');
|
||||||
@ -214,41 +318,37 @@
|
|||||||
const checkboxes = document.querySelectorAll('.organisation .checkbox-input');
|
const checkboxes = document.querySelectorAll('.organisation .checkbox-input');
|
||||||
|
|
||||||
checkboxes.forEach((checkbox) => {
|
checkboxes.forEach((checkbox) => {
|
||||||
applyFilterAndSearch();
|
|
||||||
checkbox.addEventListener('change', function() {
|
checkbox.addEventListener('change', function() {
|
||||||
|
|
||||||
applyFilterAndSearch();
|
const areaDevices = this.parentNode.querySelector('.area-devices');
|
||||||
const devices = this.parentNode.querySelector('.area-devices');
|
|
||||||
if (this.checked) {
|
if (this.checked) {
|
||||||
devices.style.display = 'block';
|
areaDevices.style.display = 'block';
|
||||||
|
|
||||||
// Активируем дочерние чекбоксы
|
// Активируем дочерние чекбоксы
|
||||||
const childCheckboxes = devices.querySelectorAll('.device-filter');
|
const childCheckboxes = areaDevices.querySelectorAll('.device-filter');
|
||||||
childCheckboxes.forEach((childCheckbox) => {
|
childCheckboxes.forEach((childCheckbox) => {
|
||||||
childCheckbox.checked = true;
|
childCheckbox.checked = true;
|
||||||
applyFilterAndSearch();
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
devices.style.display = 'none';
|
areaDevices.style.display = 'none';
|
||||||
applyFilterAndSearch();
|
|
||||||
|
|
||||||
// Деактивируем дочерние чекбоксы
|
// Деактивируем дочерние чекбоксы
|
||||||
const childCheckboxes = devices.querySelectorAll('.device-filter');
|
const childCheckboxes = areaDevices.querySelectorAll('.device-filter');
|
||||||
childCheckboxes.forEach((childCheckbox) => {
|
childCheckboxes.forEach((childCheckbox) => {
|
||||||
childCheckbox.checked = false;
|
childCheckbox.checked = false;
|
||||||
applyFilterAndSearch();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Деактивируем дочерние чекбоксы, если родительский чекбокс не выбран
|
// Деактивируем дочерние чекбоксы, если родительский чекбокс не выбран
|
||||||
if (!this.checked) {
|
if (!this.checked) {
|
||||||
const childCheckboxes = devices.querySelectorAll('.device-filter');
|
const childCheckboxes = areaDevices.querySelectorAll('.device-filter');
|
||||||
childCheckboxes.forEach((childCheckbox) => {
|
childCheckboxes.forEach((childCheckbox) => {
|
||||||
childCheckbox.checked = false;
|
childCheckbox.checked = false;
|
||||||
applyFilterAndSearch();
|
|
||||||
});
|
});
|
||||||
devices.style.display = 'none';
|
areaDevices.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestUpdate();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user