525 lines
27 KiB
HTML
525 lines
27 KiB
HTML
<!doctype html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>设置 · 流·Studio</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||
<link rel="stylesheet" href="assets/restraint.css">
|
||
<style>
|
||
/* ─── 设置布局:左 nav + 右 panel ─── */
|
||
.settings-grid { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 24px; align-items: start; }
|
||
|
||
.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); }
|
||
.settings-nav a:hover { background: var(--background-lighter); }
|
||
.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; }
|
||
|
||
/* ─── 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; }
|
||
.form-row:last-child { border-bottom: 0; }
|
||
.form-row .lbl { font-size: 12.5px; color: var(--black-alpha-56); }
|
||
.form-row .lbl .req { color: var(--accent-crimson); margin-left: 2px; }
|
||
.form-row .lbl-sub { font-size: 11px; color: var(--black-alpha-48); font-family: var(--font-mono); margin-top: 2px; letter-spacing: .02em; }
|
||
.form-row .val { display: flex; align-items: center; gap: 10px; min-width: 0; }
|
||
.form-row .val .input, .form-row .val .select { width: 100%; max-width: 380px; }
|
||
.form-row .val .static { font-size: 13px; color: var(--accent-black); font-variant-numeric: tabular-nums; }
|
||
.form-row .val .static.mono { font-family: var(--font-mono); font-size: 12.5px; color: var(--black-alpha-56); }
|
||
.form-row .val .role-tag { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; border-radius: var(--r-pill); font-size: 11px; font-weight: 500; background: var(--heat-12); color: var(--heat); }
|
||
.form-row .val .role-tag .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--heat); }
|
||
|
||
/* ─── 头像上传 ─── */
|
||
.avatar-edit { display: flex; align-items: center; gap: 16px; }
|
||
.avatar-edit .av-big { width: 64px; height: 64px; border-radius: 50%; background: var(--background-lighter); border: 1px solid var(--border-faint); display: grid; place-items: center; font-size: 24px; font-weight: 600; color: var(--accent-black); }
|
||
.avatar-edit .av-actions { display: flex; gap: 8px; }
|
||
|
||
/* ─── toggle switch ─── */
|
||
.switch { position: relative; width: 36px; height: 20px; flex: 0 0 36px; }
|
||
.switch input { opacity: 0; width: 0; height: 0; }
|
||
.switch .slider { position: absolute; inset: 0; background: var(--black-alpha-24); border-radius: 20px; cursor: pointer; transition: background var(--t-base); }
|
||
.switch .slider::before { content: ''; position: absolute; left: 2px; top: 2px; width: 16px; height: 16px; background: var(--accent-white); border-radius: 50%; transition: transform var(--t-base); }
|
||
.switch input:checked + .slider { background: var(--heat); }
|
||
.switch input:checked + .slider::before { transform: translateX(16px); }
|
||
|
||
/* ─── 偏好选项卡 ─── */
|
||
.pref-choices { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 8px; max-width: 540px; }
|
||
.pref-choice { padding: 10px 12px; border: 1px solid var(--border-faint); border-radius: var(--r-md); cursor: pointer; transition: all var(--t-base); }
|
||
.pref-choice:hover { background: var(--background-lighter); }
|
||
.pref-choice.selected { border-color: var(--heat); background: var(--heat-12); }
|
||
.pref-choice .t { font-size: 12.5px; font-weight: 600; color: var(--accent-black); }
|
||
.pref-choice .d { font-size: 11px; color: var(--black-alpha-48); margin-top: 2px; font-family: var(--font-mono); letter-spacing: .02em; }
|
||
.pref-choice.selected .t { color: var(--heat); }
|
||
|
||
/* ─── 时长档 ─── */
|
||
.duration-row { display: flex; gap: 8px; }
|
||
.dur-chip { padding: 6px 14px; border: 1px solid var(--border-faint); border-radius: var(--r-md); font-size: 13px; cursor: pointer; font-family: var(--font-mono); font-variant-numeric: tabular-nums; transition: all var(--t-base); background: var(--surface); }
|
||
.dur-chip:hover { background: var(--background-lighter); }
|
||
.dur-chip.selected { border-color: var(--heat); background: var(--heat-12); color: var(--heat); font-weight: 600; }
|
||
|
||
/* ─── 设备列表 ─── */
|
||
.device-row { display: flex; align-items: center; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--border-faint); }
|
||
.device-row:last-child { border-bottom: 0; }
|
||
.device-row .ic { width: 36px; height: 36px; border-radius: var(--r-md); background: var(--background-lighter); display: grid; place-items: center; color: var(--black-alpha-56); }
|
||
.device-row .ic svg { width: 18px; height: 18px; }
|
||
.device-row .nm { font-size: 13px; font-weight: 500; }
|
||
.device-row .meta { font-size: 11.5px; color: var(--black-alpha-48); font-family: var(--font-mono); margin-top: 2px; letter-spacing: .02em; }
|
||
.device-row .tag-cur { font-family: var(--font-mono); font-size: 10.5px; padding: 1px 6px; background: var(--accent-forest); color: var(--accent-white); border-radius: var(--r-sm); margin-left: 8px; letter-spacing: .04em; font-weight: 600; }
|
||
.device-row .spacer { margin-left: auto; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id="page">
|
||
|
||
<div class="page-head">
|
||
<div>
|
||
<h1>设置</h1>
|
||
<div class="sub"><span class="mono">// 个人信息 · 偏好 · 通知 · 安全</span></div>
|
||
</div>
|
||
<div class="actions">
|
||
<button class="btn" id="save-cancel" disabled style="opacity:.5; cursor:not-allowed;">取消</button>
|
||
<button class="btn btn-primary" id="save-btn" disabled style="opacity:.5; cursor:not-allowed;">
|
||
<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="M4 12l5 5L20 6"/></svg>
|
||
保存所有变更
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-grid">
|
||
<!-- 左侧 nav -->
|
||
<aside class="settings-nav">
|
||
<div class="nav-h">个人</div>
|
||
<a href="#sec-profile" class="active" data-jump="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>
|
||
个人信息
|
||
</a>
|
||
<a href="#sec-security" data-jump="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>
|
||
安全
|
||
</a>
|
||
<a href="#sec-notify" data-jump="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>
|
||
通知
|
||
</a>
|
||
<div class="nav-h" style="margin-top: 16px;">偏好</div>
|
||
<a href="#sec-pref" data-jump="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>
|
||
创作默认
|
||
</a>
|
||
<a href="#sec-display" data-jump="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>
|
||
显示
|
||
</a>
|
||
<div class="nav-h" style="margin-top: 16px;">账号</div>
|
||
<a href="#sec-danger" data-jump="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>
|
||
危险操作
|
||
</a>
|
||
</aside>
|
||
|
||
<!-- 右侧内容 -->
|
||
<main>
|
||
<!-- ─── 个人信息 ─── -->
|
||
<section class="pane" id="sec-profile">
|
||
<h3>个人信息</h3>
|
||
<div class="pane-desc">// 头像、姓名、联系方式 · 邮箱用于接收通知</div>
|
||
|
||
<div class="form-row">
|
||
<div class="lbl">头像</div>
|
||
<div class="val">
|
||
<div class="avatar-edit">
|
||
<div class="av-big">李</div>
|
||
<div class="av-actions">
|
||
<button class="btn btn-sm" onclick="Shell.toast('上传头像', '占位 · 选择本地图片')">上传新头像</button>
|
||
<button class="btn btn-ghost btn-sm" onclick="Shell.toast('恢复默认头像', '已重置')">恢复默认</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="lbl">显示名称<span class="req">*</span></div>
|
||
<div class="val"><input class="input" id="prof-name" value="小李" data-track></div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="lbl">登录邮箱</div>
|
||
<div class="val">
|
||
<input class="input" id="prof-email" value="li@shop.com" data-track>
|
||
<button class="btn btn-ghost btn-sm" onclick="Shell.toast('已发送验证邮件', 'li@shop.com')">验证</button>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="lbl">手机号</div>
|
||
<div class="val">
|
||
<input class="input" id="prof-phone" value="138****8000" data-track>
|
||
<button class="btn btn-ghost btn-sm" onclick="Shell.toast('已发送短信验证码', '+86 138****8000')">更换</button>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="lbl">所属团队<div class="lbl-sub">// 一人一团队</div></div>
|
||
<div class="val">
|
||
<span class="static">小李的店</span>
|
||
<span class="role-tag"><span class="dot"></span>超管 · 创建者</span>
|
||
<a href="team.html" style="font-size: 12px; color: var(--heat); text-decoration: none; margin-left: auto;">管理团队 →</a>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="lbl">用户 ID<div class="lbl-sub">// 不可改</div></div>
|
||
<div class="val"><span class="static mono">USR-2026-A8F2-001</span></div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ─── 安全 ─── -->
|
||
<section class="pane" id="sec-security">
|
||
<h3>安全</h3>
|
||
<div class="pane-desc">// 登录密码、双因素、在用设备</div>
|
||
|
||
<div class="form-row">
|
||
<div class="lbl">登录密码</div>
|
||
<div class="val">
|
||
<span class="static mono">●●●●●●●●●●</span>
|
||
<span class="muted-2 mono" style="font-size: 11px; margin-left: auto;">上次修改 2026-04-12</span>
|
||
<button class="btn btn-sm" onclick="Shell.toast('修改密码', '/settings/password')" style="margin-left: 10px;">修改</button>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="lbl">两步验证<div class="lbl-sub">// 推荐开启</div></div>
|
||
<div class="val">
|
||
<label class="switch"><input type="checkbox" id="opt-2fa"><span class="slider"></span></label>
|
||
<span class="static mono" style="font-size: 11.5px; color: var(--black-alpha-48);">短信 + Authenticator</span>
|
||
</div>
|
||
</div>
|
||
|
||
<h3 style="margin-top: 24px;">在用设备</h3>
|
||
<div class="pane-desc">// 不在此列表上的设备登录会触发短信告警</div>
|
||
<div>
|
||
<div class="device-row">
|
||
<div class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="14" rx="2"/><path d="M2 20h20"/></svg></div>
|
||
<div>
|
||
<div class="nm">MacBook Pro · Chrome<span class="tag-cur">CURRENT</span></div>
|
||
<div class="meta">// 上海 · 2026-05-21 14:08 · IP 116.xxx.xxx.42</div>
|
||
</div>
|
||
<div class="spacer"></div>
|
||
<span class="muted-2 mono" style="font-size: 11px;">当前会话</span>
|
||
</div>
|
||
<div class="device-row">
|
||
<div class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="2" width="12" height="20" rx="2"/><path d="M11 18h2"/></svg></div>
|
||
<div>
|
||
<div class="nm">iPhone 15 · Safari</div>
|
||
<div class="meta">// 上海 · 2026-05-20 21:43</div>
|
||
</div>
|
||
<div class="spacer"></div>
|
||
<button class="btn btn-ghost btn-sm" onclick="Shell.toast('已下线', 'iPhone 15')">下线</button>
|
||
</div>
|
||
<div class="device-row">
|
||
<div class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="14" rx="2"/><path d="M2 20h20"/></svg></div>
|
||
<div>
|
||
<div class="nm">Windows · Edge</div>
|
||
<div class="meta">// 杭州 · 2026-05-18 09:12</div>
|
||
</div>
|
||
<div class="spacer"></div>
|
||
<button class="btn btn-ghost btn-sm" onclick="Shell.toast('已下线', 'Windows Edge')">下线</button>
|
||
</div>
|
||
</div>
|
||
<div style="margin-top: 14px;">
|
||
<button class="btn" onclick="if(confirm('下线所有其他设备?')) Shell.toast('已下线其他设备', '2 个')">下线所有其他设备</button>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ─── 通知 ─── -->
|
||
<section class="pane" id="sec-notify">
|
||
<h3>通知</h3>
|
||
<div class="pane-desc">// 邮件、短信、站内提示开关</div>
|
||
|
||
<div class="form-row">
|
||
<div class="lbl">项目完成通知<div class="lbl-sub">// 视频导出后</div></div>
|
||
<div class="val">
|
||
<label class="switch"><input type="checkbox" id="n-export" checked><span class="slider"></span></label>
|
||
<span class="static mono" style="font-size: 11.5px; color: var(--black-alpha-48);">站内 · 邮件 · 短信</span>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="lbl">任务失败告警</div>
|
||
<div class="val">
|
||
<label class="switch"><input type="checkbox" id="n-fail" checked><span class="slider"></span></label>
|
||
<span class="static mono" style="font-size: 11.5px; color: var(--black-alpha-48);">站内 · 邮件</span>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="lbl">额度不足提醒<div class="lbl-sub">// 团队或个人剩余 < 20%</div></div>
|
||
<div class="val">
|
||
<label class="switch"><input type="checkbox" id="n-quota" checked><span class="slider"></span></label>
|
||
<span class="static mono" style="font-size: 11.5px; color: var(--black-alpha-48);">站内 · 短信</span>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="lbl">异地登录告警</div>
|
||
<div class="val">
|
||
<label class="switch"><input type="checkbox" id="n-login" checked><span class="slider"></span></label>
|
||
<span class="static mono" style="font-size: 11.5px; color: var(--black-alpha-48);">短信</span>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="lbl">营销 / 产品更新</div>
|
||
<div class="val">
|
||
<label class="switch"><input type="checkbox" id="n-marketing"><span class="slider"></span></label>
|
||
<span class="static mono" style="font-size: 11.5px; color: var(--black-alpha-48);">邮件</span>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ─── 创作默认 ─── -->
|
||
<section class="pane" id="sec-pref">
|
||
<h3>创作默认</h3>
|
||
<div class="pane-desc">// 新建项目时的预填值,可在向导中改</div>
|
||
|
||
<div class="form-row" style="grid-template-columns: 160px 1fr; align-items: flex-start;">
|
||
<div class="lbl" style="padding-top: 4px;">默认模板</div>
|
||
<div class="val" style="display: block;">
|
||
<div class="pref-choices" id="pref-template">
|
||
<div class="pref-choice selected" data-v="pain"><div class="t">痛点种草</div><div class="d">// 30s 默认档</div></div>
|
||
<div class="pref-choice" data-v="unbox"><div class="t">开箱测评</div><div class="d">// 45s 默认档</div></div>
|
||
<div class="pref-choice" data-v="compare"><div class="t">对比展示</div><div class="d">// 45s 默认档</div></div>
|
||
<div class="pref-choice" data-v="howto"><div class="t">教程演示</div><div class="d">// 60s 默认档</div></div>
|
||
<div class="pref-choice" data-v="drama"><div class="t">剧情带货</div><div class="d">// 60s 默认档</div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="lbl">默认时长档</div>
|
||
<div class="val">
|
||
<div class="duration-row" id="pref-duration">
|
||
<span class="dur-chip" data-v="30">30s</span>
|
||
<span class="dur-chip" data-v="45">45s</span>
|
||
<span class="dur-chip selected" data-v="60">60s</span>
|
||
</div>
|
||
<span class="static mono" style="font-size: 11.5px; color: var(--black-alpha-48); margin-left: 10px;">// 60s = 4 段 × 15s</span>
|
||
</div>
|
||
</div>
|
||
<div class="form-row" style="grid-template-columns: 160px 1fr; align-items: flex-start;">
|
||
<div class="lbl" style="padding-top: 4px;">默认字幕样式</div>
|
||
<div class="val" style="display: block;">
|
||
<div class="pref-choices" id="pref-subtitle">
|
||
<div class="pref-choice selected" data-v="big-variety"><div class="t">大字综艺</div><div class="d">// 抖音热门</div></div>
|
||
<div class="pref-choice" data-v="clean-ec"><div class="t">简洁电商</div><div class="d">// 信息清晰</div></div>
|
||
<div class="pref-choice" data-v="premium"><div class="t">高级排版</div><div class="d">// 居中衬线</div></div>
|
||
<div class="pref-choice" data-v="bullet"><div class="t">弹幕轻量</div><div class="d">// 滚动出现</div></div>
|
||
<div class="pref-choice" data-v="emphasis"><div class="t">强调爆款</div><div class="d">// 高对比</div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="lbl">默认 BGM 库</div>
|
||
<div class="val">
|
||
<select class="select" id="pref-bgm" data-track>
|
||
<option value="kapian">抖音 Top10 卡点曲库</option>
|
||
<option value="emotion">情绪向 · 治愈/悬念</option>
|
||
<option value="urban">都市电子 · 通勤场景</option>
|
||
<option value="none">无 BGM</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="lbl">默认转场</div>
|
||
<div class="val">
|
||
<select class="select" id="pref-transition" data-track>
|
||
<option>无转场</option>
|
||
<option selected>淡入淡出 · 0.3s</option>
|
||
<option>滑动 · 0.3s</option>
|
||
<option>缩放 · 0.3s</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="lbl">导出水印<div class="lbl-sub">// VIP 可关闭</div></div>
|
||
<div class="val">
|
||
<label class="switch"><input type="checkbox" id="opt-watermark" checked disabled><span class="slider"></span></label>
|
||
<span class="static mono" style="font-size: 11.5px; color: var(--black-alpha-48);">右下角 · 流·Studio</span>
|
||
<a href="account.html" style="font-size: 12px; color: var(--heat); text-decoration: none; margin-left: auto;">升级 VIP →</a>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ─── 显示 ─── -->
|
||
<section class="pane" id="sec-display">
|
||
<h3>显示</h3>
|
||
<div class="pane-desc">// 界面外观与语言</div>
|
||
|
||
<div class="form-row">
|
||
<div class="lbl">外观</div>
|
||
<div class="val">
|
||
<select class="select" id="pref-theme" data-track>
|
||
<option selected>跟随系统</option>
|
||
<option>浅色</option>
|
||
<option disabled>深色(V2)</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="lbl">语言</div>
|
||
<div class="val">
|
||
<select class="select" id="pref-lang" data-track>
|
||
<option selected>简体中文</option>
|
||
<option disabled>English(V2)</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="lbl">表格密度</div>
|
||
<div class="val">
|
||
<select class="select" id="pref-density" data-track>
|
||
<option>紧凑</option>
|
||
<option selected>标准</option>
|
||
<option>宽松</option>
|
||
</select>
|
||
</div>
|
||
</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('已退出', '正在跳转登录页')">退出登录</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;">
|
||
// 流·Studio · v2.1 · build 20260521
|
||
</div>
|
||
</main>
|
||
</div>
|
||
|
||
</div>
|
||
<script src="assets/shell.js"></script>
|
||
<script>
|
||
Shell.render({
|
||
active: 'settings',
|
||
crumbs: [{ label: '工作台', href: 'index.html' }, { label: '设置' }]
|
||
});
|
||
|
||
/* ─── 侧边 nav 高亮 + 滚动联动 ─── */
|
||
const sections = ['sec-profile', 'sec-security', 'sec-notify', 'sec-pref', 'sec-display', 'sec-danger'];
|
||
const navLinks = document.querySelectorAll('.settings-nav a[data-jump]');
|
||
|
||
navLinks.forEach(a => {
|
||
a.addEventListener('click', e => {
|
||
e.preventDefault();
|
||
const id = a.dataset.jump;
|
||
const el = document.getElementById(id);
|
||
if (el) {
|
||
const contentEl = document.getElementById('page-content') || document.querySelector('.content');
|
||
const offset = 16;
|
||
if (contentEl) {
|
||
contentEl.scrollTo({ top: el.offsetTop - contentEl.offsetTop - offset, behavior: 'smooth' });
|
||
} else {
|
||
el.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||
}
|
||
}
|
||
});
|
||
});
|
||
|
||
const contentEl = document.getElementById('page-content') || document.querySelector('.content');
|
||
const observer = new IntersectionObserver(entries => {
|
||
entries.forEach(en => {
|
||
if (en.isIntersecting) {
|
||
const id = en.target.id;
|
||
navLinks.forEach(a => a.classList.toggle('active', a.dataset.jump === id));
|
||
}
|
||
});
|
||
}, { root: contentEl || null, rootMargin: '-20% 0px -60% 0px', threshold: 0 });
|
||
sections.forEach(id => {
|
||
const el = document.getElementById(id);
|
||
if (el) observer.observe(el);
|
||
});
|
||
|
||
/* ─── 偏好 chip 选择 ─── */
|
||
function bindChoice(containerId, label) {
|
||
const ct = document.getElementById(containerId);
|
||
if (!ct) return;
|
||
ct.querySelectorAll('.pref-choice').forEach(c => {
|
||
c.addEventListener('click', () => {
|
||
ct.querySelectorAll('.pref-choice').forEach(x => x.classList.remove('selected'));
|
||
c.classList.add('selected');
|
||
markDirty();
|
||
});
|
||
});
|
||
}
|
||
bindChoice('pref-template');
|
||
bindChoice('pref-subtitle');
|
||
|
||
document.querySelectorAll('#pref-duration .dur-chip').forEach(c => {
|
||
c.addEventListener('click', () => {
|
||
document.querySelectorAll('#pref-duration .dur-chip').forEach(x => x.classList.remove('selected'));
|
||
c.classList.add('selected');
|
||
markDirty();
|
||
});
|
||
});
|
||
|
||
/* ─── dirty state ─── */
|
||
let dirty = false;
|
||
function markDirty() {
|
||
if (dirty) return;
|
||
dirty = true;
|
||
const saveBtn = document.getElementById('save-btn');
|
||
const cancelBtn = document.getElementById('save-cancel');
|
||
[saveBtn, cancelBtn].forEach(b => {
|
||
b.disabled = false;
|
||
b.style.opacity = '';
|
||
b.style.cursor = '';
|
||
});
|
||
}
|
||
function clearDirty() {
|
||
dirty = false;
|
||
const saveBtn = document.getElementById('save-btn');
|
||
const cancelBtn = document.getElementById('save-cancel');
|
||
[saveBtn, cancelBtn].forEach(b => {
|
||
b.disabled = true;
|
||
b.style.opacity = '.5';
|
||
b.style.cursor = 'not-allowed';
|
||
});
|
||
}
|
||
document.querySelectorAll('[data-track], input[type="checkbox"], select').forEach(el => {
|
||
el.addEventListener('change', markDirty);
|
||
if (el.tagName === 'INPUT' && el.type !== 'checkbox') el.addEventListener('input', markDirty);
|
||
});
|
||
|
||
document.getElementById('save-btn').addEventListener('click', () => {
|
||
if (!dirty) return;
|
||
Shell.toast('设置已保存', '所有变更已生效');
|
||
clearDirty();
|
||
});
|
||
document.getElementById('save-cancel').addEventListener('click', () => {
|
||
if (!dirty) return;
|
||
if (confirm('放弃未保存的变更?')) location.reload();
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|