💐 Sync 2025-11-17 00:10:18
This commit is contained in:
@@ -5,331 +5,33 @@
|
||||
'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;
|
||||
}
|
||||
// 暗色模式检测已改为使用 CSS 媒体查询 @media (prefers-color-scheme: dark)
|
||||
|
||||
// 格式化时间戳
|
||||
function formatTimestamp(timestamp) {
|
||||
if (!timestamp) return getTranslation('从未上线', getSystemLanguage());
|
||||
if (!timestamp) return _('Never Online');
|
||||
|
||||
var now = Math.floor(Date.now() / 1000);
|
||||
var diff = now - timestamp;
|
||||
var language = getSystemLanguage();
|
||||
|
||||
if (diff < 60) {
|
||||
return getTranslation('刚刚', language);
|
||||
return _('Just now');
|
||||
} else if (diff < 3600) {
|
||||
var minutes = Math.floor(diff / 60);
|
||||
return minutes + ' ' + getTranslation('分钟前', language);
|
||||
return minutes + ' ' + _('minutes ago');
|
||||
} else if (diff < 86400) {
|
||||
var hours = Math.floor(diff / 3600);
|
||||
return hours + ' ' + getTranslation('小时前', language);
|
||||
return hours + ' ' + _('hours ago');
|
||||
} else if (diff < 2592000) {
|
||||
var days = Math.floor(diff / 86400);
|
||||
return days + ' ' + getTranslation('天前', language);
|
||||
return days + ' ' + _('days ago');
|
||||
} else if (diff < 31536000) {
|
||||
var months = Math.floor(diff / 2592000);
|
||||
return months + ' ' + getTranslation('个月前', language);
|
||||
return months + ' ' + _('months ago');
|
||||
} else {
|
||||
var years = Math.floor(diff / 31536000);
|
||||
return years + ' ' + getTranslation('年前', language);
|
||||
return years + ' ' + _('years ago');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,7 +40,7 @@ function formatDeviceName(device) {
|
||||
if (device.hostname && device.hostname !== '') {
|
||||
return device.hostname;
|
||||
}
|
||||
return device.ip_address || device.mac_address || getTranslation('未知设备', getSystemLanguage());
|
||||
return device.ip_address || device.mac_address || _('Unknown Device');
|
||||
}
|
||||
|
||||
// RPC调用
|
||||
@@ -360,63 +62,42 @@ return view.extend({
|
||||
},
|
||||
|
||||
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: 16px;
|
||||
background-color: ${darkMode ? '#1a1a1a' : '#f8fafc'};
|
||||
min-height: calc(100vh - 100px);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
color: ${darkMode ? '#e2e8f0' : '#1f2937'};
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.bandix-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.bandix-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: ${darkMode ? '#f1f5f9' : '#1f2937'};
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.bandix-badge {
|
||||
background-color: ${darkMode ? '#2a2a2a' : '#f8fafc'};
|
||||
border: 1px solid ${darkMode ? '#444444' : '#cbd5e1'};
|
||||
border-radius: 4px;
|
||||
padding: 4px 10px;
|
||||
font-size: 0.875rem;
|
||||
color: ${darkMode ? '#d0d0d0' : '#475569'};
|
||||
}
|
||||
|
||||
.bandix-alert {
|
||||
background-color: ${darkMode ? '#2a2a2a' : '#eff6ff'};
|
||||
border-left: 3px solid ${darkMode ? '#3b82f6' : '#2563eb'};
|
||||
border-radius: 4px;
|
||||
padding: 10px 12px;
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: ${darkMode ? '#d0d0d0' : '#1e293b'};
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.bandix-alert-icon {
|
||||
color: ${darkMode ? '#60a5fa' : '#2563eb'};
|
||||
font-size: 0.875rem;
|
||||
font-weight: 700;
|
||||
width: 18px;
|
||||
@@ -424,72 +105,62 @@ return view.extend({
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 2px solid currentColor;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.bandix-card {
|
||||
background-color: ${darkMode ? '#2a2a2a' : 'white'};
|
||||
border-radius: 8px;
|
||||
border: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, ${darkMode ? '0.3' : '0.08'});
|
||||
margin-bottom: 24px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bandix-card-header {
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
|
||||
background-color: ${darkMode ? '#2a2a2a' : '#f8fafc'};
|
||||
}
|
||||
|
||||
.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: 0;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.stats-card {
|
||||
background-color: ${darkMode ? '#2a2a2a' : 'white'};
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
border: 1px solid ${darkMode ? '#444444' : '#e2e8f0'};
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, ${darkMode ? '0.3' : '0.08'});
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
.bandix-connection-container > .cbi-section:first-of-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.bandix-connection-container > .cbi-section:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.stats-card-title {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: ${darkMode ? '#94a3b8' : '#64748b'};
|
||||
opacity: 0.7;
|
||||
margin: 0 0 12px 0;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
}
|
||||
|
||||
.stats-grid .cbi-section {
|
||||
padding: 16px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
transition: box-shadow 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.stats-grid .cbi-section:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.stats-grid .cbi-section {
|
||||
border-color: rgba(255, 255, 255, 0.15);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.stats-grid .cbi-section:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
.stats-card-main-value {
|
||||
font-size: 2.25rem;
|
||||
font-weight: 700;
|
||||
color: ${darkMode ? '#f1f5f9' : '#1f2937'};
|
||||
margin: 0 0 8px 0;
|
||||
line-height: 1;
|
||||
}
|
||||
@@ -510,28 +181,23 @@ return view.extend({
|
||||
}
|
||||
|
||||
.stats-detail-label {
|
||||
color: ${darkMode ? '#9ca3af' : '#6b7280'};
|
||||
opacity: 0.7;
|
||||
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 ? '#2a2a2a' : '#f8fafc'};
|
||||
padding: 12px 16px;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
color: ${darkMode ? '#d0d0d0' : '#475569'};
|
||||
border: none;
|
||||
font-size: 0.875rem;
|
||||
white-space: nowrap;
|
||||
@@ -545,11 +211,9 @@ return view.extend({
|
||||
|
||||
.bandix-table td {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid ${darkMode ? '#333333' : '#f1f5f9'};
|
||||
vertical-align: middle;
|
||||
word-break: break-word;
|
||||
overflow-wrap: break-word;
|
||||
color: ${darkMode ? '#d0d0d0' : '#334155'};
|
||||
}
|
||||
|
||||
|
||||
@@ -581,7 +245,6 @@ return view.extend({
|
||||
|
||||
.device-name {
|
||||
font-weight: 600;
|
||||
color: ${darkMode ? '#f1f5f9' : '#1f2937'};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
@@ -589,12 +252,12 @@ return view.extend({
|
||||
}
|
||||
|
||||
.device-ip {
|
||||
color: ${darkMode ? '#94a3b8' : '#6b7280'};
|
||||
opacity: 0.7;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.device-mac {
|
||||
color: ${darkMode ? '#64748b' : '#9ca3af'};
|
||||
opacity: 0.6;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
@@ -637,43 +300,18 @@ return view.extend({
|
||||
|
||||
.tcp-status-value {
|
||||
font-weight: 600;
|
||||
color: ${darkMode ? '#e2e8f0' : '#374151'};
|
||||
}
|
||||
|
||||
.loading-state {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: ${darkMode ? '#94a3b8' : '#6b7280'};
|
||||
opacity: 0.7;
|
||||
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: 4px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #3b82f6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #2563eb;
|
||||
}
|
||||
`);
|
||||
document.head.appendChild(style);
|
||||
@@ -682,28 +320,27 @@ return view.extend({
|
||||
|
||||
// 页面标题
|
||||
var header = E('div', { 'class': 'bandix-header' }, [
|
||||
E('h1', { 'class': 'bandix-title' }, getTranslation('Bandix 连接监控', language))
|
||||
E('h1', { 'class': 'bandix-title' }, _('Bandix Connection Monitor'))
|
||||
]);
|
||||
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('strong', {}, _('Connection Monitor Disabled')),
|
||||
E('p', { 'style': 'margin: 4px 0 0 0;' },
|
||||
getTranslation('请在设置中启用连接监控功能', language))
|
||||
_('Please enable connection monitoring in settings'))
|
||||
])
|
||||
]);
|
||||
container.appendChild(alertDiv);
|
||||
|
||||
var settingsCard = E('div', { 'class': 'bandix-card' }, [
|
||||
E('div', { 'class': 'bandix-card-body', 'style': 'text-align: center;' }, [
|
||||
var settingsCard = E('div', { 'class': 'cbi-section' }, [
|
||||
E('div', { 'style': 'text-align: center; padding: 16px;' }, [
|
||||
E('a', {
|
||||
'href': '/cgi-bin/luci/admin/network/bandix/settings',
|
||||
'class': 'btn btn-primary'
|
||||
}, getTranslation('前往设置', language))
|
||||
'class': 'cbi-button cbi-button-positive'
|
||||
}, _('Go to Settings'))
|
||||
])
|
||||
]);
|
||||
container.appendChild(settingsCard);
|
||||
@@ -712,19 +349,18 @@ return view.extend({
|
||||
|
||||
// 添加提示信息
|
||||
var infoAlert = E('div', { 'class': 'bandix-alert' }, [
|
||||
E('span', { 'class': 'bandix-alert-icon' }, '!'),
|
||||
E('span', {}, getTranslation('列表只显示局域网设备连接,数据可能和总连接数不一致。', language))
|
||||
E('span', {}, _('List only shows LAN device connections, data may differ from total connections.'))
|
||||
]);
|
||||
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': 'cbi-section' }, [
|
||||
E('div', { 'class': 'stats-card-title' }, _('Total Connections')),
|
||||
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': 'cbi-section' }, [
|
||||
E('div', { 'class': 'stats-card-title' }, _('TCP Connections')),
|
||||
E('div', { 'class': 'stats-card-main-value', 'id': 'tcp-connections' }, '-'),
|
||||
E('div', { 'class': 'stats-card-details' }, [
|
||||
E('div', { 'class': 'stats-detail-row' }, [
|
||||
@@ -741,25 +377,26 @@ return view.extend({
|
||||
])
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'stats-card' }, [
|
||||
E('div', { 'class': 'stats-card-title' }, getTranslation('UDP连接数', language)),
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
E('div', { 'class': 'stats-card-title' }, _('UDP Connections')),
|
||||
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' }, [
|
||||
var deviceCard = E('div', { 'class': 'cbi-section' }, [
|
||||
E('h3', {}, _('Device Connection Statistics')),
|
||||
E('div', {}, [
|
||||
E('div', { 'id': 'device-table-container' }, [
|
||||
E('table', { 'class': 'bandix-table' }, [
|
||||
E('thead', {}, [
|
||||
E('tr', {}, [
|
||||
E('th', {}, getTranslation('设备', language)),
|
||||
E('th', {}, _('Device')),
|
||||
E('th', {}, 'TCP'),
|
||||
E('th', {}, 'UDP'),
|
||||
E('th', {}, getTranslation('TCP 状态详情', language)),
|
||||
E('th', {}, getTranslation('总连接数', language))
|
||||
E('th', {}, _('TCP Status Details')),
|
||||
E('th', {}, _('Total Connections'))
|
||||
])
|
||||
]),
|
||||
E('tbody', {})
|
||||
@@ -788,18 +425,18 @@ return view.extend({
|
||||
if (!devices || devices.length === 0) {
|
||||
container.innerHTML = '';
|
||||
container.appendChild(E('div', { 'class': 'loading-state' },
|
||||
getTranslation('无数据', language)));
|
||||
_('No Data')));
|
||||
return;
|
||||
}
|
||||
|
||||
var table = E('table', { 'class': 'bandix-table' }, [
|
||||
E('thead', {}, [
|
||||
E('tr', {}, [
|
||||
E('th', {}, getTranslation('设备', language)),
|
||||
E('th', {}, _('Device')),
|
||||
E('th', {}, 'TCP'),
|
||||
E('th', {}, 'UDP'),
|
||||
E('th', {}, getTranslation('TCP 状态详情', language)),
|
||||
E('th', {}, getTranslation('总连接数', language))
|
||||
E('th', {}, _('TCP Status Details')),
|
||||
E('th', {}, _('Total Connections'))
|
||||
])
|
||||
]),
|
||||
E('tbody', {}, devices.map(function (device) {
|
||||
@@ -855,11 +492,11 @@ return view.extend({
|
||||
updateGlobalStats(result.data.global_stats);
|
||||
updateDeviceTable(result.data.devices);
|
||||
} else {
|
||||
showError(getTranslation('无法获取数据', language));
|
||||
showError(_('Unable to fetch data'));
|
||||
}
|
||||
}).catch(function (error) {
|
||||
console.error('Failed to load connection data:', error);
|
||||
showError(getTranslation('无法获取数据', language));
|
||||
showError(_('Unable to fetch data'));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -869,6 +506,61 @@ return view.extend({
|
||||
// 立即执行一次,不等待轮询
|
||||
updateConnectionData();
|
||||
|
||||
// 自动适应主题背景色和文字颜色的函数
|
||||
function applyThemeColors() {
|
||||
try {
|
||||
var mainElement = document.querySelector('.main') || document.body;
|
||||
var computedStyle = window.getComputedStyle(mainElement);
|
||||
var bgColor = computedStyle.backgroundColor;
|
||||
|
||||
// 如果父元素有背景色,应用到容器和卡片
|
||||
if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') {
|
||||
var containerEl = document.querySelector('.bandix-connection-container');
|
||||
if (containerEl) {
|
||||
containerEl.style.backgroundColor = bgColor;
|
||||
}
|
||||
|
||||
// 应用到表格表头
|
||||
var tableHeaders = document.querySelectorAll('.bandix-table th');
|
||||
tableHeaders.forEach(function(th) {
|
||||
th.style.backgroundColor = bgColor;
|
||||
});
|
||||
}
|
||||
|
||||
// 检测文字颜色并应用
|
||||
var textColor = computedStyle.color;
|
||||
if (textColor && textColor !== 'rgba(0, 0, 0, 0)') {
|
||||
var containerEl = document.querySelector('.bandix-connection-container');
|
||||
if (containerEl) {
|
||||
containerEl.style.color = textColor;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// 如果检测失败,使用默认值
|
||||
console.log('Theme adaptation:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始应用主题颜色
|
||||
setTimeout(applyThemeColors, 100);
|
||||
|
||||
// 监听 DOM 变化,自动应用到新创建的元素
|
||||
if (typeof MutationObserver !== 'undefined') {
|
||||
var observer = new MutationObserver(function(mutations) {
|
||||
applyThemeColors();
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
var container = document.querySelector('.bandix-connection-container');
|
||||
if (container) {
|
||||
observer.observe(container, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user