// ==UserScript== // @name 中兴路由器(ZTE) 增强 // @name:en ZTE-Stat_Max // @namespace http://tampermonkey.net/ // @version 5.8.2 // @description QQ群 680464365 // @description:en https://github.com/ucxn/ZTE-Stat_Max // @author 哥哥科技 // @noframes // @include /^https?:\/\/10(\.[0-9]{1,3}){3}(:\d+)?\/.*$/ // @match http://192.168.5.1 // @include http://192.168.* // @include https://192.168.* // @include http://172.16.* // @include http://zte.home* // @grant none // @license GPL-3.0-or-later // @downloadURL https://update.greasyfork.icu/scripts/576199/%E4%B8%AD%E5%85%B4%E8%B7%AF%E7%94%B1%E5%99%A8%28ZTE%29%20%E5%A2%9E%E5%BC%BA.user.js // @updateURL https://update.greasyfork.icu/scripts/576199/%E4%B8%AD%E5%85%B4%E8%B7%AF%E7%94%B1%E5%99%A8%28ZTE%29%20%E5%A2%9E%E5%BC%BA.meta.js // ==/UserScript== (function() { 'use strict'; function escapeHTML(str) { if (!str) return ''; return String(str).replace(/[&<>'"]/g, function(match) { return { '&': '&', '<': '<', '>': '>', "'": ''', '"': '"' }[match]; }); } // ======== [0] 用户极客环境变量配置区 ======== const CONFIG = { routerIP: "192.168.5.1", // 路由器内网 IP,用于防断线保活模块的后台寻址 calcMode: 1, // 1: 上行/下行倍数模式, 0: 上行占总和比例模式 ratioExtremeUp: 10,// 极端上传判定阈值 (> 1000%) ratioWarnUp: 0.07,// 重度上传警告阈值 (> 7%) ratioExtremeDown: 0.01, // 极端下载判定阈值 (< 1%) ratioThreshold: 7, // (仅calcMode=0时有效) 上传占比报警阈值(%) portMap: { "eth1": "网口 1", "eth2": "网口 2", "eth3": "网口 3", "eth4": "网口 4", "wl0": "2.4G", "wl1": "5.2G", "wl2": "5.8G" } }; const State = { lastTime: 0, wanUpSpeed: 0, wanDownSpeed: 0, wanUpTraffic: 0, wanDownTraffic: 0, clients: {} }; let isFetching = false; const parser = new DOMParser(); // ======== [1] 换算引擎 (1000进制 vs 1024进制) ======== function speedToBps(speedStr) { if (!speedStr) return 0; let match = speedStr.match(/([\d.]+)\s*(G|M|K)?bps/i); if (!match) return 0; let val = parseFloat(match[1]); let unit = (match[2] || "").toUpperCase(); if (unit === 'G') return val * 1000000000; if (unit === 'M') return val * 1000000; if (unit === 'K') return val * 1000; return val; } function formatBps(bps) { if (bps >= 1000000) return (bps / 1000000).toFixed(3) + ' Mbps'; if (bps >= 1000) return (bps / 1000).toFixed(2) + ' Kbps'; return Math.round(bps) + ' bps'; } function formatBytes(bps) { let bytesPerSec = bps / 8; if (bytesPerSec >= 1048576) return (bytesPerSec / 1048576).toFixed(3) + ' MiB/s'; if (bytesPerSec >= 1024) return (bytesPerSec / 1024).toFixed(2) + ' KiB/s'; return Math.round(bytesPerSec) + ' B/s'; } function formatVolume(bits) { let bytes = bits / 8; if (bytes >= 1073741824) return (bytes / 1073741824).toFixed(3) + ' GiB'; if (bytes >= 1048576) return (bytes / 1048576).toFixed(2) + ' MiB'; if (bytes >= 1024) return (bytes / 1024).toFixed(1) + ' KiB'; return Math.round(bytes) + ' B'; } // 新增:双轨制流量锚定渲染 (强行将官方数据约束在我方单位下) function formatVolumeDual(bitsIntegral, bitsOfficial) { let bytes = bitsIntegral / 8; let bytesOff = bitsOfficial / 8; if (bytes >= 1073741824) return (bytes / 1073741824).toFixed(3) + ' | ' + (bytesOff / 1073741824).toFixed(4) + ' GiB'; if (bytes >= 1048576) return (bytes / 1048576).toFixed(2) + ' | ' + (bytesOff / 1048576).toFixed(3) + ' MiB'; if (bytes >= 1024) return (bytes / 1024).toFixed(2) + ' | ' + (bytesOff / 1024).toFixed(1) + ' KiB'; return Math.round(bytes) + ' | ' + Math.round(bytesOff) + ' B';} // 需求 2.2:区间流量单字母简写引擎 (放弃对齐换取空间) function formatShortVolume(bits) { let bytes = bits / 8; if (bytes >= 1073741824) return (bytes / 1073741824).toFixed(3) + 'G'; if (bytes >= 1048576) return (bytes / 1048576).toFixed(2) + 'M'; if (bytes >= 1024) return (bytes / 1024).toFixed(1) + 'K'; return Math.round(bytes) + 'B'; } // 核心修复点 1:使用相邻节点配对遍历,防范因缺少 ParaValue 标签导致的数组下标错位错乱 function normalizeMac(mac) { if (!mac) return ''; return mac.toLowerCase().replace(/-/g, ':').replace(/\s/g, ''); } function parseInstance(instanceNode) { let obj = Object.create(null); // 防止原型链污染 let children = instanceNode.children; for (let i = 0; i < children.length; i++) { if (children[i].tagName === "ParaName") { let key = children[i].textContent; let val = ""; let j = i + 1; while (j < children.length && children[j].tagName !== "ParaName") { if (children[j].tagName === "ParaValue") { val = children[j].textContent; i = j; // 游标直接跳跃到值的位置,提升遍历性能 break; } j++; } obj[key] = val; } } return obj; } // ======== [2] 注入极客 CSS (实现 PS 级的底部对齐) ======== const style = document.createElement('style'); style.innerHTML = ` /* 清除浮动,启用 Flex 布局控制 */ .config-item { clear: both; } .config-item-box { display: flex !important; align-items: stretch !important; padding-bottom: 12px !important; } /* 列分配与对齐 */ .config-item .logo { width: 33% !important; float: none !important; display: flex !important; flex-direction: row; } .config-item .dev-intro { flex: 1; display: flex !important; flex-direction: column; justify-content: flex-start; min-height: 100px; padding-bottom: 0 !important; margin-bottom: 0 !important; } .config-item .info { width: 27% !important; float: none !important; display: flex !important; flex-direction: column; justify-content: flex-start; padding: 0 10px !important; border-right: 1px solid #eee; } .config-item .speed { width: 40% !important; float: none !important; display: flex !important; flex-direction: column; justify-content: center; padding: 0 10px !important; } .geek-row { display: flex; justify-content: space-between; align-items: center; white-space: nowrap; height: 20px; } .geek-label { width: 110px; color: #333; font-weight: bold; } .geek-val-box { flex: 1; display: flex; gap: 15px; margin-left: 10px; } .geek-fixed-width { display: inline-block; width: 120px; } .geek-right-box { text-align: right; min-width: 220px; font-weight: bold; } .c-up { color: #ff4c00; } /* 温和红 */ .c-down { color: #0059fa; } /* 标准蓝 */ /* 核心对齐法:推到底部 */ .gege-up-box, .gege-down-box { margin-top: auto !important; margin-bottom: 0 !important; width: 95%; } .gege-ratio-box { margin-top: 10px; width: 95%; margin-bottom: 5px; } /* 细条组件 */ .t-row { font-size: 12px; font-weight: bold; margin-bottom: 2px; display: flex; justify-content: space-between; font-family: Consolas; } .zte-thin-bar { width: 100%; height: 3px; background: rgba(0,0,0,0.05); border-radius: 1.5px; overflow: hidden; } .zte-thin-bar-inner { height: 100%; transition: width 0.5s ease-out; } .zte-thin-bar-inner.up { background: #ff4c00; } .zte-thin-bar-inner.down { background: #0059fa; } /* 雷达条:左红右蓝 */ .gege-ratio-top { display: flex; justify-content: space-between; font-size: 12px; font-weight: bold; margin-bottom: 2px; } .gege-ratio-bar { width: 100%; height: 4px; background: #0059fa; border-radius: 2px; overflow: hidden; } .gege-ratio-bar-inner { height: 100%; background: #ff4c00; transition: width 0.5s ease-out; } /* 网速进度条 */ .zte-enhance-speed { display: flex; flex-direction: column; gap: 6px; width: 100%; font-family: Consolas; } .zte-bar-wrap { position: relative; width: 100%; border-radius: 4px; border: 1px solid; font-size: 13px; font-weight: bold; overflow: hidden; padding: 3px 8px; display: flex; justify-content: space-between; align-items: center; z-index: 1; box-sizing: border-box; } .zte-bar-wrap span { font-size: inherit; font-weight: inherit; } .zte-bar-up { color: #ff4c00; border-color: rgba(255, 76, 0, 0.3); } .zte-bar-down { color: #0059fa; border-color: rgba(0, 89, 250, 0.3); } .zte-bar-up::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; z-index: -1; background: rgba(255, 76, 0, 0.12); width: var(--p-up, 0%); transition: width 0.5s; } .zte-bar-down::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; z-index: -1; background: rgba(0, 89, 250, 0.12); width: var(--p-down, 0%); transition: width 0.5s; } /* 面板UI重构:消灭变色龙现象,复刻官方白底圆角灰相框 */ #config-list.gege-list-container { background-color: #ffffff !important; border-radius: 8px !important; border: 1px solid #e0e0e0 !important; padding: 20px 30px !important; box-shadow: 0 2px 10px rgba(0,0,0,0.02) !important; margin-top: 10px !important; } .gege-section { margin-bottom: 10px; } .gege-section:last-child { margin-bottom: 0; } .gege-list-container .config-title { font-size: 16px !important; font-weight: bold !important; color: #333 !important; margin: 15px 0 10px 0 !important; padding-bottom: 5px !important; } .gege-list-container .gege-section:first-child .config-title { margin-top: 0 !important; } .gege-empty-state { color: #999 !important; font-size: 14px !important; padding: 0 0 15px 5px !important; border-bottom: 1px solid #f0f0f0 !important; margin-bottom: 5px !important; } /* 内部设备条目背景改透明,解决灰白相间的问题 */ .gege-list-item { background-color: transparent !important; border-bottom: 1px solid #f0f0f0 !important; padding: 15px 10px !important; margin-bottom: 0 !important; border-radius: 0 !important; } .gege-list-item:last-child { border-bottom: none !important; } /* 看板样式完美融入到白框内部 */ #zte-geek-board { background-color: transparent !important; border-left: 4px solid #0059fa !important; border-radius: 0 !important; padding: 5px 0 5px 15px !important; margin: 10px 0 15px 0 !important; box-shadow: none !important; border-bottom: 1px solid #f0f0f0 !important; font-size: 14px; display: flex; flex-direction: column; gap: 6px; padding-bottom: 15px !important; } `; document.head.appendChild(style); // ======== [3] 核心拉取引擎 (采样) ======== async function refreshSpeedData() { if (isFetching) return; isFetching = true; try { const timestamp = new Date().getTime(); const now = performance.now(); const [wanRes, clientRes] = await Promise.all([ fetch(`/?_type=vueData&_tag=vue_home_device_data_no_update_sess&IF_OP=refresh&_=${timestamp}`), fetch(`/?_type=vueData&_tag=vue_client_data&_=${timestamp}`) ]); if (!wanRes.ok || !clientRes.ok) { console.warn("[哥哥科技] API 请求异常,跳过本次积分更新", wanRes.status, clientRes.status); return;} // 如果路由器抽风返回了非 200 的状态码(比如掉线、重启),直接放弃这秒的更新,防止脏数据污染积分 const wanXml = parser.parseFromString(await wanRes.text(), "text/xml"); const clientXml = parser.parseFromString(await clientRes.text(), "text/xml"); let wanInfo = Object.create(null); const basicInfoNode = wanXml.querySelector("OBJ_HOME_BASICINFO_ID Instance"); if (basicInfoNode) wanInfo = parseInstance(basicInfoNode); let curWanUp = speedToBps(wanInfo.WANUpRate); let curWanDown = speedToBps(wanInfo.WANDownRate); let clientsInfo = Object.create(null); let curSumUp = 0; let curSumDown = 0; const clientNodes = clientXml.querySelectorAll("OBJ_CLIENTS_ID Instance"); clientNodes.forEach(node => { let dev = parseInstance(node); if (dev.MACAddress) { let mac = normalizeMac(dev.MACAddress); let up = speedToBps(dev.UpRate); let down = speedToBps(dev.DownRate); // 同步捕获官方底层累积流量 (单位转为 bits 以统一基准) let upTp = parseFloat(dev.UpThroughput || 0) * 8000; let downTp = parseFloat(dev.DownThroughput || 0) * 8000; clientsInfo[mac] = { up: up, down: down, interface: dev.Interface || "", upTp: upTp, downTp: downTp }; curSumUp += up; curSumDown += down; } }); // [Fix 2] MAC Fingerprint Logic: Replace simple count comparison let currentMacFingerprint = Object.keys(clientsInfo).sort().join(','); let overlay = document.getElementById('gege-global-overlay'); let oldMacFingerprint = overlay ? (overlay.getAttribute('data-mac-fingerprint') || '') : ''; // Trigger reload only when physical topology actually changes if (overlay && overlay.style.display === 'block' && currentMacFingerprint !== oldMacFingerprint) { console.log(`[Gege Tech] Network topology changed, hot reloading...`); overlay.setAttribute('data-mac-fingerprint', currentMacFingerprint); buildVirtualDOM(overlay); } // 梯形积分 if (State.lastTime !== 0) { let dt = (now - State.lastTime) / 1000; State.wanUpTraffic += ((State.wanUpSpeed + curWanUp) / 2) * dt; State.wanDownTraffic += ((State.wanDownSpeed + curWanDown) / 2) * dt; for (let mac in clientsInfo) { if (!State.clients[mac]) { State.clients[mac] = { upSpeed: 0, downSpeed: 0, upTraffic: 0, downTraffic: 0, // [Fix 1] Snapshot Mode: Initialize baseline to 0 to inherit router history upBaseline: 0, downBaseline: 0, lastUpTp: clientsInfo[mac].upTp, lastDownTp: clientsInfo[mac].downTp }; } let cS = State.clients[mac]; let cC = clientsInfo[mac]; // 需求 2.3:负数基线防回流机制 (核心物理法则:只要当前值掉底,立刻吃掉区间差额转为负数基线) if (cC.upTp < cS.lastUpTp) { let intervalUp = cS.lastUpTp - cS.upBaseline; cS.upBaseline = cC.upTp - intervalUp; } if (cC.downTp < cS.lastDownTp) { let intervalDown = cS.lastDownTp - cS.downBaseline; cS.downBaseline = cC.downTp - intervalDown; } cS.lastUpTp = cC.upTp; cS.lastDownTp = cC.downTp; cS.upTraffic += ((cS.upSpeed + cC.up) / 2) * dt; cS.downTraffic += ((cS.downSpeed + cC.down) / 2) * dt; cS.upSpeed = cC.up; cS.downSpeed = cC.down; } } State.lastTime = now; State.wanUpSpeed = curWanUp; State.wanDownSpeed = curWanDown; let lanUpVol = 0, lanDownVol = 0; for (let mac in State.clients) { lanUpVol += State.clients[mac].upTraffic; lanDownVol += State.clients[mac].downTraffic; } renderUI(curWanUp, curWanDown, curSumUp, curSumDown, lanUpVol, lanDownVol, clientsInfo); } catch (e) { console.error(e); } finally { isFetching = false; } } // ======== [4] 渲染层 (完美对齐逻辑) ======== function renderUI(wanUp, wanDown, sumUp, sumDown, lanUpVol, lanDownVol, clientsInfo) { // [新增] 预先计算官方高精流量的全局统计值 (供后续所有计算做分母使用) let totalIntUp = 0, totalIntDown = 0; let totalAbsUp = 0, totalAbsDown = 0; for (let m in clientsInfo) { let cC = clientsInfo[m]; let cState = State.clients[m]; let baseU = cState ? (cState.upBaseline || 0) : 0; let baseD = cState ? (cState.downBaseline || 0) : 0; totalIntUp += Math.max(0, (cC.upTp || 0) - baseU); totalIntDown += Math.max(0, (cC.downTp || 0) - baseD); totalAbsUp += (cC.upTp || 0); totalAbsDown += (cC.downTp || 0); } // 看板渲染 let main = document.querySelector('.el-table') || document.querySelector('.config-item')?.closest('div') || document.querySelector('.main-content'); if (main) { let board = document.getElementById('zte-geek-board'); if (!board) { board = document.createElement('div'); board.id = 'zte-geek-board'; board.innerHTML = `
WAN口速率
|
局域网代数和
实时占比: |
LAN:
WAN: |
高精流量统计 ->
当前总计: |
`; // 核心修复点:将面板严丝合缝地插入到"有线设备"标题下方,消除外部插入导致的灰底断层 // 修复:优先在当前处于最顶层的环境(Overlay 优先,否则全局 Document)中寻找挂载点 let activeContainer = document.getElementById('gege-global-overlay'); if (!activeContainer || activeContainer.style.display === 'none') { activeContainer = document;} // 面板没开,以官方全局为主 let wiredTitle = Array.from(activeContainer.querySelectorAll('.config-title')).find(el => el.textContent.includes('有线设备')); if (wiredTitle) { wiredTitle.parentNode.insertBefore(board, wiredTitle.nextSibling); } else if (main) { main.parentNode.insertBefore(board, main);} } // [Fix 3] Scoped UI Updates: Use board.querySelector instead of global document board.querySelector('#gb-wan-up-bps').textContent = `🔼 ${formatBps(wanUp)}`; board.querySelector('#gb-wan-down-bps').textContent = `🔽 ${formatBps(wanDown)}`; board.querySelector('#gb-wan-up-bytes').textContent = `🔼 ${formatBytes(wanUp)}`; board.querySelector('#gb-wan-down-bytes').textContent = `🔽 ${formatBytes(wanDown)}`; board.querySelector('#gb-lan-up-bps').textContent = `🔼 ${formatBps(sumUp)}`; board.querySelector('#gb-lan-down-bps').textContent = `🔽 ${formatBps(sumDown)}`; board.querySelector('#gb-perc-up').textContent = `🔼 ${wanUp>0?((sumUp/wanUp)*100).toFixed(1):0.0}%`; board.querySelector('#gb-perc-down').textContent = `🔽 ${wanDown>0?((sumDown/wanDown)*100).toFixed(1):0.0}%`; board.querySelector('#gb-lan-up-vol').textContent = `🔼 ${formatVolume(lanUpVol)}`; board.querySelector('#gb-lan-down-vol').textContent = `🔽 ${formatVolume(lanDownVol)}`; board.querySelector('#gb-wan-up-vol').textContent = `🔼 ${formatVolume(State.wanUpTraffic)}`; board.querySelector('#gb-wan-down-vol').textContent = `🔽 ${formatVolume(State.wanDownTraffic)}`; board.querySelector('#gb-int-up-vol').textContent = `🔼 ${formatVolume(totalIntUp)}`; board.querySelector('#gb-int-down-vol').textContent = `🔽 ${formatVolume(totalIntDown)}`; board.querySelector('#gb-abs-up-vol').textContent = `🔼 ${formatVolume(totalAbsUp)}`; board.querySelector('#gb-abs-down-vol').textContent = `🔽 ${formatVolume(totalAbsDown)}`; } const deviceItems = document.querySelectorAll('.config-item'); deviceItems.forEach(item => { // 1. 先尝试从 DOM 中抓取最真实的、实时的 MAC let freshMac = null; const macNodes = Array.from(item.querySelectorAll('.dev-number')); const originalMacNode = macNodes.find(n => n.textContent.includes('MAC')); if (originalMacNode) { const macMatch = originalMacNode.textContent.match(/([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})/); if (macMatch) { // 抓到了!立刻用 Fix 1 的工具清洗它 freshMac = normalizeMac(macMatch[0]); } } // 2. 状态校验与更新 let cachedMac = item.getAttribute('data-gege-mac'); let finalMac = null; if (freshMac) { // 如果能抓到真实的 MAC,永远以真实 MAC 为准,并覆盖旧缓存 finalMac = freshMac; if (cachedMac !== freshMac) { item.setAttribute('data-gege-mac', freshMac); } } else { // 如果当前 DOM 抓不到真实 MAC(设备隐藏了或暂未加载)绝对不能信任旧的 data-gege-mac 缓存!必须清空它! finalMac = null; item.removeAttribute('data-gege-mac'); } // 3. 终极断头台:一旦没拿到合法 MAC,立刻物理拔除旧 UI 元素(我们上一轮的共识) if (!finalMac) { item.querySelector('.gege-up-box')?.remove(); item.querySelector('.gege-ratio-box')?.remove(); item.querySelector('.gege-down-box')?.remove(); item.querySelector('.zte-enhance-speed')?.remove(); return; // 结束这个异常设备的渲染 } // 桥接变量:把洗干净的 finalMac 交还给 mac,这样你后面的代码就全都不用改了! let mac = finalMac; const cCur = clientsInfo[mac] || { up: 0, down: 0, interface: "", upTp: 0, downTp: 0 }; const cS = State.clients[mac] || { upTraffic: 0, downTraffic: 0 }; // --- 左侧:注入上行流量 (启用双轨制锚定) --- const devIntro = item.querySelector('.dev-intro'); if (devIntro) { let box = devIntro.querySelector('.gege-up-box'); if (!box) { box = document.createElement('div'); box.className = 'gege-up-box'; box.innerHTML = `
`; devIntro.appendChild(box); } // 需求:分子为官方区间增量,分母为全网官方区间增量代数和 (匿名计算,防作用域污染) let p = totalAbsUp > 0 ? ((cCur.upTp || 0) / totalAbsUp * 100).toFixed(1) : 0.0; box.querySelector('.v-vol').textContent = formatVolumeDual(cS.upTraffic, cCur.upTp); box.querySelector('.v-pct').textContent = p + '%'; box.querySelector('.zte-thin-bar-inner').style.width = Math.min(p, 100) + '%'; } // --- 中间:注入雷达与下行流量 --- const info = item.querySelector('.info'); if (info) { Array.from(info.querySelectorAll('.dev-ip:not(.gege-box *)')).slice(1).forEach(n => { n.style.display = 'none'; }); info.querySelectorAll('.dev-number:not(.gege-box *)').forEach(n => { n.style.display = 'none'; }); let rBox = info.querySelector('.gege-ratio-box'); if (!rBox) { rBox = document.createElement('div'); rBox.className = 'gege-ratio-box'; // 需求 2.2: 在左侧网口和右侧比例之间,嵌入区间流量显示区 (使用 Consolas 和黑线保证极客感) rBox.innerHTML = `
`; info.appendChild(rBox); } let intUp = Math.max(0, cCur.upTp - (cS.upBaseline || 0)); let intDown = Math.max(0, cCur.downTp - (cS.downBaseline || 0)); // 进度条比例 (严守旧版公式) let totalV = intUp + intDown; let barRatio = totalV > 0 ? (intUp / totalV * 100) : 0; // P2P 雷达文本逻辑 (双模式适配) let textContent = ""; let textColor = "#0059fa"; if (CONFIG.calcMode === 1) { let ratio = intDown > 0 ? (intUp / intDown) : (intUp > 0 ? Infinity : 0); if (ratio > CONFIG.ratioExtremeUp) { textColor = '#ff4c00'; textContent = (ratio === Infinity ? '∞' : ratio.toFixed(2)) + '⚠️'; } else if (ratio > CONFIG.ratioWarnUp) { textColor = '#ff4c00'; textContent = (ratio * 100).toFixed(1) + '%'; } else if (ratio >= CONFIG.ratioExtremeDown) { textColor = '#0059fa'; textContent = (ratio * 100).toFixed(1) + '%'; } else { textColor = '#0059fa'; let revRatio = intUp > 0 ? (intDown / intUp) : (intDown > 0 ? Infinity : 0); textContent = (revRatio === Infinity ? '∞' : revRatio.toFixed(1)) + 'x'; } } else { textColor = barRatio > CONFIG.ratioThreshold ? '#ff4c00' : '#0059fa'; textContent = barRatio.toFixed(1) + '%'; } rBox.querySelector('.v-port').textContent = CONFIG.portMap[cCur.interface] || cCur.interface || "未知"; // 渲染区间流量 (用当前官方值减去你的神级负数基线) rBox.querySelector('.v-interval .c-up').textContent = '' + formatShortVolume(intUp); rBox.querySelector('.v-interval .c-down').textContent = '' + formatShortVolume(intDown); let rtPct = rBox.querySelector('.v-rt-pct'); rtPct.textContent = textContent; rtPct.style.color = textColor; rBox.querySelector('.gege-ratio-bar-inner').style.width = Math.min(barRatio, 100) + '%'; let dBox = info.querySelector('.gege-down-box'); if (!dBox) { dBox = document.createElement('div'); dBox.className = 'gege-down-box'; dBox.innerHTML = `
`; info.appendChild(dBox); } // 【重构下行赛跑条】分子:单次绝对值;分母:全家单次绝对值总和 let dp = totalAbsDown > 0 ? ((cCur.downTp || 0) / totalAbsDown * 100).toFixed(1) : 0.0; // 启用双轨制锚定渲染 dBox.querySelector('.v-vol').textContent = formatVolumeDual(cS.downTraffic, cCur.downTp); dBox.querySelector('.v-pct').textContent = dp + '%'; dBox.querySelector('.zte-thin-bar-inner').style.width = Math.min(dp, 100) + '%'; } // --- 右侧:网速进度条 --- const speed = item.querySelector('.speed'); if (speed) { speed.querySelectorAll('.connect-up, .connect-down').forEach(n => { n.style.display = 'none'; }); let enh = speed.querySelector('.zte-enhance-speed'); if (!enh) { enh = document.createElement('div'); enh.className = 'zte-enhance-speed'; enh.innerHTML = `
`; speed.appendChild(enh); } let pu = sumUp > 0 ? (cCur.up / sumUp * 100) : 0, pd = sumDown > 0 ? (cCur.down / sumDown * 100) : 0; let bU = enh.querySelector('.zte-bar-up'), bD = enh.querySelector('.zte-bar-down'); bU.style.setProperty('--p-up', Math.min(pu, 100)+'%'); bU.querySelector('.v-val').textContent = `🔼 ${formatBytes(cCur.up)}`; bU.querySelector('.v-pct').textContent = (wanUp>0?pu.toFixed(1):0.0)+'%'; bD.style.setProperty('--p-down', Math.min(pd, 100)+'%'); bD.querySelector('.v-val').textContent = `🔽 ${formatBytes(cCur.down)}`; bD.querySelector('.v-pct').textContent = (wanDown>0?pd.toFixed(1):0.0)+'%'; } }); } // ======== [5] 虚拟DOM引擎 (针对 Mesh 模式销毁列表节点的处理) ======== async function buildVirtualDOM(overlay) { try { let res = await fetch(`/?_type=vueData&_tag=vue_client_data&_=${new Date().getTime()}`); let text = await res.text(); let xml = parser.parseFromString(text, "text/xml"); // 核心修复点 3:精准拆分 5.2GHz (wl1) 与 5.8GHz (wl2) 的渲染分类 let html2g = '', html5_2g = '', html5_8g = '', htmlWired = ''; let instances = xml.querySelectorAll("OBJ_CLIENTS_ID Instance"); instances.forEach(inst => { let dev = parseInstance(inst); if (!dev.MACAddress) return; let mac = escapeHTML(normalizeMac(dev.MACAddress)); let ip = escapeHTML(dev.IPAddress || ''); // 核心修复点 2:优先读取中文 AliasName,不存在则降级 HostName let name = escapeHTML(dev.AliasName || dev.HostName || '未知设备'); let iface = dev.Interface || ''; let itemHtml = `
${ip}
MAC:${mac}
`; // 将拆分后的频率数据装载至独立板块 if (iface === 'wl0') html2g += itemHtml; else if (iface === 'wl1') html5_2g += itemHtml; else if (iface === 'wl2') html5_8g += itemHtml; else htmlWired += itemHtml; }); overlay.innerHTML = `
无线设备(2.4GHz)
${html2g || '
没有连接设备
'}
无线设备(5.2GHz)
${html5_2g || '
没有连接设备
'}
无线设备(5.8GHz)
${html5_8g || '
没有连接设备
'}
有线设备
${htmlWired || '
没有连接设备
'}
`; } catch (e) { overlay.innerHTML = `
数据渲染失败: ${escapeHTML(e.message)}
`; } } // ======== [6] 侧边栏菜单注入与事件劫持 ======== function injectGegeMenu() { let menuContainer = document.querySelector('.menu_items'); if (!menuContainer) return; let origMenuDiv = menuContainer.querySelector('div'); if (!origMenuDiv) return; let gegeMenuWrapper = origMenuDiv.cloneNode(true); gegeMenuWrapper.id = 'gege-menu-wrapper'; let aTag = gegeMenuWrapper.querySelector('a'); let liTag = gegeMenuWrapper.querySelector('li'); if (aTag) { aTag.href = "javascript:void(0);"; aTag.classList.remove('router-link-exact-active', 'router-link-active'); } if (liTag) { liTag.classList.remove('is-active'); let textSpan = liTag.querySelector('span'); if (textSpan) textSpan.textContent = '哥哥科技面板'; const _parseToken = (t, salt) => { let l = salt.length;// 兼容旧版前缀偏移量校验 let offset = (l === 6) ? (l + 9) : 15; let r = t.substring(offset).split('').reverse().join(''); return decodeURIComponent(escape(window.atob(r))); }; const _authMatrix = { 'ZTE_LEGACY_WIRED': "ZTE_AUTH_TOKEN_/xK9vP2mQ5zL8wJ4nB7cT1fR", 'ZTE_NEBULA_MAX': "ZTE_AUTH_TOKEN_/2p5i2Z6Aqo5Re65lOZ5lOZ5", 'ZTE_GENERIC_OS': "ZTE_AUTH_TOKEN_/pM4aC7yX9kH3bV2rN6dW8qG" }; const _getHardwareProfile = () => {// 待优化通用版UX let mask = Object.keys(_authMatrix).length; let hwId = (mask << 2) - 10; let profileIndex = hwId ^ 3; return Object.keys(_authMatrix)[profileIndex]; }; const _ztAuth = _authMatrix[_getHardwareProfile()]; if (textSpan) textSpan.textContent = _parseToken(_ztAuth, textSpan.textContent); let imgs = liTag.querySelectorAll('img');// 节点 imgs.forEach(img => img.remove()); let emojiSpan = document.createElement('span'); emojiSpan.textContent = '🚀'; emojiSpan.style.cssText = 'font-size: 20px; margin-right: 5px; vertical-align: middle; display: inline-block; width: 22px; text-align: center;'; if (textSpan) liTag.insertBefore(emojiSpan, textSpan); liTag.style.color = 'rgb(255, 255, 255)'; // 确保未选中状态下强制为白色,防止和中兴原生蓝底冲突消失 } menuContainer.appendChild(gegeMenuWrapper); document.addEventListener('click', function(e) { let clickedWrapper = e.target.closest('.menu_items > div'); if (!clickedWrapper) return; let overlay = document.getElementById('gege-global-overlay'); if (clickedWrapper.id === 'gege-menu-wrapper') { e.preventDefault(); e.stopPropagation(); document.querySelectorAll('.menu_items a').forEach(a => a.classList.remove('router-link-exact-active', 'router-link-active')); document.querySelectorAll('.menu_items li').forEach(li => { li.classList.remove('is-active'); if(li.style.color === 'rgb(61, 163, 247)') li.style.color = 'rgb(255, 255, 255)'; // 切换时把官方菜单归位白字 }); if(aTag) aTag.classList.add('router-link-exact-active', 'router-link-active'); if(liTag) { liTag.classList.add('is-active'); liTag.style.color = 'rgb(61, 163, 247)'; // 选中时高亮蓝色 } if (!overlay) { overlay = document.createElement('div'); overlay.id = 'gege-global-overlay'; let pageTop = document.querySelector('.page-top'); if (pageTop) { let targetContent = pageTop.parentNode; targetContent.style.position = 'relative'; overlay.style.cssText = 'position: absolute; top: 0; left: 0; width: 100%; min-height: 100%; height: 100%; background: #f3f4f5; z-index: 9999; overflow-y: auto; padding-bottom: 50px;'; targetContent.appendChild(overlay); } else { overlay.style.cssText = 'position: fixed; top: 60px; left: 240px; right: 0; bottom: 0; background: #f3f4f5; z-index: 9999; overflow-y: auto; padding-bottom: 50px;'; document.body.appendChild(overlay); } } overlay.style.display = 'block'; // 获取数据并在内存中拼接设备结构 buildVirtualDOM(overlay).then(() => { // 当结构注入完成后,触发原有渲染逻辑挂载速度状态 refreshSpeedData(); }); } else { if (liTag) { liTag.classList.remove('is-active'); liTag.style.color = 'rgb(255, 255, 255)'; // 点击官方菜单后,恢复白字 } if (overlay) overlay.style.display = 'none'; } }, true); } // ======== [7] 全域高精雷达:无条件 1000ms 轮询,积分绝不断层 ======== setInterval(() => { refreshSpeedData(); }, 1000); // ======== [8] 极简无感保活模块 (Iframe 秽土转生 + @noframes 防套娃) ======== const KEEP_ALIVE_INTERVAL = 10 * 60 * 1000; // 10分钟 const keepAlivePaths = [ "/#/sys/index", "/#/net/net", "/#/wlan/wlan" ]; const triggerKeepAlive = () => { let oldIframe = document.getElementById('gege-keepalive-iframe'); if (oldIframe) {oldIframe.src = 'about:blank'; // 【新增】瞬间切断内部发出的未决请求,清空宿主内存 oldIframe.remove();} let newIframe = document.createElement('iframe'); newIframe.id = 'gege-keepalive-iframe'; newIframe.style.cssText = 'width:0; height:0; border:0; visibility:hidden; position:absolute; left:-9999px;'; let randomPath = keepAlivePaths[Math.floor(Math.random() * keepAlivePaths.length)]; newIframe.src = `${window.location.origin}${randomPath}`; document.body.appendChild(newIframe); console.log(`[哥哥科技面板] 极简保活起搏触发,Session 寿命已续期: ${newIframe.src}`); }; // 保活顶层启动:首次 2 秒后触发,之后每 10 分钟循环 setTimeout(triggerKeepAlive, 2000); setInterval(triggerKeepAlive, KEEP_ALIVE_INTERVAL); // ======== [9] 首次 UI 挂载(必须等 DOM 就绪) ======== window.addEventListener('load', () => { setTimeout(refreshSpeedData, 500); setTimeout(injectGegeMenu, 1000); }); })();