Polish editor timeline interactions
This commit is contained in:
+110
-45
@@ -11,23 +11,38 @@
|
||||
|
||||
.settings-nav { position: sticky; top: 16px; }
|
||||
.settings-nav .nav-h { font-family: var(--font-mono); font-size: 10.5px; color: var(--black-alpha-48); letter-spacing: .06em; text-transform: uppercase; padding: 0 12px 8px; }
|
||||
.settings-nav a { display: flex; align-items: center; gap: 10px; padding: 10px 12px; font-size: 13px; color: var(--accent-black); border-radius: var(--r-md); border: 1px solid transparent; cursor: pointer; text-decoration: none; transition: background var(--t-base), border-color var(--t-base); position: relative; }
|
||||
.settings-nav a:hover { background: var(--background-lighter); }
|
||||
.settings-nav a:focus-visible { outline: 2px solid var(--heat); outline-offset: 2px; }
|
||||
.settings-nav :where(a, button) { display: flex; align-items: center; gap: 10px; width: 100%; padding: 10px 12px; font: inherit; font-size: 13px; color: var(--accent-black); border-radius: var(--r-md); border: 1px solid transparent; background: transparent; cursor: pointer; text-decoration: none; text-align: left; transition: background var(--t-base), border-color var(--t-base), color var(--t-base); position: relative; }
|
||||
.settings-nav :where(a, button):hover { background: var(--background-lighter); }
|
||||
.settings-nav :where(a, button):focus-visible { outline: 2px solid var(--heat); outline-offset: 2px; }
|
||||
.settings-nav a.active { background: var(--heat-12); color: var(--heat); border-color: var(--heat-20); font-weight: 600; }
|
||||
.settings-nav a svg { width: 16px; height: 16px; stroke-width: 1.5; }
|
||||
.settings-nav :where(a, button) svg { width: 16px; height: 16px; stroke-width: 1.5; flex: 0 0 auto; }
|
||||
.settings-nav a .nav-badge { margin-left: auto; font-family: var(--font-mono); font-size: 10px; color: var(--black-alpha-48); padding: 1px 6px; background: var(--background-lighter); border: 1px solid var(--border-faint); border-radius: var(--r-pill); letter-spacing: .02em; line-height: 14px; }
|
||||
.settings-nav a.active .nav-badge { color: var(--heat); background: var(--accent-white); border-color: var(--heat-20); }
|
||||
.settings-nav a .nav-dot { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); width: 6px; height: 6px; border-radius: 50%; background: var(--heat); display: none; }
|
||||
.settings-nav a.has-changes .nav-dot { display: block; }
|
||||
.settings-nav a.active .nav-dot { right: -4px; }
|
||||
.settings-nav .logout-pill {
|
||||
width: calc(100% - 24px);
|
||||
height: 38px;
|
||||
margin: 4px 12px 0;
|
||||
justify-content: center;
|
||||
border-radius: var(--r-pill);
|
||||
background: var(--accent-black);
|
||||
border-color: var(--accent-black);
|
||||
color: var(--accent-white);
|
||||
font-weight: 500;
|
||||
}
|
||||
.settings-nav .logout-pill:hover,
|
||||
.settings-nav .logout-pill:focus-visible {
|
||||
background: var(--black-alpha-88);
|
||||
border-color: var(--black-alpha-88);
|
||||
color: var(--accent-white);
|
||||
}
|
||||
|
||||
/* ─── pane ─── */
|
||||
.pane { background: var(--surface); border: 1px solid var(--border-faint); border-radius: var(--r-md); padding: 24px; margin-bottom: 16px; }
|
||||
.pane h3 { font-size: 14px; font-weight: 600; margin-bottom: 4px; }
|
||||
.pane .pane-desc { font-size: 12px; color: var(--black-alpha-48); font-family: var(--font-mono); letter-spacing: .02em; margin-bottom: 18px; }
|
||||
.pane.danger { border-color: rgba(180,30,30,.25); background: rgba(180,30,30,.03); }
|
||||
.pane.danger h3 { color: var(--accent-crimson); }
|
||||
|
||||
/* ─── form row ─── */
|
||||
.form-row { display: grid; grid-template-columns: 160px minmax(0, 1fr); gap: 16px; padding: 14px 0; border-bottom: 1px solid var(--border-faint); align-items: center; }
|
||||
@@ -96,6 +111,39 @@
|
||||
.av-up-rules { margin-top: 12px; padding-top: 10px; border-top: 1px dashed var(--border-faint); font-size: 11px; color: var(--black-alpha-56); font-family: var(--font-mono); letter-spacing: .02em; line-height: 1.7; }
|
||||
.av-up-rules .li { display: flex; gap: 8px; }
|
||||
.av-up-rules .li::before { content: '//'; color: var(--black-alpha-32); flex: 0 0 auto; }
|
||||
.logout-confirm-modal { width: min(440px, 92vw); max-width: min(440px, 92vw); }
|
||||
.logout-confirm-copy { margin: 0 0 12px; color: var(--black-alpha-72); }
|
||||
.logout-confirm-points {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
padding: 12px;
|
||||
background: var(--background-lighter);
|
||||
border: 1px solid var(--border-faint);
|
||||
border-radius: var(--r-md);
|
||||
}
|
||||
.logout-confirm-points .li {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.55;
|
||||
color: var(--black-alpha-64);
|
||||
}
|
||||
.logout-confirm-points .li::before {
|
||||
content: '//';
|
||||
flex: 0 0 auto;
|
||||
font-family: var(--font-mono);
|
||||
color: var(--black-alpha-32);
|
||||
}
|
||||
.logout-unsaved-note {
|
||||
margin-top: 12px;
|
||||
padding: 9px 11px;
|
||||
border: 1px solid var(--heat-20);
|
||||
border-radius: var(--r-md);
|
||||
background: var(--heat-12);
|
||||
color: var(--heat);
|
||||
font-size: 12.5px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -120,38 +168,38 @@
|
||||
<aside class="settings-nav" role="tablist" aria-label="设置分区">
|
||||
<div class="nav-h">个人</div>
|
||||
<a href="#sec-profile" class="active" data-jump="sec-profile" role="tab" aria-controls="sec-profile">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-4.4 3.6-8 8-8s8 3.6 8 8"/></svg>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="7.5" r="3.5"/><path d="M5 21a7 7 0 0 1 14 0"/></svg>
|
||||
<span>个人信息</span>
|
||||
<span class="nav-dot" aria-hidden="true"></span>
|
||||
</a>
|
||||
<a href="#sec-security" data-jump="sec-security" role="tab" aria-controls="sec-security">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10Z"/><path d="m9 12 2 2 4-4"/></svg>
|
||||
<span>安全</span>
|
||||
<span class="nav-badge" data-count-source="sec-security">3</span>
|
||||
<span class="nav-dot" aria-hidden="true"></span>
|
||||
</a>
|
||||
<a href="#sec-notify" data-jump="sec-notify" role="tab" aria-controls="sec-notify">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9M10 21a2 2 0 0 0 4 0"/></svg>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10 21a2 2 0 0 0 4 0"/></svg>
|
||||
<span>通知</span>
|
||||
<span class="nav-badge" data-count-source="sec-notify">5</span>
|
||||
<span class="nav-dot" aria-hidden="true"></span>
|
||||
</a>
|
||||
<div class="nav-h" style="margin-top: 16px;">偏好</div>
|
||||
<a href="#sec-pref" data-jump="sec-pref" role="tab" aria-controls="sec-pref">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="m3 7 9-4 9 4-9 4-9-4z"/><path d="m3 12 9 4 9-4"/></svg>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 4h-7"/><path d="M10 4H3"/><path d="M21 12h-9"/><path d="M8 12H3"/><path d="M21 20h-5"/><path d="M12 20H3"/><path d="M14 2v4"/><path d="M8 10v4"/><path d="M16 18v4"/></svg>
|
||||
<span>创作默认</span>
|
||||
<span class="nav-dot" aria-hidden="true"></span>
|
||||
</a>
|
||||
<a href="#sec-display" data-jump="sec-display" role="tab" aria-controls="sec-display">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M2 10h20"/></svg>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="13" rx="2"/><path d="M8 21h8"/><path d="M12 17v4"/></svg>
|
||||
<span>显示</span>
|
||||
<span class="nav-dot" aria-hidden="true"></span>
|
||||
</a>
|
||||
<div class="nav-h" style="margin-top: 16px;">账号</div>
|
||||
<a href="#sec-danger" data-jump="sec-danger" role="tab" aria-controls="sec-danger" style="color: var(--accent-crimson);">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><path d="M12 9v4M12 17h.01"/></svg>
|
||||
<span>危险操作</span>
|
||||
</a>
|
||||
<button class="logout-pill" type="button" id="settings-logout-btn" onclick="logoutCurrentDevice()">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><path d="m16 17 5-5-5-5"/><path d="M21 12H9"/></svg>
|
||||
<span>退出登录</span>
|
||||
</button>
|
||||
</aside>
|
||||
|
||||
<!-- 右侧内容 -->
|
||||
@@ -405,40 +453,40 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── 危险操作 ─── -->
|
||||
<section class="pane danger" id="sec-danger">
|
||||
<h3>危险操作</h3>
|
||||
<div class="pane-desc">// 这些操作不可撤销,请确认后再执行</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="lbl">导出我的数据<div class="lbl-sub">// 项目 + 资产元数据</div></div>
|
||||
<div class="val">
|
||||
<span class="static mono" style="font-size: 11.5px; color: var(--black-alpha-56);">// 准备时间约 24 小时,完成后邮件通知</span>
|
||||
<button class="btn btn-sm" style="margin-left: auto;" onclick="Shell.toast('已申请导出', '约 24 小时后邮件发送')">申请导出</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="lbl">退出登录</div>
|
||||
<div class="val">
|
||||
<span class="static mono" style="font-size: 11.5px; color: var(--black-alpha-56);">// 仅退出当前设备,数据保留</span>
|
||||
<button class="btn btn-sm" style="margin-left: auto; color: var(--accent-crimson); border-color: var(--accent-crimson);" onclick="if(confirm('确定退出登录?')){Shell.toast('已退出','正在跳转登录页');setTimeout(()=>location.href='login.html',600);}">退出登录</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="lbl">注销账号</div>
|
||||
<div class="val">
|
||||
<span class="static mono" style="font-size: 11.5px; color: var(--black-alpha-56);">// 团队余额清零、所有项目作为孤儿归档</span>
|
||||
<button class="btn btn-sm" style="margin-left: auto; color: var(--accent-crimson); border-color: var(--accent-crimson);" onclick="if(confirm('彻底注销当前账号? 此操作不可恢复')) Shell.toast('已提交注销申请', '24 小时内人工复核')">注销账号</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div style="text-align: center; padding: 24px 0 8px; color: var(--black-alpha-32); font-family: var(--font-mono); font-size: 11px; letter-spacing: .04em;">
|
||||
// Airshelf · v2.1 · build 20260521
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- ─── 退出登录确认 modal ─── -->
|
||||
<div class="modal-bg" id="logout-confirm-bg" onclick="if(event.target===this)Shell.closeModal('logout-confirm-bg')">
|
||||
<div class="modal logout-confirm-modal" role="dialog" aria-modal="true" aria-labelledby="logout-confirm-title" aria-describedby="logout-confirm-desc">
|
||||
<span class="corner-tr" aria-hidden></span><span class="corner-bl" aria-hidden></span>
|
||||
<div class="modal-h">
|
||||
<div class="ic-m">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><path d="m16 17 5-5-5-5"/><path d="M21 12H9"/></svg>
|
||||
</div>
|
||||
<div class="ti" id="logout-confirm-title">退出当前账号<span>// LOG OUT CURRENT SESSION</span></div>
|
||||
</div>
|
||||
<div class="modal-b" id="logout-confirm-desc">
|
||||
<p class="logout-confirm-copy">确认后将退出当前设备上的 Airshelf,再次使用需要重新登录。</p>
|
||||
<div class="logout-confirm-points">
|
||||
<div class="li">项目、资产、团队成员与余额数据都会保留</div>
|
||||
<div class="li">仅影响当前浏览器会话,不会下线其他设备</div>
|
||||
</div>
|
||||
<div class="logout-unsaved-note" id="logout-unsaved-note" hidden>当前有未保存的设置变更,退出后这些变更不会保存。</div>
|
||||
</div>
|
||||
<div class="modal-f">
|
||||
<button class="btn" type="button" id="logout-confirm-cancel" onclick="Shell.closeModal('logout-confirm-bg')">取消</button>
|
||||
<button class="btn btn-primary" type="button" id="logout-confirm-ok">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><path d="m16 17 5-5-5-5"/><path d="M21 12H9"/></svg>
|
||||
确认退出
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ─── 上传头像 modal ─── -->
|
||||
<div class="modal-bg" id="avatar-up-bg" onclick="if(event.target===this)Shell.closeModal('avatar-up-bg')">
|
||||
<div class="modal av-up-modal">
|
||||
@@ -494,8 +542,25 @@ Shell.render({
|
||||
});
|
||||
|
||||
/* ─── 配置 ─── */
|
||||
const SECTIONS = ['sec-profile', 'sec-security', 'sec-notify', 'sec-pref', 'sec-display', 'sec-danger'];
|
||||
const SECTIONS = ['sec-profile', 'sec-security', 'sec-notify', 'sec-pref', 'sec-display'];
|
||||
const navLinks = document.querySelectorAll('.settings-nav a[data-jump]');
|
||||
let suppressLeavePrompt = false;
|
||||
|
||||
function logoutCurrentDevice() {
|
||||
const note = document.getElementById('logout-unsaved-note');
|
||||
if (note) note.hidden = !(typeof dirtyFields !== 'undefined' && dirtyFields.size > 0);
|
||||
Shell.openModal('logout-confirm-bg');
|
||||
requestAnimationFrame(() => document.getElementById('logout-confirm-cancel')?.focus());
|
||||
}
|
||||
|
||||
function confirmLogout() {
|
||||
suppressLeavePrompt = true;
|
||||
Shell.closeModal('logout-confirm-bg');
|
||||
Shell.toast('已退出', '正在跳转登录页');
|
||||
setTimeout(() => location.href = 'login.html', 600);
|
||||
}
|
||||
|
||||
document.getElementById('logout-confirm-ok')?.addEventListener('click', confirmLogout);
|
||||
|
||||
/* ─── 1. 点击 nav → 只显示对应 section,其余隐藏 + 同步 URL hash ─── */
|
||||
function showSection(id) {
|
||||
@@ -651,7 +716,7 @@ cancelBtn.addEventListener('click', () => {
|
||||
|
||||
/* ─── 8. 离开页面前提醒(有未保存变更时)─── */
|
||||
window.addEventListener('beforeunload', e => {
|
||||
if (dirtyFields.size > 0) {
|
||||
if (!suppressLeavePrompt && dirtyFields.size > 0) {
|
||||
e.preventDefault();
|
||||
e.returnValue = '';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user