design(prototype): 设备管理改下钻页 + 桌面登录页注册表单切换

- 我的设备:账户页内联列表 → 一行入口(计数+chevron)→ 独立设备管理页/子屏(完整列表+⋮操作),对齐真实 DevicesScreen + IA spec
- 桌面登录静态帧:「没有账户?免费注册」→ 切换到注册表单(邮箱/密码/确认密码 + 已有账户?登录 返回)

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:47:10 +08:00
parent 53426bd4c1
commit 18858b33d4
2 changed files with 113 additions and 49 deletions
+53 -6
View File
@@ -614,8 +614,14 @@
</div>
</div>
<div class="sec-label"><span data-i18n="acc.devices">设备管理</span> · <span id="devHeaderCount">1 / 5</span></div>
<div class="card card-flush" id="deviceList" style="margin-bottom:var(--space-5)"></div>
<div class="sec-label" data-i18n="acc.devices">设备管理</div>
<div class="card card-flush" style="margin-bottom:var(--space-5)">
<button class="setting-row is-tap" id="accDevicesRow">
<span class="icon-box"><svg><use href="#i-monitor-smartphone"/></svg></span>
<div class="st-body"><div class="st-title" data-i18n="acc.myDevices">我的设备</div><div class="st-sub" data-i18n="acc.myDevicesSub">重命名 · 强制下线 · 移除</div></div>
<span class="st-trail"><span class="st-val" id="accDevCount" style="font-family:var(--font-mono)">1 / 5</span><svg><use href="#i-chevron-right"/></svg></span>
</button>
</div>
<div class="card card-flush">
<button class="setting-row is-tap" id="logoutRow">
@@ -626,6 +632,15 @@
</div>
</section>
<!-- 设备管理视图(自账户页下钻) -->
<section class="view" data-view="devices">
<div class="account-view">
<button class="pv-back" id="dvBack"><svg><use href="#i-arrow-left"/></svg><span data-i18n="dev.back">返回</span></button>
<div class="sec-label"><span data-i18n="acc.devices">设备管理</span> · <span id="devHeaderCount">1 / 5</span></div>
<div class="card card-flush" id="deviceList" style="margin-bottom:var(--space-5)"></div>
</div>
</section>
<!-- 设置视图 -->
<section class="view" data-view="settings">
<div class="settings-view">
@@ -994,7 +1009,7 @@
</div>
<div class="lb-word">PANGOLIN</div>
</div>
<div class="login-form">
<div class="login-form" id="signinForm">
<h3 data-i18n="login.title">登录账户</h3>
<div class="field">
<label class="flabel" data-i18n="login.email">邮箱</label>
@@ -1005,7 +1020,24 @@
<input class="input" type="password" placeholder="••••••••" value="••••••••">
</div>
<button class="btn btn-primary btn-block btn-lg" data-i18n="login.signin">登录</button>
<button class="btn btn-ghost btn-block" data-i18n="login.signup">没有账户?免费注册</button>
<button class="btn btn-ghost btn-block" id="toSignup" data-i18n="login.signup">没有账户?免费注册</button>
</div>
<div class="login-form" id="signupForm" hidden>
<h3 data-i18n="login.signupTitle">注册账户</h3>
<div class="field">
<label class="flabel" data-i18n="login.email">邮箱</label>
<input class="input" type="email" placeholder="me@pangolin.app">
</div>
<div class="field">
<label class="flabel" data-i18n="login.pwd">密码</label>
<input class="input" type="password" placeholder="••••••••">
</div>
<div class="field">
<label class="flabel" data-i18n="login.pwd2">确认密码</label>
<input class="input" type="password" placeholder="••••••••">
</div>
<button class="btn btn-primary btn-block btn-lg" data-i18n="login.signupBtn">免费注册</button>
<button class="btn btn-ghost btn-block" id="toSignin" data-i18n="login.haveAccount">已有账户?登录</button>
</div>
</div>
</div>
@@ -1023,6 +1055,7 @@ var I18N = {
'nav.orders':{zh:'我的订单',en:'Orders'},
'nav.account':{zh:'账户',en:'Account'},
'nav.notifications':{zh:'通知',en:'Notifications'}, 'nav.invite':{zh:'邀请好友',en:'Invite friends'},
'nav.devices':{zh:'设备管理',en:'Devices'},
'acc.billing':{zh:'购买 & 订单 & 兑换',en:'Buy · Orders · Redeem'},
'acc.buy':{zh:'购买套餐',en:'Buy a plan'}, 'acc.buySub':{zh:'升级 PRO 会员',en:'Upgrade to PRO'},
'acc.orders':{zh:'我的订单',en:'My orders'}, 'acc.ordersSub':{zh:'查看订单记录',en:'View order history'},
@@ -1084,6 +1117,7 @@ var I18N = {
'stats.allDevices':{zh:'全部设备',en:'All devices'},
'acc.info':{zh:'账户信息',en:'Account info'}, 'acc.email':{zh:'邮箱',en:'Email'}, 'acc.pwd':{zh:'密码',en:'Password'},
'acc.change':{zh:'修改',en:'Change'}, 'acc.devices':{zh:'设备管理',en:'Devices'}, 'acc.pref':{zh:'偏好',en:'Preferences'},
'acc.myDevices':{zh:'我的设备',en:'My Devices'}, 'acc.myDevicesSub':{zh:'重命名 · 强制下线 · 移除',en:'Rename · force offline · remove'},
'acc.lang':{zh:'语言',en:'Language'}, 'acc.dark':{zh:'深色外观',en:'Dark appearance'}, 'acc.protocol':{zh:'协议',en:'Protocol'},
'acc.upgrade':{zh:'续费 / 升级',en:'Renew / Upgrade'}, 'acc.logout':{zh:'退出登录',en:'Sign out'},
'set.redeem':{zh:'兑换 & 购买',en:'Redeem & buy'}, 'set.redeemSub':{zh:'PRO 会员',en:'PRO member'},
@@ -1102,7 +1136,7 @@ var I18N = {
'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'},
'dev.current':{zh:'本机',en:'This device'}, 'dev.remove':{zh:'移除',en:'Remove'},
'dev.current':{zh:'本机',en:'This device'}, 'dev.remove':{zh:'移除',en:'Remove'}, 'dev.back':{zh:'返回',en:'Back'},
'dev.lastOnline':{zh:'最后在线',en:'Last online'}, 'dev.now':{zh:'刚刚',en:'just now'},
'dev.h2':{zh:'2 小时前',en:'2h ago'}, 'dev.yesterday':{zh:'昨天',en:'yesterday'},
'state.on':{zh:'已开启',en:'On'}, 'state.off':{zh:'跟随浅色',en:'Off'},
@@ -1114,7 +1148,9 @@ var I18N = {
'login.note':{zh:'(独立静态屏,不并入交互窗口)',en:'(standalone static screen, outside the interactive window)'},
'login.slogan':{zh:'极速畅连 · 稳定加速线路<br>隐私保护 · 全程无日志',en:'Fast & stable lines<br>Privacy protected · No logs'},
'login.title':{zh:'登录账户',en:'Sign in'}, 'login.email':{zh:'邮箱',en:'Email'}, 'login.pwd':{zh:'密码',en:'Password'},
'login.signin':{zh:'登录',en:'Sign in'}, 'login.signup':{zh:'没有账户?免费注册',en:'No account? Sign up free'}
'login.signin':{zh:'登录',en:'Sign in'}, 'login.signup':{zh:'没有账户?免费注册',en:'No account? Sign up free'},
'login.signupTitle':{zh:'注册账户',en:'Sign up'}, 'login.pwd2':{zh:'确认密码',en:'Confirm password'},
'login.signupBtn':{zh:'免费注册',en:'Sign up free'}, 'login.haveAccount':{zh:'已有账户?登录',en:'Have an account? Sign in'}
};
var HTML_KEYS = { 'login.slogan':1 };
@@ -1258,6 +1294,7 @@ function renderDevices(){
list.innerHTML = html;
var cnt = S.persp==='pro' ? 3 : 1;
document.getElementById('devHeaderCount').textContent = cnt + ' / 5';
var _adc = document.getElementById('accDevCount'); if(_adc) _adc.textContent = cnt + ' / 5';
}
/* ========= 统计示例数据(按设备切换:3 周期卡 + 14 天折线,均为演示值) ========= */
@@ -1670,6 +1707,8 @@ var _accBuy=document.getElementById('accBuyRow'); if(_accBuy) _accBuy.addEventLi
var _accOrders=document.getElementById('accOrdersRow'); if(_accOrders) _accOrders.addEventListener('click', function(){ openOrders('account'); });
var _accRedeem=document.getElementById('accRedeemRow'); if(_accRedeem) _accRedeem.addEventListener('click', function(){ openPurchase('account'); });
var _accInvite=document.getElementById('accInviteRow'); if(_accInvite) _accInvite.addEventListener('click', function(){ setView('invite'); });
var _accDevices=document.getElementById('accDevicesRow'); if(_accDevices) _accDevices.addEventListener('click', function(){ setView('devices'); });
var _dvBack=document.getElementById('dvBack'); if(_dvBack) _dvBack.addEventListener('click', function(){ setView('account'); });
var _tbBell=document.getElementById('tbBell'); if(_tbBell) _tbBell.addEventListener('click', function(){ if(S.view!=='notifications'){ S.notifFrom=S.view; } setView('notifications'); });
var _ntBack=document.getElementById('ntBack'); if(_ntBack) _ntBack.addEventListener('click', function(){ setView(S.notifFrom||'connect'); });
var _invBack=document.getElementById('invBack'); if(_invBack) _invBack.addEventListener('click', function(){ setView('account'); });
@@ -1683,6 +1722,14 @@ document.querySelectorAll('.js-inv-copy').forEach(function(b){ b.addEventListene
applyLang();
renderConnect(); renderPersp(); applyUpd(); syncSegs();
})();
/* 登录 ⇆ 注册 表单切换(独立静态帧) */
(function(){
var si=document.getElementById('signinForm'), su=document.getElementById('signupForm');
var toSu=document.getElementById('toSignup'), toSi=document.getElementById('toSignin');
if(toSu) toSu.addEventListener('click', function(){ si.hidden=true; su.hidden=false; });
if(toSi) toSi.addEventListener('click', function(){ su.hidden=true; si.hidden=false; });
})();
</script>
</body>
</html>
+60 -43
View File
@@ -608,48 +608,13 @@
</div>
</div>
<!-- 我的设备 · 免费 1/1 -->
<div id="devFree">
<div class="sec-label" data-i18n="devFreeLbl">我的设备 · 1 / 1 台</div>
<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="devOnlineNow">当前在线</span></div>
</div>
<div class="dv-trail"><button class="icon-mini plain" aria-label="更多"><svg><use href="#i-more-vertical"/></svg></button></div>
</div>
</div>
</div>
<!-- 我的设备 · PRO 3/5 -->
<div id="devPro" hidden>
<div class="sec-label" data-i18n="devProLbl">我的设备 · 3 / 5 台</div>
<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="devOnlineNow">当前在线</span></div>
</div>
<div class="dv-trail"><button class="icon-mini plain" aria-label="更多"><svg><use href="#i-more-vertical"/></svg></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">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="icon-mini plain" aria-label="更多"><svg><use href="#i-more-vertical"/></svg></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="icon-mini plain" aria-label="更多"><svg><use href="#i-more-vertical"/></svg></button></div>
</div>
<!-- 我的设备(下钻至设备管理子屏) -->
<div class="sec-label" data-i18n="secDevices">设备</div>
<div class="card card-flush">
<div class="setting-row is-tap" id="rowDevices">
<span class="icon-box"><svg><use href="#i-monitor-smartphone"/></svg></span>
<div class="st-body"><div class="st-title" data-i18n="myDevices">我的设备</div></div>
<div class="st-trail"><span class="st-val" id="devCount" style="font-family:var(--font-mono)">1 / 1</span><svg><use href="#i-chevron-right"/></svg></div>
</div>
</div>
@@ -1012,6 +977,54 @@
</section>
<!-- 兑换码(仅码,无「去哪买」) -->
<!-- 设备管理:完整设备列表(persona 视角联动:免费 1 台 / PRO 多台) -->
<section class="sub-screen" data-sub="devices">
<div class="sub-scr sub-stack">
<div id="devFree">
<div class="sec-label" data-i18n="devFreeLbl">我的设备 · 1 / 1 台</div>
<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="devOnlineNow">当前在线</span></div>
</div>
<div class="dv-trail"><button class="icon-mini plain" aria-label="更多"><svg><use href="#i-more-vertical"/></svg></button></div>
</div>
</div>
</div>
<div id="devPro" hidden>
<div class="sec-label" data-i18n="devProLbl">我的设备 · 3 / 5 台</div>
<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="devOnlineNow">当前在线</span></div>
</div>
<div class="dv-trail"><button class="icon-mini plain" aria-label="更多"><svg><use href="#i-more-vertical"/></svg></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">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="icon-mini plain" aria-label="更多"><svg><use href="#i-more-vertical"/></svg></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="icon-mini plain" aria-label="更多"><svg><use href="#i-more-vertical"/></svg></button></div>
</div>
</div>
</div>
</div>
</section>
<section class="sub-screen" data-sub="redeem">
<div class="sub-scr sub-stack">
<div class="field">
@@ -1166,6 +1179,7 @@ const I18N = {
planFree:'免费版', planProMeta:'PRO 会员 · 2027-07-11', upgrade:'续费 / 升级', renew:'续费 / 升级',
secAccount:'账户信息', accEmail:'邮箱', accPassword:'密码', accChange:'修改',
devFreeLbl:'我的设备 · 1 / 1 台', devProLbl:'我的设备 · 3 / 5 台',
secDevices:'设备', myDevices:'我的设备', devMgmt:'设备管理',
thisDevice:'本机', devOnlineNow:'当前在线', lastOnline:'最后在线', rel2h:'2 小时前', relYday:'昨天',
secPref:'偏好', prefLang:'语言', prefTheme:'深色外观', prefProtocol:'协议', followLight:'跟随浅色', stateOn:'已开启',
secMore:'更多', navDevices:'设备管理', navSettings:'设置', navRedeem:'兑换 & 购买',
@@ -1237,6 +1251,7 @@ const I18N = {
planFree:'Free', planProMeta:'PRO member · 2027-07-11', upgrade:'Renew / Upgrade', renew:'Renew / Upgrade',
secAccount:'Account', accEmail:'Email', accPassword:'Password', accChange:'Change',
devFreeLbl:'My devices · 1 / 1', devProLbl:'My devices · 3 / 5',
secDevices:'Devices', myDevices:'My Devices', devMgmt:'Device management',
thisDevice:'This device', devOnlineNow:'Online now', lastOnline:'Last online', rel2h:'2h ago', relYday:'yesterday',
secPref:'Preferences', prefLang:'Language', prefTheme:'Dark appearance', prefProtocol:'Protocol', followLight:'Follow light', stateOn:'On',
secMore:'More', navDevices:'Devices', navSettings:'Settings', navRedeem:'Redeem & Buy',
@@ -1515,6 +1530,7 @@ function render(){
$('#bannerPro').hidden = state.persona!=='pro';
$('#devFree').hidden = state.persona!=='free';
$('#devPro').hidden = state.persona!=='pro';
$('#devCount').textContent = state.persona==='pro' ? '3 / 5' : '1 / 1';
// 偏好:主题行随主题联动
const dark=state.theme==='dark';
@@ -1892,7 +1908,7 @@ $('#ordBack').addEventListener('click', ()=>{ if(ordOverlay.dataset.screen==='de
/* ---------- 通用子屏(顶栏铃铛→通知;账户→设置 / 联系 / 邀请 / 兑换;命名空间 sub ---------- */
const subOverlay = $('#subOverlay');
let subToastTimer=null;
const SUB_TITLE = { settings:'navSettings', contact:'navContact', notifications:'navNotifications', invite:'inviteTitle', redeem:'navRedeemCode' };
const SUB_TITLE = { settings:'navSettings', contact:'navContact', notifications:'navNotifications', invite:'inviteTitle', redeem:'navRedeemCode', devices:'devMgmt' };
function subShow(name){
document.querySelectorAll('#subOverlay .sub-screen').forEach(sc=>sc.classList.toggle('is-active', sc.dataset.sub===name));
subOverlay.dataset.sub=name;
@@ -1912,6 +1928,7 @@ $('#rowSettings').addEventListener('click', ()=>subOpen('settings'));
$('#rowContact').addEventListener('click', ()=>subOpen('contact'));
$('#rowInvite').addEventListener('click', ()=>subOpen('invite'));
$('#rowRedeem').addEventListener('click', ()=>subOpen('redeem'));
$('#rowDevices').addEventListener('click', ()=>subOpen('devices'));
// 返回 → 关闭回上一屏
$('#subBack').addEventListener('click', subClose);
// 复制反馈