🌴 Sync 2026-10-30 02:13:56

This commit is contained in:
github-actions[bot] committed 2026-10-30 02:13:56 +08:00
1 parent 8d20d896c0
commit 57d68d2cec
34 files changed
+1383 -304

No files matched your search

@@ -254,25 +254,74 @@
background: var(--isp-accent, var(--isp-accent-other));
}
/* 卡头(方案 B,0.3.1):44px 图标左置 + 名称右排。
0.3.0 是「名称左、20px 图标右」,运营商多起来时右角的小图标很难认,
且各家 SVG 的视觉重心不一致;改成左置大图后,卡片读起来像一张名片:
先认标志,再看名字,最后看数字。 */
.isp-card-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-bottom: 6px;
justify-content: flex-start;
gap: 10px;
margin-bottom: 10px;
}
.isp-op-name {
font-size: 0.98rem;
font-size: 1rem;
font-weight: 600;
color: var(--isp-text);
letter-spacing: 0.02em;
/* 四列布局下内容宽 ~218px:44px 图标 + 10px 间距后,
长运营商名(ISP_TABLE 自定义标签)不要顶破卡片 */
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 图标位:logo 与「无 logo 的 glyph 占位」共用 44×44 的盒子,
两者视觉同构——卡片并排时卡头高度、名称基线完全对齐。 */
.isp-op-ico {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 44px;
width: 44px;
height: 44px;
color: var(--isp-accent, var(--isp-accent-other));
opacity: 0.9;
opacity: 0.95;
}
/* 品牌 PNG(192×192 透明底):按盒子缩放,不拉伸、不裁切 */
.isp-brand-logo {
display: block;
width: 100%;
height: 100%;
object-fit: contain;
}
/* 深色主题的 logo 提亮(实测,不是"看着差不多"):
电信官方蓝 #004080 的 luminance 太低,直接贴到深色卡面 (#222) 上对比度只有
1.55:1——44px 下像蒙了一层灰。只提亮不改色相(brightness/saturate):
电信 1.55:1 → 3.31:1 联通 2.47:1 → 3.84:1 移动 2.46:1 → 5.31:1
(前一列按 PNG 原色对 #222 算,后一列从深色截图里采 logo 像素实测。)
浅色主题不动:白底上三家分别是 10.27 / 6.44 / 6.48:1,提亮反而失真。
LuCI 的 bootstrap 主题用 :root[data-darkmode="true"] 切整套变量,这里跟随
同一个属性——用户点顶栏开关或跟系统深色,都会命中。 */
[data-darkmode="true"] .isp-brand-logo {
filter: brightness(1.55) saturate(1.05);
}
/* 兜底卡:与运营商卡同构(顶部色条 + 44px 卡头 + 大数字)。
它是「未归属任何运营商」的那一格,所以身份色用中性灰而不是某家的品牌色。 */
.isp-card-fb {
--isp-accent: var(--isp-accent-other);
}
/* 网格里只有一条「没有启用的运营商」提示时,让它横跨整行,
否则会被 repeat(4) 挤成 1/4 宽的一条细缝 */
.isp-cards > .isp-empty {
grid-column: 1 / -1;
}
.isp-big {
@@ -819,17 +868,30 @@
.lc-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
align-items: center;
justify-content: flex-start;
gap: 10px;
}
/* 与状态页同一个卡头语言(0.3.1):44px logo/glyph 左置。
映射不到运营商的 wan(isp 为空、自定义源)用线稿 glyph 占位,盒子同尺寸。 */
.lc-head-ico {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 44px;
width: 44px;
height: 44px;
}
.lc-title {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 8px;
min-width: 0;
/* 加了 44px 图标后,标题占中间的剩余宽度,评分仍然贴右 */
flex: 1 1 auto;
}
.lc-name {
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="715" viewBox="-29.771 0 5703 1554" width="2500"><g fill="#02489d"><path d="m1014.229 261s-28-31-65-31c0 0-81 0-136 175 0 0-47 153-47 380 0 0 0 214 44 365 0 0 44 153 108 153 0 0 45 0 83-84 0 0 38-84 54-221l120-30s-31 255-127 420c0 0-99 166-224 166 0 0-150 0-256-225 0 0-106-225-106-544 0 0 0-319 106-546 0 0 106-226 256-226 0 0 150 0 257 230z"/><g><path d="M1436.229 568s-207 171 -537 254c0 0 -331 82 -594 28c0 0 -263 -56 -302 -217c0 0 -33 -134 112 -282c0 0 143 -147 400 -245l17 66s-150 64 -233 145c0 0 -85 85 -66 158c0 0 25 93 201 121c0 0 175 27 402 -29c0 0 225 -56 369 -163c0 0 142 -108 118 -202c0 0 -20 -81 -164 -116l-22 -86s187 7 311 69c0 0 127 62 153 167c0 0 41 160 -165 332"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="2500" height="2500" viewBox="0 0 192.756 192.756"><g fill-rule="evenodd" clip-rule="evenodd"><g><path d="M59.293 54.077c0 -20.53 16.605 -37.136 37.135 -37.136c20.429 0 37.136 16.605 37.136 37.136c0 20.429 -16.707 37.035 -37.136 37.035c-20.53 -0.001 -37.135 -16.606 -37.135 -37.035zM75.596 47.434l3.724 3.02l15.397 -12.982c1.409 -1.107 3.22 -1.711 5.132 -1.711s3.725 0.705 5.234 1.812l19.422 15.8v3.623l-17.209 -13.888c-1.309 -1.007 -2.816 -1.51 -4.529 -1.51c-1.709 0 -3.32 0.604 -4.629 1.61l-0.201 0.101l-0.1 0.101c-1.611 1.409 -9.561 8.051 -13.587 11.372h0.101c0.805 1.308 2.918 1.51 4.83 0.402h0.101c4.328 -3.724 8.252 -7.044 8.655 -7.447c1.209 -1.006 2.717 -1.711 4.328 -1.711c1.609 0 3.02 0.604 4.125 1.51L118.769 57.6l3.623 3.019l2.113 1.711l-0.402 0.101l0.402 0.402s-16.404 13.989 -20.529 17.511c-2.014 2.114 -4.932 3.422 -8.152 3.422c-2.919 0 -5.736 -1.107 -7.75 -3.019L67.646 64.14v-5.133l20.329 16.505c1.409 1.006 3.019 1.61 4.931 1.61s3.724 -0.705 5.133 -1.812c0.805 -0.705 13.082 -11.171 18.617 -15.8l-3.623 -3.019l-15.498 13.084c-1.509 1.006 -3.22 1.61 -5.132 1.61c-2.013 0 -3.824 -0.604 -5.334 -1.812l-19.423 -15.8V49.95l17.31 13.989c1.208 0.906 2.717 1.51 4.428 1.51c1.812 0 3.422 -0.604 4.63 -1.61l0.201 -0.201l0.101 -0.101c1.711 -1.409 9.56 -7.951 13.585 -11.373h-0.1v-0.101l-1.006 -0.705c-1.008 -0.503 -2.416 -0.302 -3.725 0.403c-0.1 0 -0.1 0.101 -0.1 0c-4.328 3.724 -8.354 7.044 -8.756 7.447c-1.107 1.107 -2.717 1.711 -4.327 1.711c-1.51 0 -2.919 -0.503 -4.126 -1.409L73.483 49.246l-0.101 0.101l-3.522 -2.918l-1.007 -1.107l-1.207 -1.006h0.201l-0.201 -0.201s16.404 -13.989 20.53 -17.511c2.113 -2.113 4.931 -3.422 8.151 -3.422c3.02 0 5.737 1.208 7.749 3.02l20.43 16.706v5.132l-20.328 -16.605c-1.41 -1.006 -3.02 -1.61 -4.83 -1.61a8.446 8.446 0 0 0 -5.234 1.811c-0.805 0.702 -12.983 11.169 -18.518 15.798z" fill="#2767b0"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="2500" height="1367" viewBox="0.122 228.756 611.755 334.503"><path fill="#D91A21" d="M380.786 389.207l20.073-19.89-15.851-15.729 15.851-15.974-20.073-19.706-15.79 15.667-10.465-10.465c-16.646-16.708-44.003-16.708-60.71 0-8.446 8.384-12.607 19.461-12.607 30.478 0 10.894 4.101 21.909 12.607 30.355 16.707 16.769 44.063 16.769 60.71 0a1481.013 1481.013 0 0 0 10.465-10.466l15.79 15.73zm-46.022-25.337c-5.875 5.875-15.239 5.875-21.053 0-2.815-2.815-4.223-6.671-4.223-10.343a15.23 15.23 0 0 1 4.223-10.526c5.813-5.813 15.178-5.813 21.053 0 4.59 4.651 10.404 10.526 10.404 10.526s-5.814 5.631-10.404 10.343zm264.567-40.82c-16.708-16.708-44.003-16.708-60.771 0-4.713 4.835-10.465 10.465-10.465 10.465l-15.852-15.728-19.89 19.706 15.789 15.973-15.789 15.729 19.89 19.891 15.79-15.79s5.753 5.691 10.465 10.466c16.77 16.769 44.064 16.769 60.771 0 8.385-8.446 12.607-19.462 12.607-30.355s-4.16-21.973-12.545-30.357zm-19.951 40.82c-5.753 5.875-15.116 5.875-20.931 0-4.712-4.712-10.465-10.343-10.465-10.343s5.753-5.875 10.465-10.526c5.814-5.813 15.178-5.813 20.931 0 2.876 2.877 4.284 6.671 4.284 10.526 0 3.672-1.346 7.527-4.284 10.343zm-112.73-10.343s46.695-46.757 51.286-51.408c16.829-16.769 16.829-43.941 0-60.832-16.708-16.708-43.881-16.708-60.772 0-6.548 6.731-10.403 10.526-10.403 10.526s-3.795-3.795-10.404-10.526c-16.83-16.708-44.003-16.708-60.833 0-16.769 16.891-16.769 44.063 0 60.832l51.408 51.408s-46.757 46.573-51.408 51.286c-16.769 16.769-16.769 44.003 0 60.833 16.83 16.707 44.003 16.707 60.833 0 6.609-6.732 10.404-10.649 10.404-10.649s3.855 3.917 10.403 10.649c16.892 16.707 44.064 16.707 60.772 0 16.829-16.83 16.829-44.064 0-60.833-4.591-4.713-51.286-51.286-51.286-51.286zm-71.237-71.298c-5.692-5.875-5.692-15.116 0-20.931 5.813-5.691 15.238-5.691 20.93 0l10.526 10.526-15.851 15.729 19.829 19.951 15.851-15.851 15.851 15.851 19.829-19.951-15.79-15.729s4.896-4.957 10.527-10.526c5.752-5.691 15.116-5.691 20.869 0 5.813 5.814 5.813 15.056 0 20.931-6.06 6.059-51.47 51.592-51.47 51.592s-45.165-45.533-51.101-51.592zm102.694 163.466c-5.753 5.691-15.116 5.691-20.869 0-5.631-5.569-10.526-10.466-10.526-10.466l15.789-15.851-19.828-19.891-15.912 15.79-15.852-15.851-19.828 19.89 15.851 15.852s-4.957 4.896-10.526 10.465c-5.691 5.691-15.116 5.691-20.931 0-5.691-5.753-5.691-15.239 0-20.931 5.937-6.059 51.225-51.592 51.225-51.592s45.41 45.533 51.469 51.592c5.751 5.692 5.751 15.24-.062 20.993z"/></svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

@@ -33,9 +33,51 @@ var ICONS = {
refresh: '<path d="M20 11a8 8 0 1 0-2.3 6.3"/><path d="M20 4v7h-7"/>',
pulse: '<path d="M2 12h4l2.5-6 3.5 12 2.5-6H22"/>',
chevron: '<path d="M6 9l6 6 6-6"/>',
shield: '<path d="M12 3l7 3v6c0 4-3 7-7 9-4-2-7-5-7-9V6z"/>'
shield: '<path d="M12 3l7 3v6c0 4-3 7-7 9-4-2-7-5-7-9V6z"/>',
/* 运营商兜底 glyph:wan 卡映射不到品牌 logo 时用它们占位
(与状态页同一套 24 格线稿;视图之间不共享模块,故此处自带一份) */
tel: '<path d="M12 9v12"/><circle cx="12" cy="5" r="2"/><path d="M8.6 8.4a5 5 0 0 1 6.8 0"/><path d="M6 5.8a8.4 8.4 0 0 1 12 0"/>',
uni: '<circle cx="6" cy="6" r="2"/><circle cx="18" cy="6" r="2"/><circle cx="12" cy="18" r="2"/><path d="M8 6h8"/><path d="M7 8l4 8"/><path d="M17 8l-4 8"/>',
cmcc: '<rect x="7" y="3" width="10" height="18" rx="2"/><path d="M11 18h2"/>',
cernet: '<path d="M3 9.5 12 5l9 4.5-9 4.5z"/><path d="M7 11.5V16c0 1.2 2.2 2.2 5 2.2s5-1 5-2.2v-4.5"/>',
other: '<circle cx="12" cy="12" r="7"/><path d="M12 5v14"/>'
};
/* wan 卡的 isp 源码 → glyph(与状态页 GLYPH_FOR 同源) */
var GLYPH_FOR = {
chinatelecom: 'tel', chinanet: 'tel', telecom: 'tel',
unicom_cnc: 'uni', unicom: 'uni',
cmcc: 'cmcc', mobile: 'cmcc',
cernet: 'cernet'
};
/* 品牌 logo:与状态页用的是同一组 PNG(同一路径、同一文件名)。
wan 卡按 linecheck JSON 里的 isp 字段归属映射;映射不到就走 GLYPH_FOR/占位。 */
var BRAND_LOGO = {
chinatelecom: 'logo-chinatelecom.png',
chinanet: 'logo-chinatelecom.png',
telecom: 'logo-chinatelecom.png',
unicom_cnc: 'logo-unicom.png',
unicom: 'logo-unicom.png',
cmcc: 'logo-mobile.png',
mobile: 'logo-mobile.png'
};
var HEAD_ICO = 44;
function brandLogo(src, size) {
var file = BRAND_LOGO[src];
if (!file)
return null;
var img = document.createElement('img');
img.src = L.resource('isp-split/logos/' + file);
img.alt = src;
img.width = size || HEAD_ICO;
img.height = size || HEAD_ICO;
img.className = 'isp-brand-logo';
return img;
}
function svgIcon(name, size) {
var span = document.createElement('span');
span.className = 'isp-ico';
@@ -68,8 +110,12 @@ function fmtAge(sec) {
}
function fmtMs(v) {
if (v === null || v === undefined || v === '' || v === 0)
/* F-24b:null = 未测/失败,显示“—”;"<1" 与历史 0 都显示 "<1ms"。
0 不再作为“没测到”的哨兵(亚毫秒 DNS 曾经被四舍五入成 0)。 */
if (v === null || v === undefined || v === '')
return '—';
if (v === 0 || v === '<1')
return '<1ms';
return v + 'ms';
}
@@ -427,6 +473,14 @@ return view.extend({
var lat = w.latency || {};
var geo = w.geo || {};
var http = w.http || {};
/* F-23:exit_probe 是 shell 侧逐 URL 记录的探测明细。有明细但
exit_ip 为空 => 回显源不可达/返回格式不符;连明细都没有 =>
没有发起探测(curl 缺失/未配置),文案保持“可能未真正出网”。
所有 JSON 字段只作为 E() 的文本子项,绝不拼进 HTML 解析路径。 */
var ep = Array.isArray(w.exit_probe) ? w.exit_probe : [];
var exitText = w.exit_ip || (ep.length
? _('回显源不可达(建议更换 LINE_CHECK_ECHO_URLS)')
: _('未取到(可能未真正出网)'));
var detail = E('div', { 'class': 'lc-detail', 'hidden': '' });
var toggle = E('button', {
@@ -457,6 +511,12 @@ return view.extend({
sc.grade === 'C' ? 'var(--isp-warn)' : 'var(--isp-bad)')
}, [
E('div', { 'class': 'lc-head' }, [
/* D2:与状态页同一套卡头——44px 左置。
wan 卡按 isp 归属取品牌 logo;自定义 ISP / 未识别(isp 为空)
走 GLYPH_FOR 的线稿 glyph,同一 44px 盒子,卡头高度不变。 */
E('span', { 'class': 'lc-head-ico' }, [
brandLogo(w.isp) || svgIcon(GLYPH_FOR[w.isp] || 'pulse', HEAD_ICO)
]),
E('div', { 'class': 'lc-title' }, [
E('span', { 'class': 'lc-name' }, [ w.iface || '?' ]),
E('span', { 'class': 'lc-dev' }, [ w.device || '' ]),
@@ -477,7 +537,7 @@ return view.extend({
E('span', { 'class': 'lc-tag is-muted' }, [ classText(w.ip_class) ])
]),
row(_('出口 IP'), [
E('span', {}, [ w.exit_ip || _('未取到(可能未真正出网)') ]),
E('span', {}, [ exitText ]),
multi
]),
row(_('运营商'), [ w.isp_label || _('未知') ]),
@@ -35,7 +35,13 @@ var ICONS = {
uni: '<circle cx="6" cy="6" r="2"/><circle cx="18" cy="6" r="2"/><circle cx="12" cy="18" r="2"/><path d="M8 6h8"/><path d="M7 8l4 8"/><path d="M17 8l-4 8"/>',
cmcc: '<rect x="7" y="3" width="10" height="18" rx="2"/><path d="M11 18h2"/>',
cernet: '<path d="M3 9.5 12 5l9 4.5-9 4.5z"/><path d="M7 11.5V16c0 1.2 2.2 2.2 5 2.2s5-1 5-2.2v-4.5"/>',
other: '<circle cx="12" cy="12" r="7"/><path d="M12 5v14"/>'
other: '<circle cx="12" cy="12" r="7"/><path d="M12 5v14"/>',
/* 兜底流量卡的身份 glyph:地球 + 经纬线("未归属任何运营商"的那部分流量)。
与上面一族同为 24 格 stroke 线稿(stroke-width 1.8、round 收口),
44px 下线条不会糊;颜色走 currentColor,深浅主题都不用另做一套。 */
net: '<circle cx="12" cy="12" r="8.5"/><path d="M3.5 12h17"/>' +
'<path d="M12 3.5c2.4 2.4 3.8 5.4 3.8 8.5s-1.4 6.1-3.8 8.5"/>' +
'<path d="M12 3.5C9.6 5.9 8.2 8.9 8.2 12s1.4 6.1 3.8 8.5"/>'
};
var GLYPH_FOR = {
@@ -45,17 +51,22 @@ var GLYPH_FOR = {
cernet: 'cernet'
};
/* 品牌 logo(运营商官方标志,fill 型多色 SVG,静态文件按请求加载) */
/* 品牌 logo(运营商官方风格标志,192×192 透明底 PNG,静态文件按请求加载)。
0.3.1 起换图不换名:文件名仍是 logo-<运营商>,只把扩展名从 .svg 改成 .png。
PNG 是位图,深浅两个主题共用一份即可(透明底天然跟随背景)。 */
var BRAND_LOGO = {
chinatelecom: 'logo-chinatelecom.svg',
chinanet: 'logo-chinatelecom.svg',
telecom: 'logo-chinatelecom.svg',
unicom_cnc: 'logo-unicom.svg',
unicom: 'logo-unicom.svg',
cmcc: 'logo-mobile.svg',
mobile: 'logo-mobile.svg'
chinatelecom: 'logo-chinatelecom.png',
chinanet: 'logo-chinatelecom.png',
telecom: 'logo-chinatelecom.png',
unicom_cnc: 'logo-unicom.png',
unicom: 'logo-unicom.png',
cmcc: 'logo-mobile.png',
mobile: 'logo-mobile.png'
};
/* 卡头图标统一 44px:logo 与「无 logo 的 glyph 占位」同尺寸,视觉同构 */
var HEAD_ICO = 44;
function brandLogo(src, size) {
var file = BRAND_LOGO[src];
if (!file)
@@ -63,8 +74,8 @@ function brandLogo(src, size) {
var img = document.createElement('img');
img.src = L.resource('isp-split/logos/' + file);
img.alt = src;
img.width = size || 20;
img.height = size || 20;
img.width = size || HEAD_ICO;
img.height = size || HEAD_ICO;
img.className = 'isp-brand-logo';
return img;
}
@@ -360,6 +371,14 @@ return view.extend({
return false;
if (/^\s*#/.test(String(r.set || '')) || /^\s*#/.test(String(r.src || '')))
return false;
/* D1-3:教育网(cernet)退出状态页,纯 UI 退场。
后端数据行照旧保留(status --json / 诊断 / 导出都不受影响),
只是这一版不把它算进运营商网格:cernet 用户量小,指标常年为空,
混在电信/联通/移动里只会稀释「一眼看三家」的信息密度。
set 名与 src 一起判:ISP_TABLE 里 cernet 的集合名可以改,
src 改不了。 */
if (r.src === 'cernet' || r.set === 'isp_cernet')
return false;
return true;
});
@@ -369,7 +388,6 @@ return view.extend({
? _('读取状态失败:') + this.statusError
: _('没有启用的运营商。检查 /etc/isp-split.conf 里的 ISP_TABLE。')
]));
return;
}
var maxPackets = 0;
@@ -401,8 +419,13 @@ return view.extend({
'style': '--isp-accent: ' + accent
}, [
E('div', { 'class': 'isp-card-head' }, [
E('span', { 'class': 'isp-op-name' }, [ r.label || r.src ]),
E('span', { 'class': 'isp-op-ico' }, [ brandLogo(r.src, 20) || svgIcon(GLYPH_FOR[r.src] || 'other', 20) ])
/* 方案 B:logo 44px 左置 + 运营商名。
没有品牌 logo 的来源(自定义源 / other)走同一尺寸的 glyph 占位,
两张卡并排时卡头高度与基线一致,不会一张高一卡矮。 */
E('span', { 'class': 'isp-op-ico' }, [
brandLogo(r.src) || svgIcon(GLYPH_FOR[r.src] || 'other', HEAD_ICO)
]),
E('span', { 'class': 'isp-op-name' }, [ r.label || r.src ])
]),
E('div', {}, [
E('span', { 'class': 'isp-big' }, [ r.exists ? fmtNum(packets) : '—' ]),
@@ -430,6 +453,22 @@ return view.extend({
}, [ '—' ])
]));
});
/* D1-4:兜底流量并入同一张运营商网格,成为「第 4 张卡」。
桌面四列(.isp-cards 是 repeat(4)):三家运营商 + 兜底正好一行四格;
ISP_TABLE 多开运营商时按四列继续换行,兜底始终排在最后,不会被挤到
另一行只剩一半宽。卡头与运营商卡同构:44px glyph + 名称。
内容由 paintFallback() 写进 #isp-fallback(paintOps 之后调用)。 */
box.appendChild(E('article', {
'class': 'isp-card isp-card-op isp-card-fb',
'style': '--isp-accent: var(--isp-accent-other)'
}, [
E('div', { 'class': 'isp-card-head' }, [
E('span', { 'class': 'isp-op-ico' }, [ svgIcon('net', HEAD_ICO) ]),
E('span', { 'class': 'isp-op-name' }, [ _('兜底流量') ])
]),
E('div', { 'id': 'isp-fallback' })
]));
},
/* ---------------- 双线健康 ---------------- */
@@ -485,7 +524,11 @@ return view.extend({
box.appendChild(list);
},
/* ---------------- 兜底流量 ---------------- */
/* ---------------- 兜底流量(第 4 张运营商卡的主体) ----------------
口径与 0.3.0 完全一致:未命中任何运营商集合的连接数(首包)与流量。
卡头在 paintOps() 里画(44px glyph + 名称),这里只负责主体,
字号/间距与运营商卡的 isp-big / isp-op-sub 同一套类名,因此
两张卡的数字基线和单位位置自然对齐。 */
paintFallback: function() {
var box = document.getElementById('isp-fallback');
@@ -714,18 +757,15 @@ return view.extend({
E('div', { 'id': 'isp-load-error', 'class': 'isp-err', 'style': 'display:none' }),
E('div', { 'id': 'isp-cards', 'class': 'isp-cards' }),
E('div', { 'class': 'isp-grid-2' }, [
/* 兜底流量不再是独立大卡:它已经并入顶部运营商网格的第 4 格
(见 paintOps)。这里腾出来的位置让双线健康 / 数据源健康度 /
数据时间排成一行三格(isp-grid-3,断点 1100→2 列、820→1 列),
页面从「2+2+2」变成「4 张运营商 + 3 张明细」,视线更顺。 */
E('div', { 'class': 'isp-grid-3' }, [
E('section', { 'class': 'isp-card' }, [
E('h3', {}, [ _('双线健康') ]),
E('div', { 'id': 'isp-lines' })
]),
E('section', { 'class': 'isp-card' }, [
E('h3', {}, [ _('兜底流量') ]),
E('div', { 'id': 'isp-fallback' })
])
]),
E('div', { 'class': 'isp-grid-2' }, [
E('section', { 'class': 'isp-card' }, [
E('h3', {}, [ _('数据源健康度') ]),
E('div', { 'id': 'isp-sources' })