<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>设备消息监控</title> <style> html, body { height: 100%; margin: 0; background-color: #3C3F41; } table { width: 100%; table-layout: fixed; border-collapse: collapse; } td { height: inherit; border: 1px solid #555; } select { width: 210px; height: 30px; margin: 0 auto 5px auto; color: #BABABA; font-size: 16px; padding: 2px; border: 1px solid #646464; background-color: #414141; } input { width: 100px; height: 23px; color: #BABABA; padding: 2px; border: 1px solid #646464; background-color: #414141; } button { width: 90px; color: #BABABA; font-size: 18px; border: 1px solid #4C708C; background-color: #365880; } button:active { color: #EEE; border: 1px solid #6B96B4; } p { color: #BABABA; text-align: left; white-space: pre-wrap; word-wrap: break-word; word-break: break-all; } a { color: #FFF; } .cell { height: inherit; text-align: center; display: flex; flex-direction: column; } .textarea { height: inherit; overflow: auto; background-color: #2B2B2B; } .tool { padding: 10px 0 10px 0; } ::-webkit-scrollbar { width: 10px; } ::-webkit-scrollbar-thumb { background-color: #666; } ::-webkit-scrollbar-thumb:hover { background-color: #777; } </style> </head> <body> <table id="dev_box"> <tr> <td> <div class="cell"> <div class="textarea" id="dev_text_[idx]"></div> <div class="tool"> <select id="dev_sel_[idx]">[option]</select> <input type="text" onkeyup="onSearch(this)"> <button type="button" onclick="onSub([idx])">订阅</button> <button type="button" onclick="onUnsub([idx])">取消</button> <button type="button" onclick="$('dev_text_[idx]').innerHTML=''">清空</button> <button type="button" onclick="onExport([idx])">导出</button> </div> </div> </td> </tr> </table> <a href="?s=1,2" target="_blank">1*2</a> <a href="?s=1,4" target="_blank">1*4</a> <a href="?s=2,6" target="_blank">2*6</a> <script type="text/javascript"> const reg = new RegExp("\\[([^\\[\\]]*?)]", 'igm'); //igm是指分别用于指定区分大小写的匹配、全局匹配和多行匹配。 const host = window.location.host; const domain = '//' + host; let eventSource = null; const subscriptions = {}; const $ = function (id) { return document.getElementById(id); }; function getQueryVariable(key) { const query = window.location.search.substring(1); const paramsArr = query.split("&"); for (let i = 0; i < paramsArr.length; i++) { const pair = paramsArr[i].split("="); if (pair[0] === key) { return pair[1]; } } return null; } const renderForm = function (data) { let options = '<option value="">没有可订阅的设备</option>\n'; if (data && data.length) { options = '<option value="">---请选择订阅设备---</option>\n'; options += data.map(function (device) { return `<option value="${device.mobileNo}">${device.mobileNo} - ${device.plateNo}</option>` }).join('\n'); } let size = [1, 1]; const s = getQueryVariable('s'); if (s) size = s.split(','); const row = size[0] > 3 ? 3 : size[0]; const column = size[1] > 6 ? 6 : size[1]; const height = Math.floor((document.documentElement.clientHeight) / row) - 1; const table = $('dev_box'); const td = table.children[0].children[0].innerHTML; let html = ''; let idx = 0; for (let i = 0; i < row; i++) { html += `<tr style="height: ${height}px">`; for (let j = 0; j < column; j++) { html += td.replace(reg, function (node, key) { return ({idx: idx, option: options})[key] }); ++idx; } html += '</tr>'; } table.innerHTML = html; }; function onSearch(e) { let value = e.value; let select = e.previousElementSibling; if (!select.disabled) { const options = select.querySelectorAll('option'); options[0].selected = true; if (value) { options.forEach(option => { if (option.innerHTML.search(value) >= 0) { option.selected = true; } }); } } } function onExport(idx) { const data = $('dev_text_' + idx).innerText; if (data) { const a = document.createElement('a'); a.download = $('dev_sel_' + idx).value + '_' + new Date().toLocaleString().replaceAll(/[^0-9]/g, '_') + '.txt'; a.href = 'data:text/plain;base64,' + btoa(unescape(encodeURIComponent(data))); a.click(); } } function onSub(idx) { const select = $('dev_sel_' + idx); if (select.disabled) { return; } const clientId = select.value; if (!clientId) { return; } if (subscriptions[clientId]) { alert(`同一设备[${clientId}]不能重复订阅`); return; } select.disabled = true; const text = $('dev_text_' + idx); text.println = function (data) { const c = this.scrollTop + this.offsetHeight + 4; const h = this.scrollHeight; const p = document.createElement('p'); p.append(new Date().toLocaleString().replaceAll('/', '-') + ' '); p.append(data); this.append(p); if (c >= h) this.scrollTop = this.scrollHeight; } eventSource.addEventListener(clientId, subscriptions[clientId] = function (event) { text.println(event.data); }); fetch(`${domain}/device/sse?sub=1&clientId=${clientId}`, {method: 'post'}) .then(res => res.json()) .then(function (res) { if (res) { text.println('开始订阅'); } else { text.println('订阅失败,请刷新页面'); } }).catch(err => text.println('连接服务器失败')); } function onUnsub(idx) { const select = $('dev_sel_' + idx); const clientId = select.value; if (!clientId) { return; } fetch(`${domain}/device/sse?sub=0&clientId=${clientId}`, {method: 'post'}) .then(res => res.json()) .then(function (res) { if (res) { console.log(`取消订阅设备[${clientId}]....`); eventSource.removeEventListener(clientId, subscriptions[clientId]); delete subscriptions[clientId]; select.disabled = false; $('dev_text_' + idx).println('结束订阅'); } }).catch(err => console.error(err)); } window.onload = function () { fetch(`${domain}/device/option`) .then(res => res.json()) .then(function (res) { renderForm(res.data); eventSource = new EventSource(`${domain}/device/sse`); eventSource.addEventListener('open', function (e) { console.log(`建立连接`); }); eventSource.addEventListener('error', function (e) { console.log(`断开连接`); }); }).catch(err => console.error(err)); } </script> </body> </html>