const apiEndpoint = '/api/channel/list'; const rowsPerPage = 10; //算法配置窗口部分控件 const searchEndpoint = '/api/channel/select'; const canvas = document.getElementById('myCanvas'); const ctx = canvas.getContext('2d'); const backgroundCanvas = document.getElementById('backgroundCanvas'); const backgroundCtx = backgroundCanvas.getContext('2d'); const img = new Image(); const tbody = document.getElementById('schedule-body');//布防计划 let currentPage = 1; let channelData = []; let channelData_bak = []; let areaData = ["请选择"]; let currentEditingRow = null; let cid_schedule = "-1"; let m_polygon = ""; let check_area = 0; let draw_status = false; //是否是绘制状态,处于绘制状态才能开始绘制 let b_img = false; //有没有加载图片成功,如果没有初始化的时候就不绘制线条了。 let points = []; document.addEventListener('DOMContentLoaded', function () { fetchChannelData(); //初始化页面元素数据 document.getElementById('searchButton').addEventListener('click', function () { performSearch(); }); //新增通道 document.getElementById('saveButton').addEventListener('click', function () { addChannel(1); }); //修改通道 document.getElementById('saveButton_cc').addEventListener('click', function () { addChannel(2); }); //算法配置 document.getElementById('cancelButton_mx').addEventListener('click', function () { close_mx_model(); }); //保存算法配置 document.getElementById('saveButton_mx').addEventListener('click', function () { save_mx_model(); }); //开始绘制区域按钮 document.getElementById('but_hzqy').addEventListener('click', function () { startDraw(); }); }); //添加和修改通道 1--新增,2--修改 function addChannel(itype) { let area; let cName; let Rtsp; let cid; if(itype ==1){ const saveButton = document.getElementById('saveButton'); const CNameInput = document.getElementById('CNameInput'); const RTSPInput = document.getElementById('RTSPInput'); area = document.getElementById('areaSelect_M').value; cName = CNameInput.value.trim(); Rtsp = RTSPInput.value.trim(); cid = -1 } else if(itype ==2){ const saveButton = document.getElementById('saveButton_cc'); const CNameInput = document.getElementById('CNameInput_cc'); const RTSPInput = document.getElementById('RTSPInput_cc'); area = document.getElementById('areaSelect_CC').value; cName = CNameInput.value.trim(); Rtsp = RTSPInput.value.trim(); cid = currentEditingRow.cells[0].innerText; } if(area === "请选择"){ alert('请选择所属区域'); } else{ if (cName && Rtsp) { saveButton.disabled = true; //发送视频链接接口 const url = '/api/channel/add'; const data = {"area":area,"cName":cName,"Rtsp":Rtsp,"cid":cid}; // 发送 POST 请求 fetch(url, { method: 'POST', // 指定请求方法为 POST headers: { 'Content-Type': 'application/json' // 设置请求头,告诉服务器请求体的数据类型为 JSON }, body: JSON.stringify(data) // 将 JavaScript 对象转换为 JSON 字符串 }) .then(response => response.json()) // 将响应解析为 JSON .then(data => { const istatus = data.status; if(istatus === 0){ alert(data.msg); // 使用 Modal 显示消息 // 启用保存按钮 saveButton.disabled = false; return; } else{ // 启用保存按钮 saveButton.disabled = false; //刷新列表 fetchChannelData(); if(itype ==1){ //添加通道成功 $('#channelModal').modal('hide'); alert("添加通道成功!"); // 使用 Modal 显示消息 } else if(itype==2){ //修改通道成功 currentEditingRow = null; $('#ChangeC').modal('hide'); alert("修改通道成功!"); // 使用 Modal 显示消息 } } }) .catch((error) => { alert(`Error: ${error.message}`); // 使用 Modal 显示错误信息 // 启用保存按钮 saveButton.disabled = false; return; }); } else { alert('通道名称和RTSP地址不能为空'); } } } async function fetchChannelData() { //刷新通道数据 try { const response = await fetch(apiEndpoint); channelData = await response.json(); channelData_bak = channelData; renderTable(); //读取通道list接口,刷新表格 renderPagination(); //刷新分页元素 renderAreaOptions(); //所属区域下来框 } catch (error) { console.error('Error fetching channel data:', error); } } //关键字查询数据 async function performSearch() { try { const area = document.getElementById('areaSelect').value; const channelName = document.getElementById('channelNameInput').value; if(area === "请选择" && channelName===""){ channelData = channelData_bak; } else if(area === "请选择"){ channelData = []; channelData_bak.forEach((channel) => { if(channelName === channel.channel_name){ channelData.push(channel); } }); } else if(channelName === ""){ channelData = []; channelData_bak.forEach((channel) => { if(area === channel.area_name){ channelData.push(channel); } }); } else{ channelData = []; channelData_bak.forEach((channel) => { if(area === channel.area_name && channelName === channel.channel_name){ channelData.push(channel); } }); } // 渲染表格和分页控件 currentPage = 1; // 重置当前页为第一页 renderTable(); renderPagination(); } catch (error) { console.error('Error performing search:', error); } } //刷新表单页面数据 function renderTable() { const tableBody = document.getElementById('table-body'); tableBody.innerHTML = ''; const start = (currentPage - 1) * rowsPerPage; const end = start + rowsPerPage; const pageData = channelData.slice(start, end); const surplus_count = rowsPerPage - pageData.length; let area_name = ""; pageData.forEach((channel) => { if(area_name!==channel.area_name){ //这里要求区域名称一样的要在一起 area_name = channel.area_name; areaData.push(area_name); } const row = document.createElement('tr'); row.innerHTML = ` ${channel.ID} ${channel.area_name} ${channel.channel_name} ${channel.ulr} ${channel.model_name} `; tableBody.appendChild(row); row.querySelector('.modify-btn').addEventListener('click', () => modifyChannel(row)); row.querySelector('.algorithm-btn').addEventListener('click', () => configureAlgorithm(row)); row.querySelector('.delete-btn').addEventListener('click', () => deleteChannel(row)); }); } //修改通道信息 function modifyChannel(row) { // const cid = row.cells[0].innerText; const areaName = row.cells[1].innerText; const channelName = row.cells[2].innerText; const url = row.cells[3].innerText; const area = document.getElementById('areaSelect_CC'); const CName = document.getElementById('CNameInput_cc'); const RTSP = document.getElementById('RTSPInput_cc'); for(let i=0;i< area.options.length;i++){ if(area.options[i].value === areaName){ area.options[i].selected = true; break; } } CName.value = channelName; RTSP.value = url; currentEditingRow = row; $('#ChangeC').modal('show'); } //算法配置 -- 点击算法按钮 function configureAlgorithm(row) { //获取当前行信息 currentEditingRow = row; const cid = row.cells[0].innerText; //清除数据,若需要的话 ctx.clearRect(0, 0, canvas.width, canvas.height); //清除左侧绘画和画线信息 tbody.innerHTML = ''; //清空布防控件数据 points = []; //清空绘制检测区域 draw_status = false; b_img = false; document.getElementById('but_hzqy').textContent = "绘制区域"; //开始初始化算法管理模块 show_channel_img(cid); //显示一帧图片 -- 获取不到图片就是黑画面 show_channel_model_schedule(cid); //显示结构化数据 //显示窗口 $('#MX_M').modal('show'); } //获取一帧图片 function show_channel_img(cid){ const data = {"cid":cid}; fetch('/api/channel/img', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }) .then(response => response.json()) .then(data => { if (data.image) { b_img = true; img.src = 'data:image/jpeg;base64,' + data.image; } else { console.error('Error:', data.error); } }) .catch(error => console.error('Error:', error)); } //图片加载事项 img.onload = () => { //清除、画图和画线应该分开 // 设置画布宽高 backgroundCanvas.width = canvas.width = img.width; backgroundCanvas.height = canvas.height = img.height; // 将图片绘制到背景画布上 backgroundCtx.drawImage(img, 0, 0, img.width, img.height); // 将背景画布的内容复制到前台画布上 ctx.drawImage(backgroundCanvas, 0, 0, canvas.width, canvas.height); //绘制画面 }; //开始和重新绘制 function startDraw(){ if(!document.getElementById('zdjc').checked){ alert("请先选择指定区域!"); return; } let but = document.getElementById('but_hzqy'); if(!draw_status){//开始绘制 if(points.length >0){ if (confirm('开始绘制将清除未提交保存的绘制数据,是否继续?')) { draw_status = true; points = []; //按钮文字调整为结束绘制 but.textContent = '结 束 绘 制'; // 清除前台画布 ctx.clearRect(0, 0, canvas.width, canvas.height); // 将背景画布的内容复制到前台画布上 ctx.drawImage(backgroundCanvas, 0, 0, canvas.width, canvas.height); } } else{ draw_status = true; but.textContent = '结束绘制'; } } else{//结束绘制 draw_status = false; but.textContent = '绘制区域'; } } //单选按钮点击事件处理 function handleRadioClick(event) { const selectedRadio = event.target; console.log('Selected Radio:', selectedRadio.id); // 根据选中的单选按钮执行相应操作 if (selectedRadio.id === 'qjjc') { console.log("points.length",points.length); // 处理全画面生效的逻辑 if(points.length>0){ if (!confirm('已经绘制了检测区域,确认要切换到全画面生效吗?')) { document.getElementById('zdjc').checked = true; } } console.log('全画面生效'); } else if (selectedRadio.id === 'zdjc') { // 处理指定区域的逻辑 console.log('指定区域'); } } // 鼠标点击事件处理--动态绘图 canvas.addEventListener('click', (event) => { if(draw_status){ const rect = canvas.getBoundingClientRect(); const scaleX = canvas.width / rect.width; const scaleY = canvas.height / rect.height; // 获取鼠标相对于canvas的位置 const x = (event.clientX - rect.left) * scaleX; const y = (event.clientY - rect.top) * scaleY; points.push({ x, y }); //绘制线条 drawLines(); } }); //初始化读取该视频通道与算法配置的相关信息 --- 这里用GET会更加贴切一些 function show_channel_model_schedule(cid){ //发送视频链接接口 const url = '/api/channel/C2M'; const data = {"cid":cid}; // 发送 POST 请求 fetch(url, { method: 'POST', // 指定请求方法为 POST headers: { 'Content-Type': 'application/json' // 设置请求头,告诉服务器请求体的数据类型为 JSON }, body: JSON.stringify(data) // 将 JavaScript 对象转换为 JSON 字符串 }) .then(response => response.json()) // 将响应解析为 JSON .then(data => { const m_datas = data.m_datas; const c2m_data = data.c2m_data; const schedule = data.schedule; //console.log("m_datas--",m_datas); //console.log("c2m_data--",c2m_data); //console.log("schedule--",schedule); //配置算法下拉清单 select_datas = ["请选择"]; m_datas.forEach(option => { select_datas.push(option.model_name); }); set_select_data("model_select",select_datas); select_str = currentEditingRow.cells[4].innerText; //检测区域 if(c2m_data.length >0){ model_id = c2m_data[0].model_id; model_name = currentEditingRow.cells[4].innerText; set_select_selct("model_select",model_name); check_area = c2m_data[0].check_area if( check_area == 0){ //全画面生效 document.getElementById('qjjc').checked = true; m_polygon = ""; } else{//指定区域 document.getElementById('zdjc').checked = true; m_polygon = c2m_data[0].polygon; if(m_polygon !== ""){ //指定区域了,一般是会有数据的。 const coords = parseCoordStr(m_polygon); points = coords; drawLines(); } } console.log("m_polygon",m_polygon); //阈值 document.getElementById('zxyz').value = c2m_data[0].conf_thres document.getElementById('iouyz').value = c2m_data[0].iou_thres } const days = ['一', '二', '三', '四', '五', '六','日']; const num_days=['0','1','2','3','4','5','6'] days.forEach((day, dayIndex) => { const row = document.createElement('tr'); const dayCell = document.createElement('th'); dayCell.textContent = day; row.appendChild(dayCell); num_day = num_days[dayIndex] for (let hour = 0; hour < 24; hour++) { const cell = document.createElement('td'); if(schedule.length >0){ const status = schedule.find(item => item.day === num_day && item.hour === hour); if (status && status.status === 1) { cell.classList.add('blocked'); } else { cell.classList.add('allowed'); } } else{ cell.classList.add('blocked'); } row.appendChild(cell); cell.addEventListener('click', () => { if (cell.classList.contains('blocked')) { cell.classList.remove('blocked'); cell.classList.add('allowed'); // Update status in the database //updateStatus(day, hour, 0); } else { cell.classList.remove('allowed'); cell.classList.add('blocked'); // Update status in the database //updateStatus(day, hour, 1); } }); } tbody.appendChild(row); }); }) .catch((error) => { alert(`Error: ${error.message}`); // 使用 Modal 显示错误信息 return; }); } // 将字符串转换为数组 function parseCoordStr(str) { return str.match(/\(([^)]+)\)/g).map(pair => { const [x, y] = pair.replace(/[()]/g, '').split(',').map(Number); return { x, y }; }); } // 绘制区域,各点连接 function drawLines() { if(b_img){ // 清除前台画布 ctx.clearRect(0, 0, canvas.width, canvas.height); // 将背景画布的内容复制到前台画布上 ctx.drawImage(backgroundCanvas, 0, 0, canvas.width, canvas.height); // 绘制点和线 ctx.strokeStyle = 'red'; ctx.lineWidth = 2; if (points.length > 0) { ctx.beginPath(); ctx.moveTo(points[0].x, points[0].y); for (let i = 1; i < points.length; i++) { ctx.lineTo(points[i].x, points[i].y); } // 连接最后一个点到起点 ctx.lineTo(points[0].x, points[0].y); ctx.stroke(); } points.forEach(point => { ctx.beginPath(); ctx.arc(point.x, point.y, 5, 0, Math.PI * 2); ctx.fillStyle = 'red'; ctx.fill(); }); } } //关闭算法配置窗口 function close_mx_model(){ if (confirm('确定退出窗口吗?未保存的修改将丢失!')) { $('#MX_M').modal('hide'); } } //保存算法配置窗口数据 function save_mx_model(){ //? currentEditingRow =null; } //删除通道 function deleteChannel(row) { if (confirm('确定删除此区域吗?')) { cid = row.cells[0].innerText; //发送视频链接接口 const url = '/api/channel/del'; const data = {"cid":cid}; // 发送 POST 请求 fetch(url, { method: 'POST', // 指定请求方法为 POST headers: { 'Content-Type': 'application/json' // 设置请求头,告诉服务器请求体的数据类型为 JSON }, body: JSON.stringify(data) // 将 JavaScript 对象转换为 JSON 字符串 }) .then(response => response.json()) // 将响应解析为 JSON .then(data => { const istatus = data.status; if(istatus === 0){ alert(data.msg); // 使用 Modal 显示消息 // 启用保存按钮 saveButton.disabled = false; return; } else{ // 启用保存按钮 saveButton.disabled = false; //刷新列表 row.remove(); alert("删除通道成功!"); } }) .catch((error) => { alert(`Error: ${error.message}`); // 使用 Modal 显示错误信息 // 启用保存按钮 saveButton.disabled = false; return; }); } } //刷新分页标签 function renderPagination() { const pagination = document.getElementById('pagination'); pagination.innerHTML = ''; const totalPages = Math.ceil(channelData.length / rowsPerPage); for (let i = 1; i <= totalPages; i++) { const pageItem = document.createElement('li'); pageItem.className = 'page-item' + (i === currentPage ? ' active' : ''); pageItem.innerHTML = `${i}`; pageItem.addEventListener('click', (event) => { event.preventDefault(); currentPage = i; renderTable(); renderPagination(); }); pagination.appendChild(pageItem); } } //刷新区域下拉控件 function renderAreaOptions() { const areaSelect = document.getElementById('areaSelect'); const areaSelect_M = document.getElementById('areaSelect_M') const areaSelect_CC = document.getElementById('areaSelect_CC') areaData.forEach(option => { const optionElement = document.createElement('option'); optionElement.textContent = option; areaSelect.appendChild(optionElement); const optionElement_m = document.createElement('option'); optionElement_m.textContent = option; areaSelect_M.appendChild(optionElement_m); const optionElement_cc = document.createElement('option'); optionElement_cc.textContent = option; areaSelect_CC.appendChild(optionElement_cc); }); }