diff --git a/design/prototype/screens/ui-desktop.html b/design/prototype/screens/ui-desktop.html index fdf968c..c749186 100644 --- a/design/prototype/screens/ui-desktop.html +++ b/design/prototype/screens/ui-desktop.html @@ -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 @@
主题
语言 -
+ +
视角
更新条
+
挡板 +

- 单窗口可交互桌面客户端(980×640)。左侧栏切视图、连接键状态机、设置开关真能开关、语言中/EN 单显切换、视角 免费版/PRO 切换。品牌区与底部会员卡均可进账户。顶栏演示控制段与设置页开关双向联动。登录与设备超限另附独立静态帧。 + 单窗口可交互桌面客户端(980×640)。左侧栏切视图、连接键状态机、设置开关真能开关、语言中/EN 单显切换、视角 免费版/PRO 切换。品牌区与底部会员卡均可进账户。顶栏演示控制段与设置页开关双向联动;「挡板」段可唤出登录后设备超限挡板(盖满整窗、可关闭)。登录另附独立静态帧。

@@ -930,6 +949,35 @@
+ + +
+
+ + +
设备数已达上限
+
你的套餐最多 5 台设备,请移除一台以继续。
+ +
+
+ +
iPhone 15
最后在线 2 小时前
+
+
+
+ +
MacBook Pro
本机
+
+
+
+ +
iPad Air
最后在线 昨天
+
+
+
+ +
+
@@ -962,34 +1010,6 @@ - -
-
设备超限登录后设备数超上限挡板(独立静态屏,不并入交互窗口)
-
- -
设备数已达上限
-
你的套餐最多 5 台设备,请移除一台以继续。
- -
-
- -
iPhone 15
最后在线 2 小时前
-
-
-
- -
MacBook Pro
本机
-
-
-
- -
iPad Air
最后在线 昨天
-
-
-
- -
-
@@ -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:'极速畅连 · 稳定加速线路
隐私保护 · 全程无日志',en:'Fast & stable lines
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(){ diff --git a/design/prototype/screens/ui-mobile.html b/design/prototype/screens/ui-mobile.html index a242a93..000e2a5 100644 --- a/design/prototype/screens/ui-mobile.html +++ b/design/prototype/screens/ui-mobile.html @@ -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 @@
语言 -
- - +
+ +
@@ -392,13 +404,17 @@
+
+ 挡板 +
+

单机可交互原型:底部四 Tab、连接键三态状态机(未连接 → 连接中 → 已连接)、 - 节点选择联动、套餐视角切换。全部复用 ../atoms.css 原子,vanilla JS 无依赖。用顶栏右侧演示控制段切主题 / 语言 / 视角 / 更新提示。 + 节点选择联动、套餐视角切换。全部复用 ../atoms.css 原子,vanilla JS 无依赖。用顶栏右侧演示控制段切主题 / 语言 / 视角 / 更新提示,「挡板」可唤出登录后设备超限挡板(盖满 App 窗口、可关闭)。

@@ -1013,6 +1029,45 @@
+ + +
+
+
+ +
设备数已达上限
+
你的账户最多可同时登录 5 台设备,请移除一台后继续。
+
+ +
+
+ +
+
iPhone 15本机
+
在线
+
+
+
+
+ +
+
MacBook Pro
+
最后在线 2 小时前
+
+
+
+
+ +
+
iPad Air
+
最后在线 昨天
+
+
+
+
+ +
+
@@ -1088,63 +1143,6 @@ - -
-
静态设备超限挡板 · #16
-
-
- 9:41 -
- - - -
-
-
-
- -
设备数已达上限
-
你的账户最多可同时登录 5 台设备,请移除一台后继续。
-
- -
-
- -
-
iPhone 15本机
-
在线
-
-
-
-
- -
-
MacBook Pro
-
最后在线 2 小时前
-
-
-
-
- -
-
iPad Air
-
最后在线 昨天
-
-
-
-
- -
-
Windows PC
-
最后在线 3 天前
-
-
-
-
- -
-
-
@@ -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(); } });