164 lines
6.5 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>光湖 · 注册</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{
--bg:#060a14;--card:rgba(14,20,48,0.8);
--blue:#60a5fa;--cyan:#22d3ee;--text:#eaf0ff;--dim:#94a7d0;
--border:rgba(96,165,250,0.15);--ok:#34d399;--err:#f87171;
}
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:var(--bg);color:var(--text);min-height:100vh;display:flex;align-items:center;justify-content:center;overflow:hidden}
.bg{position:fixed;inset:0;background:radial-gradient(ellipse at 30% 20%,rgba(40,80,160,0.3),transparent 60%),radial-gradient(ellipse at 70% 80%,rgba(30,60,130,0.25),transparent 60%),#060a14;z-index:0}
.card{position:relative;z-index:1;width:100%;max-width:400px;padding:40px 32px;background:var(--card);border:1px solid var(--border);border-radius:16px;backdrop-filter:blur(20px)}
.logo{text-align:center;font-size:32px;margin-bottom:8px}
.title{text-align:center;font-size:22px;font-weight:500;color:var(--text);margin-bottom:4px}
.sub{text-align:center;font-size:13px;color:var(--dim);margin-bottom:28px}
.step{display:none}
.step.active{display:block}
label{display:block;font-size:13px;color:var(--dim);margin-bottom:6px}
input{width:100%;padding:12px;border-radius:10px;border:1px solid var(--border);background:rgba(12,20,40,0.6);color:var(--text);font-size:15px;outline:none;transition:border .3s;margin-bottom:16px}
input:focus{border-color:var(--blue)}
.btn{width:100%;padding:12px;border-radius:10px;border:none;background:linear-gradient(135deg,var(--blue),var(--cyan));color:#fff;font-size:15px;font-weight:500;cursor:pointer;transition:opacity .3s}
.btn:hover{opacity:.85}
.btn:disabled{opacity:.5;cursor:not-allowed}
.msg{text-align:center;font-size:13px;margin-top:12px;min-height:20px}
.msg.ok{color:var(--ok)}
.msg.err{color:var(--err)}
.back{display:block;text-align:center;margin-top:16px;font-size:13px;color:var(--dim);text-decoration:none}
.back:hover{color:var(--blue)}
.countdown{color:var(--dim)}
</style>
</head>
<body>
<div class="bg"></div>
<div class="card">
<div class="logo">🌊</div>
<div class="title">注册光湖协作中心</div>
<div class="sub">用邮箱注册,立即开始协作</div>
<div id="step1" class="step active">
<label>邮箱地址</label>
<input type="email" id="email" placeholder="your@email.com" autocomplete="email">
<button class="btn" id="sendBtn" onclick="sendCode()">发送验证码</button>
<div class="msg" id="msg1"></div>
</div>
<div id="step2" class="step">
<label>验证码(已发送至 <span id="emailDisplay"></span></label>
<input type="text" id="code" placeholder="6位验证码" maxlength="6" autocomplete="one-time-code">
<label>设置密码</label>
<input type="password" id="password" placeholder="至少6位">
<button class="btn" id="verifyBtn" onclick="verifyCode()">完成注册</button>
<div class="msg" id="msg2"></div>
<a href="#" class="back" onclick="backToStep1()">← 换一个邮箱</a>
</div>
<div class="msg" id="msg3" style="margin-top:16px"></div>
<a href="/outline/" class="back" style="margin-top:8px">已有账号?去登录 →</a>
</div>
<script>
let countdownTimer = null;
async function sendCode() {
const email = document.getElementById('email').value.trim();
const msg = document.getElementById('msg1');
const btn = document.getElementById('sendBtn');
if (!email || !email.includes('@')) {
msg.className = 'msg err'; msg.textContent = '请输入有效邮箱';
return;
}
btn.disabled = true; btn.textContent = '发送中...';
msg.className = 'msg'; msg.textContent = '';
try {
const res = await fetch('/register-api/send-code', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({email})
});
const data = await res.json();
if (data.ok) {
document.getElementById('emailDisplay').textContent = email;
document.getElementById('step1').classList.remove('active');
document.getElementById('step2').classList.add('active');
startCountdown(60);
} else {
msg.className = 'msg err'; msg.textContent = data.error;
btn.disabled = false; btn.textContent = '发送验证码';
}
} catch(e) {
msg.className = 'msg err'; msg.textContent = '网络错误,请重试';
btn.disabled = false; btn.textContent = '发送验证码';
}
}
function startCountdown(sec) {
if (countdownTimer) clearInterval(countdownTimer);
const btn = document.getElementById('sendBtn');
btn.disabled = true;
countdownTimer = setInterval(() => {
sec--;
if (sec <= 0) {
clearInterval(countdownTimer);
btn.disabled = false;
btn.textContent = '重新发送';
} else {
btn.textContent = `${sec}秒后重发`;
}
}, 1000);
}
async function verifyCode() {
const email = document.getElementById('email').value.trim();
const code = document.getElementById('code').value.trim();
const password = document.getElementById('password').value;
const msg = document.getElementById('msg2');
const btn = document.getElementById('verifyBtn');
if (!code || code.length !== 6) {
msg.className = 'msg err'; msg.textContent = '请输入6位验证码';
return;
}
if (!password || password.length < 6) {
msg.className = 'msg err'; msg.textContent = '密码至少6位';
return;
}
btn.disabled = true; btn.textContent = '注册中...';
try {
const res = await fetch('/register-api/verify', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({email, code, password})
});
const data = await res.json();
if (data.ok) {
msg.className = 'msg ok'; msg.textContent = '注册成功3秒后跳转登录...';
setTimeout(() => { window.location.href = '/outline/'; }, 3000);
} else {
msg.className = 'msg err'; msg.textContent = data.error;
btn.disabled = false; btn.textContent = '完成注册';
}
} catch(e) {
msg.className = 'msg err'; msg.textContent = '网络错误,请重试';
btn.disabled = false; btn.textContent = '完成注册';
}
}
function backToStep1() {
document.getElementById('step2').classList.remove('active');
document.getElementById('step1').classList.add('active');
if (countdownTimer) clearInterval(countdownTimer);
const btn = document.getElementById('sendBtn');
btn.disabled = false; btn.textContent = '发送验证码';
}
</script>
</body>
</html>