'use strict'; 'require view'; 'require ui'; 'require uci'; 'require rpc'; 'require poll'; const translations = { 'zh-cn': { 'Bandix 连接监控': 'Bandix 连接监控', '正在加载数据...': '正在加载数据...', '无法获取数据': '无法获取数据', '连接监控': '连接监控', '设备连接统计': '设备连接统计', '全局连接统计': '全局连接统计', '设备': '设备', 'IP地址': 'IP地址', 'MAC地址': 'MAC地址', '活跃TCP': '活跃TCP', '活跃UDP': '活跃UDP', '已关闭TCP': '已关闭TCP', '总连接数': '总连接数', '最后更新': '最后更新', '总连接数统计': '总连接数统计', 'TCP连接数': 'TCP连接数', 'UDP连接数': 'UDP连接数', '已建立TCP': '已建立TCP', 'TIME_WAIT TCP': 'TIME_WAIT TCP', 'CLOSE_WAIT TCP': 'CLOSE_WAIT TCP', '设备总数': '设备总数', '连接监控未启用': '连接监控未启用', '请在设置中启用连接监控功能': '请在设置中启用连接监控功能', '前往设置': '前往设置', '无数据': '无数据', '未知设备': '未知设备', '在线设备': '在线设备', '从未上线': '从未上线', '刚刚': '刚刚', '分钟前': '分钟前', '小时前': '小时前', '天前': '天前', '个月前': '个月前', '年前': '年前', '最后上线': '最后上线', '列表只显示局域网设备连接,数据可能和总连接数不一致。': '列表只显示局域网设备连接,数据可能和总连接数不一致。', 'TCP 状态详情': 'TCP 状态详情' }, 'zh-tw': { 'Bandix 连接监控': 'Bandix 連接監控', '正在加载数据...': '正在載入資料...', '无法获取数据': '無法獲取資料', '连接监控': '連接監控', '设备连接统计': '設備連接統計', '全局连接统计': '全局連接統計', '设备': '設備', 'IP地址': 'IP地址', 'MAC地址': 'MAC地址', '活跃TCP': '活躍TCP', '活跃UDP': '活躍UDP', '已关闭TCP': '已關閉TCP', '总连接数': '總連接數', '最后更新': '最後更新', '总连接数统计': '總連接數統計', 'TCP连接数': 'TCP連接數', 'UDP连接数': 'UDP連接數', '已建立TCP': '已建立TCP', 'TIME_WAIT TCP': 'TIME_WAIT TCP', 'CLOSE_WAIT TCP': 'CLOSE_WAIT TCP', '设备总数': '設備總數', '连接监控未启用': '連接監控未啟用', '请在设置中启用连接监控功能': '請在設置中啟用連接監控功能', '前往设置': '前往設置', '无数据': '無數據', '未知设备': '未知設備', '在线设备': '在線設備', '从未上线': '從未上線', '刚刚': '剛剛', '分钟前': '分鐘前', '小时前': '小時前', '天前': '天前', '个月前': '個月前', '年前': '年前', '最后上线': '最後上線', '列表只显示局域网设备连接,数据可能和总连接数不一致。': '列表只顯示局域網設備連接,數據可能和總連接數不一致。', 'TCP 状态详情': 'TCP 狀態詳情' }, 'en': { 'Bandix 连接监控': 'Bandix Connection Monitor', '正在加载数据...': 'Loading data...', '无法获取数据': 'Unable to fetch data', '连接监控': 'Connection Monitor', '设备连接统计': 'Device Connection Statistics', '全局连接统计': 'Global Connection Statistics', '设备': 'Device', 'IP地址': 'IP Address', 'MAC地址': 'MAC Address', '活跃TCP': 'Active TCP', '活跃UDP': 'Active UDP', '已关闭TCP': 'Closed TCP', '总连接数': 'Total Connections', '最后更新': 'Last Updated', '总连接数统计': 'Total Connections', 'TCP连接数': 'TCP Connections', 'UDP连接数': 'UDP Connections', '已建立TCP': 'Established TCP', 'TIME_WAIT TCP': 'TIME_WAIT TCP', 'CLOSE_WAIT TCP': 'CLOSE_WAIT TCP', '设备总数': 'Total Devices', '连接监控未启用': 'Connection Monitor Disabled', '请在设置中启用连接监控功能': 'Please enable connection monitoring in settings', '前往设置': 'Go to Settings', '无数据': 'No Data', '未知设备': 'Unknown Device', '在线设备': 'Online Devices', '从未上线': 'Never Online', '刚刚': 'Just now', '分钟前': 'minutes ago', '小时前': 'hours ago', '天前': 'days ago', '个月前': 'months ago', '年前': 'years ago', '最后上线': 'Last seen', '列表只显示局域网设备连接,数据可能和总连接数不一致。': 'List only shows LAN device connections, data may differ from total connections.', 'TCP 状态详情': 'TCP Status Details' }, 'fr': { 'Bandix 连接监控': 'Surveillance de Connexion Bandix', '正在加载数据...': 'Chargement des données...', '无法获取数据': 'Impossible de récupérer les données', '连接监控': 'Surveillance des Connexions', '设备连接统计': 'Statistiques de Connexion des Appareils', '全局连接统计': 'Statistiques de Connexion Globales', '设备': 'Appareil', 'IP地址': 'Adresse IP', 'MAC地址': 'Adresse MAC', '活跃TCP': 'TCP Actif', '活跃UDP': 'UDP Actif', '已关闭TCP': 'TCP Fermé', '总连接数': 'Total des Connexions', '最后更新': 'Dernière Mise à Jour', '总连接数统计': 'Total des Connexions', 'TCP连接数': 'Connexions TCP', 'UDP连接数': 'Connexions UDP', '已建立TCP': 'TCP Établi', 'TIME_WAIT TCP': 'TCP TIME_WAIT', 'CLOSE_WAIT TCP': 'TCP CLOSE_WAIT', '设备总数': 'Total des Appareils', '连接监控未启用': 'Surveillance des Connexions Désactivée', '请在设置中启用连接监控功能': 'Veuillez activer la surveillance des connexions dans les paramètres', '前往设置': 'Aller aux Paramètres', '无数据': 'Aucune Donnée', '未知设备': 'Appareil Inconnu', '在线设备': 'Appareils En Ligne', '从未上线': 'Jamais En Ligne', '刚刚': 'À l\'instant', '分钟前': 'il y a minutes', '小时前': 'il y a heures', '天前': 'il y a jours', '个月前': 'il y a mois', '年前': 'il y a années', '最后上线': 'Dernier Vue', '列表只显示局域网设备连接,数据可能和总连接数不一致。': 'La liste ne montre que les connexions des appareils LAN, les données peuvent différer du total des connexions.', 'TCP 状态详情': 'Détails du Statut TCP' }, 'ja': { 'Bandix 连接监控': 'Bandix 接続監視', '正在加载数据...': 'データを読み込み中...', '无法获取数据': 'データを取得できません', '连接监控': '接続監視', '设备连接统计': 'デバイス接続統計', '全局连接统计': 'グローバル接続統計', '设备': 'デバイス', 'IP地址': 'IPアドレス', 'MAC地址': 'MACアドレス', '活跃TCP': 'アクティブTCP', '活跃UDP': 'アクティブUDP', '已关闭TCP': 'クローズドTCP', '总连接数': '総接続数', '最后更新': '最終更新', '总连接数统计': '総接続数', 'TCP连接数': 'TCP接続数', 'UDP连接数': 'UDP接続数', '已建立TCP': 'TCP確立済', 'TIME_WAIT TCP': 'TCP TIME_WAIT', 'CLOSE_WAIT TCP': 'TCP CLOSE_WAIT', '设备总数': '総デバイス数', '连接监控未启用': '接続監視が無効', '请在设置中启用连接监控功能': '設定で接続監視機能を有効にしてください', '前往设置': '設定に移動', '无数据': 'データなし', '未知设备': '不明なデバイス', '在线设备': 'オンラインデバイス', '从未上线': '未接続', '刚刚': 'たった今', '分钟前': '分前', '小时前': '時間前', '天前': '日前', '个月前': 'ヶ月前', '年前': '年前', '最后上线': '最後の接続', '列表只显示局域网设备连接,数据可能和总连接数不一致。': 'リストはLANデバイスの接続のみを表示し、データは総接続数と異なる場合があります。', 'TCP 状态详情': 'TCP状態詳細' }, 'ru': { 'Bandix 连接监控': 'Мониторинг Соединений Bandix', '正在加载数据...': 'Загрузка данных...', '无法获取数据': 'Не удалось получить данные', '连接监控': 'Мониторинг Соединений', '设备连接统计': 'Статистика Соединений Устройств', '全局连接统计': 'Глобальная Статистика Соединений', '设备': 'Устройство', 'IP地址': 'IP-адрес', 'MAC地址': 'MAC-адрес', '活跃TCP': 'Активные TCP', '活跃UDP': 'Активные UDP', '已关闭TCP': 'Закрытые TCP', '总连接数': 'Всего Соединений', '最后更新': 'Последнее Обновление', '总连接数统计': 'Всего Соединений', 'TCP连接数': 'TCP Соединения', 'UDP连接数': 'UDP Соединения', '已建立TCP': 'Установленные TCP', 'TIME_WAIT TCP': 'TCP TIME_WAIT', 'CLOSE_WAIT TCP': 'TCP CLOSE_WAIT', '设备总数': 'Всего Устройств', '连接监控未启用': 'Мониторинг Соединений Отключен', '请在设置中启用连接监控功能': 'Пожалуйста, включите мониторинг соединений в настройках', '前往设置': 'Перейти к Настройкам', '无数据': 'Нет Данных', '未知设备': 'Неизвестное Устройство', '在线设备': 'Устройства Онлайн', '从未上线': 'Никогда Не Было Онлайн', '刚刚': 'Только что', '分钟前': 'минут назад', '小时前': 'часов назад', '天前': 'дней назад', '个月前': 'месяцев назад', '年前': 'лет назад', '最后上线': 'Последний раз видели', '列表只显示局域网设备连接,数据可能和总连接数不一致。': 'Список показывает только соединения LAN-устройств, данные могут отличаться от общего количества соединений.', 'TCP 状态详情': 'Детали Статуса TCP' } }; function getTranslation(key, language) { return translations[language]?.[key] || key; } // 获取系统语言 function getSystemLanguage() { var luciLang = uci.get('luci', 'main', 'lang'); if (luciLang && translations[luciLang]) { return luciLang; } var systemLang = document.documentElement.lang || 'en'; if (translations[systemLang]) { return systemLang; } return 'en'; } // 检查是否为暗黑模式 function isDarkMode() { // 首先检查用户设置的主题 var userTheme = uci.get('bandix', 'general', 'theme'); if (userTheme) { if (userTheme === 'dark') { return true; } else if (userTheme === 'light') { return false; } // 如果是 'auto',继续检查系统主题 } // 获取 LuCI 主题设置 var mediaUrlBase = uci.get('luci', 'main', 'mediaurlbase'); if (mediaUrlBase && mediaUrlBase.toLowerCase().includes('dark')) { return true; } // 如果是 argon 主题,检查 argon 配置 if (mediaUrlBase && mediaUrlBase.toLowerCase().includes('argon')) { var argonMode = uci.get('argon', '@global[0]', 'mode'); if (argonMode) { if (argonMode.toLowerCase() === 'dark') { return true; } else if (argonMode.toLowerCase() === 'light') { return false; } // 如果是 'normal' 或 'auto',使用浏览器检测系统颜色偏好 if (argonMode.toLowerCase() === 'normal' || argonMode.toLowerCase() === 'auto') { if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { return true; } return false; } } } // 默认情况下也使用浏览器检测系统颜色偏好 if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { return true; } return false; } // 格式化时间戳 function formatTimestamp(timestamp) { if (!timestamp) return getTranslation('从未上线', getSystemLanguage()); var now = Math.floor(Date.now() / 1000); var diff = now - timestamp; var language = getSystemLanguage(); if (diff < 60) { return getTranslation('刚刚', language); } else if (diff < 3600) { var minutes = Math.floor(diff / 60); return minutes + ' ' + getTranslation('分钟前', language); } else if (diff < 86400) { var hours = Math.floor(diff / 3600); return hours + ' ' + getTranslation('小时前', language); } else if (diff < 2592000) { var days = Math.floor(diff / 86400); return days + ' ' + getTranslation('天前', language); } else if (diff < 31536000) { var months = Math.floor(diff / 2592000); return months + ' ' + getTranslation('个月前', language); } else { var years = Math.floor(diff / 31536000); return years + ' ' + getTranslation('年前', language); } } // 格式化设备名称 function formatDeviceName(device) { if (device.hostname && device.hostname !== '') { return device.hostname; } return device.ip_address || device.mac_address || getTranslation('未知设备', getSystemLanguage()); } // RPC调用 var callGetConnection = rpc.declare({ object: 'luci.bandix', method: 'getConnection', expect: {} }); return view.extend({ load: function () { return Promise.all([ uci.load('bandix'), uci.load('luci'), uci.load('argon').catch(function () { return null; }) ]); }, render: function (data) { var language = uci.get('bandix', 'general', 'language'); if (!language || language === 'auto') { language = getSystemLanguage(); } var darkMode = isDarkMode(); var connectionEnabled = uci.get('bandix', 'connections', 'enabled') === '1'; // 创建样式 var style = E('style', {}, ` .bandix-connection-container { margin: 0; padding: 8px; background-color: ${darkMode ? '#1E1E1E' : '#f8fafc'}; min-height: calc(100vh - 100px); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; color: ${darkMode ? '#e2e8f0' : '#1f2937'}; border-radius: 8px; } .bandix-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } .bandix-title { font-size: 1.5rem; font-weight: 700; color: ${darkMode ? '#f1f5f9' : '#1f2937'}; margin: 0; } .bandix-badge { background-color: ${darkMode ? '#333333' : '#f3f4f6'}; border: 1px solid ${darkMode ? '#252526' : '#d1d5db'}; border-radius: 6px; padding: 4px 12px; font-size: 0.875rem; color: ${darkMode ? '#e2e8f0' : '#374151'}; } .bandix-alert { background-color: ${darkMode ? '#451a03' : '#fef3c7'}; border: 1px solid ${darkMode ? '#92400e' : '#f59e0b'}; border-radius: 8px; padding: 8px; margin-bottom: 12px; display: flex; align-items: center; gap: 8px; color: ${darkMode ? '#fbbf24' : '#92400e'}; } .bandix-alert-icon { color: ${darkMode ? '#fbbf24' : '#f59e0b'}; font-size: 1rem; } .bandix-card { background-color: ${darkMode ? '#252526' : 'white'}; border-radius: 12px; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, ${darkMode ? '0.3' : '0.1'}); margin-bottom: 24px; overflow: hidden; } .bandix-card-header { padding: 20px 24px 16px; border-bottom: 1px solid ${darkMode ? '#252526' : '#e5e7eb'}; background-color: ${darkMode ? '#333333' : '#fafafa'}; } .bandix-card-title { font-size: 1.125rem; font-weight: 600; color: ${darkMode ? '#f1f5f9' : '#1f2937'}; margin: 0; display: flex; align-items: center; gap: 8px; } .bandix-card-body { padding: 24px; } .stats-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-bottom: 32px; } .stats-card { background-color: ${darkMode ? '#252526' : 'white'}; border-radius: 12px; padding: 20px; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, ${darkMode ? '0.3' : '0.1'}); display: flex; flex-direction: column; align-items: center; text-align: center; } .stats-card-title { font-size: 0.875rem; font-weight: 500; color: ${darkMode ? '#9ca3af' : '#6b7280'}; margin: 0 0 12px 0; } .stats-card-main-value { font-size: 2.25rem; font-weight: 700; color: ${darkMode ? '#f1f5f9' : '#1f2937'}; margin: 0 0 8px 0; line-height: 1; } .stats-card-details { margin-top: 12px; display: flex; flex-direction: column; gap: 6px; width: 100%; } .stats-detail-row { display: flex; justify-content: space-between; align-items: center; font-size: 0.875rem; } .stats-detail-label { color: ${darkMode ? '#9ca3af' : '#6b7280'}; font-weight: 500; } .stats-detail-value { font-weight: 600; color: ${darkMode ? '#e2e8f0' : '#374151'}; } .bandix-table { width: 100%; border-collapse: collapse; background-color: transparent; table-layout: fixed; } .bandix-table th { background-color: ${darkMode ? '#333333' : '#f9fafb'}; padding: 16px 20px; text-align: left; font-weight: 600; color: ${darkMode ? '#e2e8f0' : '#374151'}; border: none; font-size: 0.875rem; white-space: nowrap; } .bandix-table th:nth-child(1) { width: 30%; } .bandix-table th:nth-child(2) { width: 12%; } .bandix-table th:nth-child(3) { width: 12%; } .bandix-table th:nth-child(4) { width: 31%; } .bandix-table th:nth-child(5) { width: 15%; } .bandix-table td { padding: 16px 20px; border-bottom: 1px solid ${darkMode ? '#252526' : '#f1f5f9'}; vertical-align: middle; word-break: break-word; overflow-wrap: break-word; color: ${darkMode ? '#cbd5e1' : 'inherit'}; } .device-info { display: flex; align-items: center; gap: 12px; } .device-status { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; } .device-status.online { background-color: #10b981; } .device-status.offline { background-color: #9ca3af; } .device-details { min-width: 0; flex: 1; } .device-name { font-weight: 600; color: ${darkMode ? '#f1f5f9' : '#1f2937'}; display: flex; align-items: center; gap: 8px; margin-bottom: 4px; } .device-ip { color: ${darkMode ? '#94a3b8' : '#6b7280'}; font-size: 0.875rem; } .device-mac { color: ${darkMode ? '#64748b' : '#9ca3af'}; font-size: 0.75rem; } .tcp-status-details { display: flex; flex-direction: column; gap: 6px; font-size: 0.875rem; } .tcp-status-item { display: flex; align-items: center; gap: 8px; } .tcp-status-label { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 0.75rem; font-weight: 600; color: white; min-width: 50px; text-align: center; } .tcp-status-label.established { background-color: #10b981; } .tcp-status-label.time-wait { background-color: #f59e0b; } .tcp-status-label.closed { background-color: #6b7280; } .tcp-status-value { font-weight: 600; color: ${darkMode ? '#e2e8f0' : '#374151'}; } .loading-state { text-align: center; padding: 40px; color: ${darkMode ? '#94a3b8' : '#6b7280'}; font-style: italic; } .error-state { text-align: center; padding: 40px; color: ${darkMode ? '#f87171' : '#ef4444'}; } .btn { display: inline-flex; align-items: center; gap: 8px; padding: 8px 16px; border-radius: 6px; font-size: 0.875rem; font-weight: 500; text-decoration: none; border: none; cursor: pointer; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); } .btn-primary { background-color: #3b82f6; color: white; } .btn-primary:hover { background-color: #2563eb; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); } `); document.head.appendChild(style); var container = E('div', { 'class': 'bandix-connection-container' }); // 页面标题 var header = E('div', { 'class': 'bandix-header' }, [ E('h1', { 'class': 'bandix-title' }, getTranslation('Bandix 连接监控', language)) ]); container.appendChild(header); // 检查连接监控是否启用 if (!connectionEnabled) { var alertDiv = E('div', { 'class': 'bandix-alert' }, [ E('span', { 'class': 'bandix-alert-icon' }, '⚠'), E('div', {}, [ E('strong', {}, getTranslation('连接监控未启用', language)), E('p', { 'style': 'margin: 4px 0 0 0;' }, getTranslation('请在设置中启用连接监控功能', language)) ]) ]); container.appendChild(alertDiv); var settingsCard = E('div', { 'class': 'bandix-card' }, [ E('div', { 'class': 'bandix-card-body', 'style': 'text-align: center;' }, [ E('a', { 'href': '/cgi-bin/luci/admin/network/bandix/settings', 'class': 'btn btn-primary' }, getTranslation('前往设置', language)) ]) ]); container.appendChild(settingsCard); return container; } // 添加提示信息 var infoAlert = E('div', { 'class': 'bandix-alert' }, [ E('span', { 'class': 'bandix-alert-icon' }, '⚠️'), E('span', {}, getTranslation('列表只显示局域网设备连接,数据可能和总连接数不一致。', language)) ]); container.appendChild(infoAlert); // 全局统计卡片 var statsGrid = E('div', { 'class': 'stats-grid' }, [ E('div', { 'class': 'stats-card' }, [ E('div', { 'class': 'stats-card-title' }, getTranslation('总连接数统计', language)), E('div', { 'class': 'stats-card-main-value', 'id': 'total-connections' }, '-') ]), E('div', { 'class': 'stats-card' }, [ E('div', { 'class': 'stats-card-title' }, getTranslation('TCP连接数', language)), E('div', { 'class': 'stats-card-main-value', 'id': 'tcp-connections' }, '-'), E('div', { 'class': 'stats-card-details' }, [ E('div', { 'class': 'stats-detail-row' }, [ E('span', { 'class': 'stats-detail-label' }, 'ESTABLISHED'), E('span', { 'class': 'stats-detail-value', 'id': 'established-tcp' }, '-') ]), E('div', { 'class': 'stats-detail-row' }, [ E('span', { 'class': 'stats-detail-label' }, 'TIME_WAIT'), E('span', { 'class': 'stats-detail-value', 'id': 'time-wait-tcp' }, '-') ]), E('div', { 'class': 'stats-detail-row' }, [ E('span', { 'class': 'stats-detail-label' }, 'CLOSE_WAIT'), E('span', { 'class': 'stats-detail-value', 'id': 'close-wait-tcp' }, '-') ]) ]) ]), E('div', { 'class': 'stats-card' }, [ E('div', { 'class': 'stats-card-title' }, getTranslation('UDP连接数', language)), E('div', { 'class': 'stats-card-main-value', 'id': 'udp-connections' }, '-') ]) ]); container.appendChild(statsGrid); // 设备连接统计表格 var deviceCard = E('div', { 'class': 'bandix-card' }, [ E('div', { 'class': 'bandix-card-body' }, [ E('div', { 'id': 'device-table-container' }, [ E('table', { 'class': 'bandix-table' }, [ E('thead', {}, [ E('tr', {}, [ E('th', {}, getTranslation('设备', language)), E('th', {}, 'TCP'), E('th', {}, 'UDP'), E('th', {}, getTranslation('TCP 状态详情', language)), E('th', {}, getTranslation('总连接数', language)) ]) ]), E('tbody', {}) ]) ]) ]) ]); container.appendChild(deviceCard); // 更新全局统计 function updateGlobalStats(stats) { if (!stats) return; document.getElementById('total-connections').textContent = stats.total_connections || 0; document.getElementById('tcp-connections').textContent = stats.tcp_connections || 0; document.getElementById('udp-connections').textContent = stats.udp_connections || 0; document.getElementById('established-tcp').textContent = stats.established_tcp || 0; document.getElementById('time-wait-tcp').textContent = stats.time_wait_tcp || 0; document.getElementById('close-wait-tcp').textContent = stats.close_wait_tcp || 0; } // 更新设备表格 function updateDeviceTable(devices) { var container = document.getElementById('device-table-container'); if (!devices || devices.length === 0) { container.innerHTML = ''; container.appendChild(E('div', { 'class': 'loading-state' }, getTranslation('无数据', language))); return; } var table = E('table', { 'class': 'bandix-table' }, [ E('thead', {}, [ E('tr', {}, [ E('th', {}, getTranslation('设备', language)), E('th', {}, 'TCP'), E('th', {}, 'UDP'), E('th', {}, getTranslation('TCP 状态详情', language)), E('th', {}, getTranslation('总连接数', language)) ]) ]), E('tbody', {}, devices.map(function (device) { return E('tr', {}, [ E('td', {}, [ E('div', { 'class': 'device-info' }, [ E('div', { 'class': 'device-status online' }), E('div', { 'class': 'device-details' }, [ E('div', { 'class': 'device-name' }, formatDeviceName(device)), E('div', { 'class': 'device-ip' }, device.ip_address || '-'), E('div', { 'class': 'device-mac' }, device.mac_address || '-') ]) ]) ]), E('td', { 'style': 'font-weight: 600;' }, device.tcp_connections || 0), E('td', { 'style': 'font-weight: 600;' }, device.udp_connections || 0), E('td', {}, [ E('div', { 'class': 'tcp-status-details' }, [ E('div', { 'class': 'tcp-status-item' }, [ E('span', { 'class': 'tcp-status-label established' }, 'EST'), E('span', { 'class': 'tcp-status-value' }, device.established_tcp || 0) ]), E('div', { 'class': 'tcp-status-item' }, [ E('span', { 'class': 'tcp-status-label time-wait' }, 'WAIT'), E('span', { 'class': 'tcp-status-value' }, device.time_wait_tcp || 0) ]), E('div', { 'class': 'tcp-status-item' }, [ E('span', { 'class': 'tcp-status-label closed' }, 'CLOSE'), E('span', { 'class': 'tcp-status-value' }, device.close_wait_tcp || 0) ]) ]) ]), E('td', {}, E('strong', {}, device.total_connections || 0)) ]); })) ]); container.innerHTML = ''; container.appendChild(table); } // 显示错误信息 function showError(message) { var container = document.getElementById('device-table-container'); container.innerHTML = ''; container.appendChild(E('div', { 'class': 'error-state' }, message)); } // 定义更新连接数据的函数 function updateConnectionData() { return callGetConnection().then(function (result) { if (result && result.status === 'success' && result.data) { updateGlobalStats(result.data.global_stats); updateDeviceTable(result.data.devices); } else { showError(getTranslation('无法获取数据', language)); } }).catch(function (error) { console.error('Failed to load connection data:', error); showError(getTranslation('无法获取数据', language)); }); } // 轮询获取数据 poll.add(updateConnectionData, 1); // 立即执行一次,不等待轮询 updateConnectionData(); return container; } });