design(prototype): 设备超限整合为全窗口挡板 overlay + 演示语言控件改 6 语言下拉

- 设备超限从脱离主窗口的独立静态帧 → 盖满可交互窗口的根级 overlay(还原真实 client 登录后挡板),桌面+移动,演示控制触发
- 顶栏演示段 LANGUAGE 从中/EN 两档 seg → 6 语言 langsel 下拉(与 Settings 一致;非中英回退英文内容)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
This commit is contained in:
wangjia
2026-07-12 16:37:21 +08:00
parent 6ff048967e
commit 7700c43216
2 changed files with 168 additions and 101 deletions
+83 -38
View File
@@ -42,7 +42,7 @@
/* ============ 桌面窗口框 ============ */
.win{ width:980px; max-width:100%; height:640px; border-radius:16px; overflow:hidden;
display:flex; background:var(--bg); border:1px solid var(--border-strong);
box-shadow:var(--shadow-xl); }
box-shadow:var(--shadow-xl); position:relative; }
/* 左侧栏 */
.side{ width:210px; flex:none; background:var(--bg-subtle); border-right:1px solid var(--border);
@@ -304,10 +304,19 @@
.login-form{ flex:1; display:flex; flex-direction:column; justify-content:center; padding:var(--space-8); gap:var(--space-4); }
.login-form h3{ font-family:var(--font-display); font-weight:700; font-size:20px; color:var(--fg1); margin:0 0 var(--space-2); }
/* 设备超限挡板 */
.limit-win{ width:460px; max-width:100%; margin:0 auto; background:var(--bg); border:1px solid var(--border-strong);
/* 设备超限挡板 overlay(登录后根级挡板,盖满整窗;对齐 device_limit_screen.dart */
.dl-overlay{ position:absolute; inset:0; z-index:60; background:var(--overlay);
display:none; align-items:center; justify-content:center; padding:var(--space-6); }
.dl-overlay.is-open{ display:flex; }
.limit-win{ position:relative; width:460px; max-width:100%; max-height:100%; overflow:auto;
background:var(--bg); border:1px solid var(--border-strong);
border-radius:16px; box-shadow:var(--shadow-xl); padding:var(--space-8) var(--space-6) var(--space-6);
display:flex; flex-direction:column; text-align:center; }
.limit-close{ position:absolute; top:var(--space-4); right:var(--space-4); width:32px; height:32px;
display:flex; align-items:center; justify-content:center; border:none; background:transparent;
color:var(--fg3); cursor:pointer; border-radius:var(--radius-md); }
.limit-close:hover{ background:var(--bg-subtle); color:var(--fg1); }
.limit-close svg{ width:18px; height:18px; }
.limit-ic{ width:44px; height:44px; margin:0 auto var(--space-3); color:var(--accent); }
.limit-title{ font-family:var(--font-display); font-weight:700; font-size:20px; color:var(--fg1); }
.limit-desc{ font-size:14px; color:var(--fg2); line-height:1.5; margin-top:8px; }
@@ -363,17 +372,27 @@
<div class="demo-grp"><span class="lbl" data-i18n="demo.theme">主题</span>
<span class="seg" id="themeSeg"><button data-theme="light" data-i18n="demo.light"></button><button data-theme="dark" data-i18n="demo.dark"></button></span></div>
<div class="demo-grp"><span class="lbl" data-i18n="demo.lang">语言</span>
<span class="seg" id="langSeg"><button data-lang="zh"></button><button data-lang="en">EN</button></span></div>
<span class="langsel" id="demoLang"><button class="langsel-trigger" id="demoLangTrig" aria-expanded="false"><span id="demoLangLabel">简体中文</span><svg><use href="#i-chevron-down"/></svg></button>
<div class="menu" id="demoLangMenu" hidden>
<button class="menu-item" data-langlabel="简体中文" data-lang="zh">简体中文</button>
<button class="menu-item" data-langlabel="English" data-lang="en">English</button>
<button class="menu-item" data-langlabel="日本語" data-lang="en">日本語</button>
<button class="menu-item" data-langlabel="한국어" data-lang="en">한국어</button>
<button class="menu-item" data-langlabel="Русский" data-lang="en">Русский</button>
<button class="menu-item" data-langlabel="Español" data-lang="en">Español</button>
</div></span></div>
<div class="demo-grp"><span class="lbl" data-i18n="demo.persp">视角</span>
<span class="seg" id="perspSeg"><button data-persp="free" data-i18n="plan.free">免费版</button><button data-persp="pro">PRO</button></span></div>
<div class="demo-grp"><span class="lbl" data-i18n="demo.upd">更新条</span>
<span class="seg" id="updSeg"><button data-upd="on" data-i18n="demo.show"></button><button data-upd="off" data-i18n="demo.hide"></button></span></div>
<div class="demo-grp"><span class="lbl" data-i18n="demo.gate">挡板</span>
<span class="seg" id="gateSeg"><button id="dlGateBtn" data-i18n="demo.deviceLimit">设备超限</button></span></div>
</div>
</div>
<div class="wrap">
<p class="lead">
单窗口可交互桌面客户端(<code>980×640</code>)。左侧栏切视图、连接键状态机、设置开关真能开关、语言中/EN 单显切换、视角 免费版/PRO 切换。品牌区与底部会员卡均可进账户。顶栏演示控制段与设置页开关双向联动登录设备超限另附独立静态帧。
单窗口可交互桌面客户端(<code>980×640</code>)。左侧栏切视图、连接键状态机、设置开关真能开关、语言中/EN 单显切换、视角 免费版/PRO 切换。品牌区与底部会员卡均可进账户。顶栏演示控制段与设置页开关双向联动;「挡板」段可唤出登录设备超限挡板(盖满整窗、可关闭)。登录另附独立静态帧。
</p>
<div class="stage">
@@ -930,6 +949,35 @@
</div>
</div>
<!-- 登录后设备超限挡板 overlay(根级、盖满整窗;演示控制「挡板 · 设备超限」触发) -->
<div class="dl-overlay" id="dlOverlay">
<div class="limit-win">
<button class="limit-close" id="dlClose" aria-label="关闭"><svg><use href="#i-x"/></svg></button>
<svg class="limit-ic"><use href="#i-monitor-smartphone"/></svg>
<div class="limit-title" data-i18n="dl.title">设备数已达上限</div>
<div class="limit-desc" data-i18n="dl.desc">你的套餐最多 5 台设备,请移除一台以继续。</div>
<button class="limit-remove js-dl-close"><svg><use href="#i-clock"/></svg><span data-i18n="dl.remove">移除最久未用</span></button>
<div class="limit-list">
<div class="device-row">
<svg class="dv-lead"><use href="#i-smartphone"/></svg>
<div class="dv-body"><div class="dv-name">iPhone 15</div><div class="dv-sub"><span data-i18n="dev.lastOnline">最后在线</span> <span data-i18n="dev.h2">2 小时前</span></div></div>
<div class="dv-trail"><button class="btn btn-danger js-dl-close" data-i18n="dev.remove">移除</button></div>
</div>
<div class="device-row">
<svg class="dv-lead"><use href="#i-laptop"/></svg>
<div class="dv-body"><div class="dv-name">MacBook Pro</div><div class="dv-sub"><span class="dv-cur" data-i18n="dev.current">本机</span></div></div>
<div class="dv-trail"></div>
</div>
<div class="device-row">
<svg class="dv-lead"><use href="#i-monitor"/></svg>
<div class="dv-body"><div class="dv-name">iPad Air</div><div class="dv-sub"><span data-i18n="dev.lastOnline">最后在线</span> <span data-i18n="dev.yesterday">昨天</span></div></div>
<div class="dv-trail"><button class="btn btn-danger js-dl-close" data-i18n="dev.remove">移除</button></div>
</div>
</div>
<button class="limit-logout js-dl-close" data-i18n="dl.logout">退出登录</button>
</div>
</div>
</div>
</div>
@@ -962,34 +1010,6 @@
</div>
</div>
<!-- 独立静态帧:设备超限挡板(对齐 device_limit_screen.dart -->
<div class="frame-wrap">
<div class="frame-cap"><span class="n">设备超限</span><span data-i18n="dl.frame">登录后设备数超上限挡板</span><span class="frame-note" data-i18n="login.note">(独立静态屏,不并入交互窗口)</span></div>
<div class="limit-win">
<svg class="limit-ic"><use href="#i-monitor-smartphone"/></svg>
<div class="limit-title" data-i18n="dl.title">设备数已达上限</div>
<div class="limit-desc" data-i18n="dl.desc">你的套餐最多 5 台设备,请移除一台以继续。</div>
<button class="limit-remove"><svg><use href="#i-clock"/></svg><span data-i18n="dl.remove">移除最久未用</span></button>
<div class="limit-list">
<div class="device-row">
<svg class="dv-lead"><use href="#i-smartphone"/></svg>
<div class="dv-body"><div class="dv-name">iPhone 15</div><div class="dv-sub"><span data-i18n="dev.lastOnline">最后在线</span> <span data-i18n="dev.h2">2 小时前</span></div></div>
<div class="dv-trail"><button class="btn btn-danger" data-i18n="dev.remove">移除</button></div>
</div>
<div class="device-row">
<svg class="dv-lead"><use href="#i-laptop"/></svg>
<div class="dv-body"><div class="dv-name">MacBook Pro</div><div class="dv-sub"><span class="dv-cur" data-i18n="dev.current">本机</span></div></div>
<div class="dv-trail"></div>
</div>
<div class="device-row">
<svg class="dv-lead"><use href="#i-monitor"/></svg>
<div class="dv-body"><div class="dv-name">iPad Air</div><div class="dv-sub"><span data-i18n="dev.lastOnline">最后在线</span> <span data-i18n="dev.yesterday">昨天</span></div></div>
<div class="dv-trail"><button class="btn btn-danger" data-i18n="dev.remove">移除</button></div>
</div>
</div>
<button class="limit-logout" data-i18n="dl.logout">退出登录</button>
</div>
</div>
</div>
@@ -1079,7 +1099,6 @@ var I18N = {
'contact.email':{zh:'邮箱客服',en:'Email support'}, 'contact.web':{zh:'官网',en:'Website'},
'contact.hoursTitle':{zh:'服务时间',en:'Support hours'}, 'contact.hours':{zh:'每日 9:00 24:00 (GMT+8)',en:'Daily 9:00 24:00 (GMT+8)'},
'ub.title':{zh:'发现新版本 v2.5.0,建议更新',en:'New version v2.5.0 available'}, 'ub.cta':{zh:'立即更新',en:'Update'},
'dl.frame':{zh:'登录后设备数超上限挡板',en:'Post-login device limit gate'},
'dl.title':{zh:'设备数已达上限',en:'Device limit reached'},
'dl.desc':{zh:'你的套餐最多 5 台设备,请移除一台以继续。',en:'Your plan allows up to 5 devices. Remove one to continue.'},
'dl.remove':{zh:'移除最久未用',en:'Remove least recently used'}, 'dl.logout':{zh:'退出登录',en:'Sign out'},
@@ -1090,6 +1109,7 @@ var I18N = {
'demo.theme':{zh:'主题',en:'Theme'}, 'demo.light':{zh:'明',en:'Light'}, 'demo.dark':{zh:'暗',en:'Dark'},
'demo.lang':{zh:'语言',en:'Language'}, 'demo.persp':{zh:'视角',en:'Perspective'},
'demo.upd':{zh:'更新条',en:'Banner'}, 'demo.show':{zh:'显',en:'On'}, 'demo.hide':{zh:'隐',en:'Off'},
'demo.gate':{zh:'挡板',en:'Gate'}, 'demo.deviceLimit':{zh:'设备超限',en:'Device limit'},
'login.frame':{zh:'分屏卡 · 品牌左 / 表单右',en:'Split card · brand left / form right'},
'login.note':{zh:'(独立静态屏,不并入交互窗口)',en:'(standalone static screen, outside the interactive window)'},
'login.slogan':{zh:'极速畅连 · 稳定加速线路<br>隐私保护 · 全程无日志',en:'Fast & stable lines<br>Privacy protected · No logs'},
@@ -1304,7 +1324,8 @@ function renderStatsChart(){
function syncSegs(){
document.querySelectorAll('#themeSeg button').forEach(function(b){ b.classList.toggle('is-active', b.dataset.theme===S.theme); });
var _tbi=document.getElementById('tbThemeIcon'); if(_tbi) _tbi.setAttribute('href', S.theme==='dark'?'#i-sun':'#i-moon');
document.querySelectorAll('#langSeg button').forEach(function(b){ b.classList.toggle('is-active', b.dataset.lang===S.lang); });
document.getElementById('demoLangLabel').textContent = S.langLabel;
document.querySelectorAll('#demoLangMenu .menu-item').forEach(function(m){ m.classList.toggle('is-selected', m.dataset.langlabel===S.langLabel); });
document.querySelectorAll('#perspSeg button').forEach(function(b){ b.classList.toggle('is-active', b.dataset.persp===S.persp); });
document.querySelectorAll('#updSeg button').forEach(function(b){ b.classList.toggle('is-active', (b.dataset.upd==='on')===S.upd); });
document.querySelectorAll('[data-toggle="dark"]').forEach(function(t){ t.classList.toggle('is-on', S.theme==='dark'); });
@@ -1361,12 +1382,25 @@ document.querySelectorAll('.server-row').forEach(function(row){
document.querySelectorAll('#themeSeg button').forEach(function(b){ b.addEventListener('click', function(){ applyTheme(b.dataset.theme); }); });
var _tbt=document.getElementById('tbTheme'); if(_tbt) _tbt.addEventListener('click', function(){ applyTheme(S.theme==='dark'?'light':'dark'); });
/* 顶栏语言段控(中/EN 驱动实际 i18n;同步 langsel */
document.querySelectorAll('#langSeg button').forEach(function(b){
b.addEventListener('click', function(){
S.lang=b.dataset.lang; S.langLabel = (S.lang==='zh') ? '简体中文' : 'English';
var demoLang = document.getElementById('demoLang');
var demoLangMenu = document.getElementById('demoLangMenu');
var demoLangTrig = document.getElementById('demoLangTrig');
demoLangTrig.addEventListener('click', function(e){
e.stopPropagation();
var open = demoLangMenu.hasAttribute('hidden');
if(open){ demoLangMenu.removeAttribute('hidden'); demoLangTrig.setAttribute('aria-expanded','true'); demoLang.classList.add('is-open'); }
else { demoLangMenu.setAttribute('hidden',''); demoLangTrig.setAttribute('aria-expanded','false'); demoLang.classList.remove('is-open'); }
});
document.querySelectorAll('#demoLangMenu .menu-item').forEach(function(m){
m.addEventListener('click', function(){
S.langLabel = m.dataset.langlabel; S.lang = m.dataset.lang; // 非中文词典的语言回退英文内容
demoLangMenu.setAttribute('hidden',''); demoLangTrig.setAttribute('aria-expanded','false'); demoLang.classList.remove('is-open');
applyLang(); syncSegs();
});
});
document.addEventListener('click', function(){
if(!demoLangMenu.hasAttribute('hidden')){ demoLangMenu.setAttribute('hidden',''); demoLangTrig.setAttribute('aria-expanded','false'); demoLang.classList.remove('is-open'); }
});
/* 顶栏视角段控 */
document.querySelectorAll('#perspSeg button').forEach(function(b){
b.addEventListener('click', function(){
@@ -1380,6 +1414,17 @@ document.querySelectorAll('#updSeg button').forEach(function(b){
});
document.getElementById('updClose').addEventListener('click', function(){ S.upd=false; applyUpd(); });
/* ========= 登录后设备超限挡板 overlay(演示:开/关,不真删设备) ========= */
(function(){
var ov=document.getElementById('dlOverlay'), btn=document.getElementById('dlGateBtn');
if(!ov) return;
function open(){ ov.classList.add('is-open'); if(btn) btn.classList.add('is-active'); }
function close(){ ov.classList.remove('is-open'); if(btn) btn.classList.remove('is-active'); }
if(btn) btn.addEventListener('click', function(){ ov.classList.contains('is-open') ? close() : open(); });
var x=document.getElementById('dlClose'); if(x) x.addEventListener('click', close);
ov.querySelectorAll('.js-dl-close').forEach(function(el){ el.addEventListener('click', close); });
})();
/* 设置/账户开关(深色 ↔ 主题双向联动;其余独立开关) */
document.querySelectorAll('.toggle').forEach(function(t){
t.addEventListener('click', function(){
+85 -63
View File
@@ -207,6 +207,11 @@
font-family:var(--font-sans); font-size:var(--text-sm); font-weight:600;
padding:var(--space-3); margin-top:var(--space-3); }
/* 设备超限挡板 overlay(登录后根级挡板,盖满 App 窗口;演示控制「挡板」触发) */
#deviceLimitOverlay{ position:absolute; left:0; right:0; top:30px; bottom:0; z-index:45;
background:var(--bg); display:none; flex-direction:column; }
#deviceLimitOverlay.is-open{ display:flex; }
/* 购买流程覆盖层(从账户页「兑换 & 购买」进入;移植 purchase.html 状态机,命名空间 .pur-* */
#purchaseOverlay{ position:absolute; left:0; right:0; top:30px; bottom:0; z-index:45;
background:var(--bg); display:none; flex-direction:column; }
@@ -373,9 +378,16 @@
</div>
<div class="demo-grp">
<span class="gl" data-i18n="demoLang">语言</span>
<div class="seg" id="segLang">
<button data-v="zh">中文</button>
<button data-v="en">EN</button>
<div class="langsel" id="demoLang">
<button class="langsel-trigger" id="demoLangTrigger" aria-expanded="false"><span id="demoLangLabel">简体中文</span><svg><use href="#i-chevron-down"/></svg></button>
<div class="menu" id="demoLangMenu" hidden>
<button class="menu-item is-selected" data-lang="zh" data-label="简体中文">简体中文</button>
<button class="menu-item" data-lang="en" data-label="English">English</button>
<button class="menu-item" data-lang="ja" data-label="日本語">日本語</button>
<button class="menu-item" data-lang="ko" data-label="한국어">한국어</button>
<button class="menu-item" data-lang="ru" data-label="Русский">Русский</button>
<button class="menu-item" data-lang="es" data-label="Español">Español</button>
</div>
</div>
</div>
<div class="demo-grp">
@@ -392,13 +404,17 @@
<button data-v="hide" data-i18n="segHide">隐藏</button>
</div>
</div>
<div class="demo-grp">
<span class="gl" data-i18n="demoGate">挡板</span>
<div class="seg"><button id="dlGateBtn" data-i18n="demoDeviceLimit">设备超限</button></div>
</div>
</div>
</div>
<div class="wrap">
<p class="lead">
单机可交互原型:底部四 Tab、连接键三态状态机(未连接 → 连接中 → 已连接)、
节点选择联动、套餐视角切换。全部复用 <code>../atoms.css</code> 原子,vanilla JS 无依赖。用顶栏右侧演示控制段切主题 / 语言 / 视角 / 更新提示。
节点选择联动、套餐视角切换。全部复用 <code>../atoms.css</code> 原子,vanilla JS 无依赖。用顶栏右侧演示控制段切主题 / 语言 / 视角 / 更新提示,「挡板」可唤出登录后设备超限挡板(盖满 App 窗口、可关闭)
</p>
<div class="stage">
@@ -1013,6 +1029,45 @@
</div>
</div>
<!-- 登录后设备超限挡板 overlay(根级、盖满 App 窗口;演示控制「挡板 · 设备超限」触发) -->
<div id="deviceLimitOverlay">
<div class="dl-body">
<div class="dl-hero">
<span class="dl-ic"><svg><use href="#i-monitor-smartphone"/></svg></span>
<div class="dl-title" data-i18n="dlTitle">设备数已达上限</div>
<div class="dl-desc" data-i18n="dlDesc">你的账户最多可同时登录 5 台设备,请移除一台后继续。</div>
</div>
<button class="dl-outline js-dl-close"><svg><use href="#i-clock"/></svg><span data-i18n="dlRemoveLru">移除最久未用</span></button>
<div class="dl-list">
<div class="device-row">
<span class="icon-box"><svg><use href="#i-smartphone"/></svg></span>
<div class="dv-body">
<div class="dv-name">iPhone 15<span class="dv-badge" data-i18n="thisDevice">本机</span></div>
<div class="dv-sub"><span class="status-dot is-online"></span><span data-i18n="dlOnline">在线</span></div>
</div>
<div class="dv-trail"></div>
</div>
<div class="device-row">
<span class="icon-box"><svg><use href="#i-laptop"/></svg></span>
<div class="dv-body">
<div class="dv-name">MacBook Pro</div>
<div class="dv-sub"><span class="status-dot is-offline"></span><span><span data-i18n="lastOnline">最后在线</span> <span data-i18n="rel2h">2 小时前</span></span></div>
</div>
<div class="dv-trail"><button class="dl-rm js-dl-close" data-i18n="dlRemove">移除</button></div>
</div>
<div class="device-row">
<span class="icon-box"><svg><use href="#i-monitor-smartphone"/></svg></span>
<div class="dv-body">
<div class="dv-name">iPad Air</div>
<div class="dv-sub"><span class="status-dot is-offline"></span><span><span data-i18n="lastOnline">最后在线</span> <span data-i18n="relYday">昨天</span></span></div>
</div>
<div class="dv-trail"><button class="dl-rm js-dl-close" data-i18n="dlRemove">移除</button></div>
</div>
</div>
<button class="dl-logout js-dl-close" data-i18n="logout">退出登录</button>
</div>
</div>
</div>
</div>
@@ -1088,63 +1143,6 @@
</div>
</div>
<!-- 静态设备超限挡板屏(不入状态机) -->
<div class="frame-wrap">
<div class="frame-cap"><span class="n">静态</span>设备超限挡板 · #16</div>
<div class="phone">
<div class="status-bar">
<span class="sb-time">9:41</span>
<div class="sb-right">
<span class="sb-sig"><i></i><i></i><i></i><i></i></span>
<svg><use href="#i-wifi"/></svg>
<span class="sb-bat"><i></i></span>
</div>
</div>
<div class="dl-body">
<div class="dl-hero">
<span class="dl-ic"><svg><use href="#i-monitor-smartphone"/></svg></span>
<div class="dl-title">设备数已达上限</div>
<div class="dl-desc">你的账户最多可同时登录 5 台设备,请移除一台后继续。</div>
</div>
<button class="dl-outline"><svg><use href="#i-clock"/></svg>移除最久未用</button>
<div class="dl-list">
<div class="device-row">
<span class="icon-box"><svg><use href="#i-smartphone"/></svg></span>
<div class="dv-body">
<div class="dv-name">iPhone 15<span class="dv-badge">本机</span></div>
<div class="dv-sub"><span class="status-dot is-online"></span><span>在线</span></div>
</div>
<div class="dv-trail"></div>
</div>
<div class="device-row">
<span class="icon-box"><svg><use href="#i-laptop"/></svg></span>
<div class="dv-body">
<div class="dv-name">MacBook Pro</div>
<div class="dv-sub"><span class="status-dot is-offline"></span><span>最后在线 2 小时前</span></div>
</div>
<div class="dv-trail"><button class="dl-rm">移除</button></div>
</div>
<div class="device-row">
<span class="icon-box"><svg><use href="#i-monitor-smartphone"/></svg></span>
<div class="dv-body">
<div class="dv-name">iPad Air</div>
<div class="dv-sub"><span class="status-dot is-offline"></span><span>最后在线 昨天</span></div>
</div>
<div class="dv-trail"><button class="dl-rm">移除</button></div>
</div>
<div class="device-row">
<span class="icon-box"><svg><use href="#i-laptop"/></svg></span>
<div class="dv-body">
<div class="dv-name">Windows PC</div>
<div class="dv-sub"><span class="status-dot is-offline"></span><span>最后在线 3 天前</span></div>
</div>
<div class="dv-trail"><button class="dl-rm">移除</button></div>
</div>
</div>
<button class="dl-logout">退出登录</button>
</div>
</div>
</div>
</div>
</div>
@@ -1154,6 +1152,8 @@ const I18N = {
zh:{
brandName:'穿山甲', brandLatin:'Pangolin',
demoTheme:'主题', demoLang:'语言', demoView:'视角', demoUpdate:'更新',
demoGate:'挡板', demoDeviceLimit:'设备超限',
dlTitle:'设备数已达上限', dlDesc:'你的账户最多可同时登录 5 台设备,请移除一台后继续。', dlRemoveLru:'移除最久未用', dlRemove:'移除', dlOnline:'在线',
segLight:'明', segDark:'暗', segFree:'免费版', segPro:'PRO',
segShow:'显示', segHide:'隐藏',
navConnect:'连接', navNodes:'节点', navStats:'统计', navAccount:'账户',
@@ -1223,6 +1223,8 @@ const I18N = {
en:{
brandName:'Pangolin', brandLatin:'',
demoTheme:'Theme', demoLang:'Language', demoView:'View', demoUpdate:'Update',
demoGate:'Gate', demoDeviceLimit:'Device limit',
dlTitle:'Device limit reached', dlDesc:'Your account allows up to 5 devices at once. Remove one to continue.', dlRemoveLru:'Remove least recently used', dlRemove:'Remove', dlOnline:'Online',
segLight:'Light', segDark:'Dark', segFree:'Free', segPro:'PRO',
segShow:'Show', segHide:'Hide',
navConnect:'Connect', navNodes:'Nodes', navStats:'Stats', navAccount:'Account',
@@ -1521,7 +1523,7 @@ function render(){
$('#themeSub').textContent = dark ? D().stateOn : D().followLight;
// seg 同步
syncSeg('#segLang', state.lang);
syncDemoLang();
syncSeg('#segPersona', state.persona);
syncSeg('#segTheme', state.theme);
syncSeg('#segUpdate', state.update);
@@ -1607,10 +1609,23 @@ $('#themeToggle').addEventListener('click', ()=>applyTheme(state.theme==='dark'?
// 演示控制段(主题 / 语言 / 视角 / 额度 / 更新)
$('#segTheme').addEventListener('click', e=>{ const b=e.target.closest('button'); if(b) applyTheme(b.dataset.v); });
$('#segLang').addEventListener('click', e=>{ const b=e.target.closest('button'); if(b){ state.lang=b.dataset.v; syncAcctLang(); render(); } });
const demoLang=$('#demoLang'), demoMenu=$('#demoLangMenu'), demoTrigger=$('#demoLangTrigger');
demoTrigger.addEventListener('click', e=>{ e.stopPropagation(); const open=demoMenu.hidden; demoMenu.hidden=!open; demoTrigger.setAttribute('aria-expanded', String(open)); });
document.addEventListener('click', ()=>{ demoMenu.hidden=true; demoTrigger.setAttribute('aria-expanded','false'); });
demoMenu.addEventListener('click', e=>{ const it=e.target.closest('.menu-item'); if(!it) return; e.stopPropagation(); demoMenu.querySelectorAll('.menu-item').forEach(m=>m.classList.toggle('is-selected', m===it)); $('#demoLangLabel').textContent=it.dataset.label; demoMenu.hidden=true; demoTrigger.setAttribute('aria-expanded','false'); if(it.dataset.lang==='zh'||it.dataset.lang==='en'){ state.lang=it.dataset.lang; syncAcctLang(); render(); } });
$('#segPersona').addEventListener('click', e=>{ const b=e.target.closest('button'); if(b){ state.persona=b.dataset.v; render(); } });
$('#segUpdate').addEventListener('click', e=>{ const b=e.target.closest('button'); if(b){ state.update=b.dataset.v; render(); } });
// 登录后设备超限挡板 overlay(演示:开/关,不真删设备)
(()=>{
const ov=$('#deviceLimitOverlay'), btn=$('#dlGateBtn');
if(!ov) return;
const open=()=>{ ov.classList.add('is-open'); if(btn) btn.classList.add('is-active'); };
const close=()=>{ ov.classList.remove('is-open'); if(btn) btn.classList.remove('is-active'); };
if(btn) btn.addEventListener('click', ()=> ov.classList.contains('is-open') ? close() : open());
ov.querySelectorAll('.js-dl-close').forEach(el=> el.addEventListener('click', close));
})();
// 语言段变更时,账户语言下拉标签跟随(仅中/英)
function syncAcctLang(){
const map={zh:'简体中文', en:'English'};
@@ -1618,6 +1633,13 @@ function syncAcctLang(){
$('#acctLangLabel').textContent=label;
acctMenu.querySelectorAll('.menu-item').forEach(m=>m.classList.toggle('is-selected', m.dataset.lang===state.lang));
}
// 演示控制段的语言下拉标签跟随全局 lang(仅中/英有词典,其余选中即回退英文内容)
function syncDemoLang(){
const map={zh:'简体中文', en:'English'};
const label=map[state.lang]; if(!label) return;
const el=document.getElementById('demoLangLabel'); if(el) el.textContent=label;
document.querySelectorAll('#demoLangMenu .menu-item').forEach(m=>m.classList.toggle('is-selected', m.dataset.lang===state.lang));
}
// 登录 / 注册 段控切换(静态帧)
$('#authSeg').addEventListener('click', e=>{ const b=e.target.closest('.auth-seg-btn'); if(b){ state.authMode=b.dataset.auth; renderAuth(); } });