| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1" />
- <title>设备连接详情</title>
- <style>
- :root {
- --bg: #f6f8fa;
- --card: #ffffff;
- --border: #e5e7eb;
- --text: #111827;
- --muted: #6b7280;
- --primary: #1976d2;
- --secondary: #f9fafb;
- }
- * { box-sizing: border-box; }
- html, body { height: 100%; }
- body {
- margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", Arial, "Noto Sans", "PingFang SC", "Microsoft YaHei", sans-serif;
- color: var(--text);
- background: var(--bg);
- }
- .page { max-width: 1100px; margin: 0 auto; padding: 20px; }
- .page-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
- .page-header h1 { font-size: 22px; margin: 0; }
- .subtext { color: var(--muted); font-size: 13px; }
- .grid { display: grid; grid-template-columns: 1fr; gap: 16px; }
- @media (min-width: 768px) { .grid { grid-template-columns: 1fr; } }
- .card { background: var(--card); border: 1px solid var(--border); border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,.06); }
- .card-header { padding: 14px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }
- .card-title { font-size: 16px; font-weight: 600; }
- .card-body { padding: 14px 16px; }
- .toolbar { display: flex; flex-wrap: wrap; gap: 12px; }
- .btn { display: inline-flex; align-items: center; gap: 8px; padding: 10px 14px; border-radius: 8px; text-decoration: none; border: 1px solid transparent; transition: background .2s ease, box-shadow .2s ease; }
- .btn-primary { background: var(--primary); color: #fff; }
- .btn-primary:hover { background: #1561ac; }
- .btn-secondary { background: var(--secondary); color: #111827; }
- .btn-secondary:hover { background: #e5e7eb; }
- .btn-player { background: #3b82f6; color: #fff; }
- .btn-player:hover { background: #2563eb; }
- .btn-player-no-audio { background: #10b981; color: #fff; }
- .btn-player-no-audio:hover { background: #0d9463; }
- .table-responsive { width: 100%; overflow-x: auto; }
- table { width: 100%; border-collapse: collapse; }
- thead th {
- background: #f9fafb;
- border-bottom: 1px solid var(--border);
- color: #374151;
- font-weight: 600;
- text-align: left;
- padding: 12px 14px;
- white-space: nowrap;
- }
- tbody td {
- border-top: 1px solid var(--border);
- padding: 12px 14px;
- line-height: 1.5;
- color: #1f2937;
- }
- tbody tr:nth-child(odd) { background: #fcfcfd; }
- tbody tr:hover { background: #eef5ff; }
- /* 轻量分隔与模块层次 */
- .section-note { margin-top: 4px; color: var(--muted); font-size: 12px; }
- /* 响应式优化 */
- @media (max-width: 600px) {
- .page { padding: 16px; }
- .page-header h1 { font-size: 18px; }
- thead th, tbody td { padding: 10px 12px; font-size: 14px; }
- .btn { width: 100%; justify-content: center; }
- }
- </style>
- </head>
- <body>
- <div class="page">
- <header class="page-header">
- <div>
- <h1>设备连接详情</h1>
- <div class="subtext">实时展示当前连接设备的关键信息</div>
- </div>
- </header>
- <div class="grid">
- <!-- 功能入口模块 -->
- <section class="card">
- <div class="card-header">
- <div class="card-title">功能入口</div>
- </div>
- <div class="card-body">
- <div class="toolbar">
- <a id="openPlayerBtn" class="btn btn-player" href="/player.html" target="_blank"
- rel="noopener">打开 WebSocket 播放器测试页面</a>
- <a id="openPlayerNoAudioBtn" class="btn btn-player-no-audio" href="/playerWhthoutAudio.html"
- target="_blank" rel="noopener">打开无音频播放器测试页面</a>
- <a id="openJessibucaBtn" class="btn btn-secondary" href="/jessibuca/demo.html"
- target="_blank" rel="noopener">打开 Jessibuca 播放器测试页面</a>
- <a id="openOfflinePlayerBtn" class="btn btn-secondary" href="/offline_player.html"
- target="_blank" rel="noopener">打开离线播放器(开发参考)</a>
- <a id="openOfflinePlayerNoAudioBtn" class="btn btn-secondary"
- href="/offline_player.html?noAudio=true" target="_blank" rel="noopener">打开离线无音频播放器(开发参考)</a>
- </div>
- <div id="popupTip" class="section-note" style="display:none;">
- 浏览器阻止了弹出窗口。请允许弹窗或点击以下备用链接:
- <a href="/player.html" target="_blank" rel="noopener">播放器</a> |
- <a href="/jessibuca/demo.html" target="_blank" rel="noopener">Jessibuca 播放器</a> |
- <a href="/playerWhthoutAudio.html" target="_blank" rel="noopener">无音频播放器</a> |
- <a href="/offline_player.html?noAudio=true" target="_blank" rel="noopener">离线无音频播放器</a>
- </div>
- </div>
- </section>
- <!-- 设备列表模块 -->
- <section class="card">
- <div class="card-header">
- <div class="card-title">设备列表</div>
- </div>
- <div class="card-body">
- <div class="table-responsive">
- <table>
- <thead>
- <tr>
- <th>远程地址</th>
- <th>SIM卡号</th>
- <th>逻辑通道号</th>
- <th>前缀</th>
- <th>连接时间</th>
- <th>最后活跃时间</th>
- <th>操作</th>
- </tr>
- </thead>
- <tbody id="deviceTableBody">
- <!-- 数据将通过JavaScript填充 -->
- </tbody>
- </table>
- </div>
- </div>
- </section>
- </div>
- </div>
- <script>
- function loadDevices() {
- fetch('/devices')
- .then(response => response.json())
- .then(devices => {
- const tbody = document.getElementById('deviceTableBody');
- tbody.innerHTML = '';
- devices.forEach(device => {
- const row = document.createElement('tr');
- const addressCell = document.createElement('td');
- addressCell.textContent = device.remoteAddress;
- row.appendChild(addressCell);
- const simCell = document.createElement('td');
- simCell.textContent = device.simCardNumber || 'N/A';
- row.appendChild(simCell);
- const channelCell = document.createElement('td');
- channelCell.textContent = device.logicChannelNumber;
- row.appendChild(channelCell);
- const prefixCell = document.createElement('td');
- prefixCell.textContent = device.prefix || '';
- row.appendChild(prefixCell);
- const connectTimeCell = document.createElement('td');
- connectTimeCell.textContent = new Date(device.connectTime).toLocaleString();
- row.appendChild(connectTimeCell);
- const activeTimeCell = document.createElement('td');
- activeTimeCell.textContent = new Date(device.lastActiveTime).toLocaleString();
- row.appendChild(activeTimeCell);
- // 添加操作按钮列
- const actionCell = document.createElement('td');
- actionCell.style.whiteSpace = 'nowrap';
- actionCell.style.display = 'flex';
- actionCell.style.gap = '6px';
- actionCell.style.flexWrap = 'wrap';
-
- // 播放按钮
- const playBtn = document.createElement('button');
- playBtn.textContent = '播放';
- playBtn.className = 'btn btn-player';
- playBtn.onclick = function () {
- // 构建带参数的URL(包含 prefix)
- const url = `/player.html?sim=${encodeURIComponent(device.simCardNumber || '')}&channel=${encodeURIComponent(device.logicChannelNumber)}&prefix=${encodeURIComponent(device.prefix || '')}`;
- // 使用bindOpenInPopup函数打开播放器
- openPlayerInPopup(url, 'PlayerWindow');
- };
- actionCell.appendChild(playBtn);
- // 无音频播放按钮(偏绿色)
- const playNoAudioBtn = document.createElement('button');
- playNoAudioBtn.textContent = '无音频播放';
- playNoAudioBtn.className = 'btn btn-player-no-audio';
- playNoAudioBtn.onclick = function () {
- // 构建带参数的URL(包含 prefix)
- const url = `/playerWhthoutAudio.html?sim=${encodeURIComponent(device.simCardNumber || '')}&channel=${encodeURIComponent(device.logicChannelNumber)}&prefix=${encodeURIComponent(device.prefix || '')}`;
- // 使用bindOpenInPopup函数打开无音频播放器
- openPlayerInPopup(url, 'PlayerWindowNoAudio');
- };
- actionCell.appendChild(playNoAudioBtn);
- row.appendChild(actionCell);
- tbody.appendChild(row);
- });
- })
- .catch(error => console.error('Error:', error));
- }
- // 初始加载
- loadDevices();
- // 每5秒刷新一次
- setInterval(loadDevices, 5000);
- // 打开播放器(新窗口)通用函数
- function openPlayerInPopup(targetUrl, windowName) {
- const availW = Math.max(800, (window.screen && window.screen.availWidth) ? window.screen.availWidth : 1200);
- const availH = Math.max(600, (window.screen && window.screen.availHeight) ? window.screen.availHeight : 800);
- const width = Math.min(1200, Math.floor(availW * 0.9));
- const height = Math.min(800, Math.floor(availH * 0.9));
- const left = Math.max(0, Math.floor((availW - width) / 2));
- const top = Math.max(0, Math.floor((availH - height) / 2));
- const features = `popup=yes,width=${width},height=${height},left=${left},top=${top},menubar=no,toolbar=no,location=no,status=no,resizable=yes,scrollbars=yes`;
- const newWin = window.open(targetUrl, windowName, features);
- if (newWin && typeof newWin.focus === 'function') {
- try { newWin.focus(); } catch (_) { }
- } else {
- // 如果弹窗被阻止,显示提示并直接跳转
- alert('浏览器阻止了弹出窗口,将直接跳转');
- window.open(targetUrl, '_blank');
- }
- }
- // 打开播放器(新窗口)通用绑定
- function bindOpenInPopup(btnId, targetUrl, windowName, tipId) {
- const btn = document.getElementById(btnId);
- const tip = tipId ? document.getElementById(tipId) : null;
- if (!btn) return;
- btn.addEventListener('click', function (e) {
- e.preventDefault();
- const availW = Math.max(800, (window.screen && window.screen.availWidth) ? window.screen.availWidth : 1200);
- const availH = Math.max(600, (window.screen && window.screen.availHeight) ? window.screen.availHeight : 800);
- const width = Math.min(1200, Math.floor(availW * 0.9));
- const height = Math.min(800, Math.floor(availH * 0.9));
- const left = Math.max(0, Math.floor((availW - width) / 2));
- const top = Math.max(0, Math.floor((availH - height) / 2));
- const features = `popup=yes,width=${width},height=${height},left=${left},top=${top},menubar=no,toolbar=no,location=no,status=no,resizable=yes,scrollbars=yes`;
- const newWin = window.open(targetUrl, windowName, features);
- if (newWin && typeof newWin.focus === 'function') {
- try { newWin.focus(); } catch (_) {}
- } else {
- if (tip) tip.style.display = 'block';
- }
- });
- }
- bindOpenInPopup('openPlayerBtn', '/player.html', 'PlayerWindow', 'popupTip');
- bindOpenInPopup('openPlayerNoAudioBtn', '/playerWhthoutAudio.html', 'PlayerWindowNoAudio', 'popupTip');
- bindOpenInPopup('openJessibucaBtn', '/jessibuca/demo.html', 'JessibucaPlayerWindow', 'popupTip');
- bindOpenInPopup('openOfflinePlayerBtn', '/offline_player.html', 'OfflinePlayerWindow', 'popupTip');
- bindOpenInPopup('openOfflinePlayerNoAudioBtn', '/offline_player.html?noAudio=true', 'OfflinePlayerWindowNoAudio', 'popupTip');
- </script>
- </body>
- </html>
|