Files
realizemultiagent/shared/code/frontend/register.html
2026-04-02 00:59:42 +08:00

225 lines
8.8 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f0f2f5; display: flex; align-items: center; justify-content: center; min-height: 100vh; }
.container { background: #fff; border-radius: 12px; box-shadow: 0 2px 12px rgba(0,0,0,0.1); padding: 40px 36px; width: 400px; }
.logo { text-align: center; margin-bottom: 28px; }
.logo h1 { font-size: 24px; color: #1a1a2e; font-weight: 600; }
.logo p { font-size: 13px; color: #888; margin-top: 4px; }
.form-group { margin-bottom: 16px; }
label { display: block; font-size: 14px; color: #333; margin-bottom: 6px; font-weight: 500; }
input { width: 100%; height: 44px; padding: 0 14px; border: 1px solid #ddd; border-radius: 8px; font-size: 15px; outline: none; transition: border-color 0.2s; }
input:focus { border-color: #4f46e5; }
.captcha-row { display: flex; gap: 10px; }
.captcha-row input { flex: 1; }
.captcha-canvas { height: 44px; border-radius: 8px; cursor: pointer; border: 1px solid #ddd; background: #f9fafb; }
.sms-row { display: flex; gap: 10px; }
.sms-row input { flex: 1; }
.btn-sms { height: 44px; padding: 0 16px; background: #e8e7ff; color: #4f46e5; border: none; border-radius: 8px; font-size: 14px; font-weight: 600; cursor: pointer; white-space: nowrap; flex-shrink: 0; }
.btn-sms:hover { background: #dddbf0; }
.btn-sms:disabled { background: #f0f0f0; color: #aaa; cursor: not-allowed; }
.btn { width: 100%; height: 46px; background: #4f46e5; color: #fff; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; transition: background 0.2s; margin-top: 10px; }
.btn:hover { background: #4338ca; }
.btn:disabled { background: #a5a6f6; cursor: not-allowed; }
.links { text-align: center; margin-top: 16px; font-size: 13px; }
.links a { color: #4f46e5; text-decoration: none; }
.links a:hover { text-decoration: underline; }
.msg { padding: 10px 14px; border-radius: 8px; font-size: 13px; margin-bottom: 16px; display: none; }
.msg.error { background: #fef2f2; color: #dc2626; border: 1px solid #fca5a5; display: block; }
.msg.success { background: #f0fdf4; color: #16a34a; border: 1px solid #86efac; display: block; }
.loading { display: none; text-align: center; margin-top: 10px; font-size: 13px; color: #888; }
.tip { font-size: 12px; color: #888; margin-top: 4px; }
</style>
</head>
<body>
<div class="container">
<div class="logo">
<h1>创建账号</h1>
<p>3分钟完成注册</p>
</div>
<div id="msg" class="msg"></div>
<form id="regForm">
<div class="form-group">
<label for="phone">手机号</label>
<input type="tel" id="phone" placeholder="请输入手机号" maxlength="11" required autocomplete="tel">
</div>
<div class="form-group">
<label for="captchaCode">图形验证码</label>
<div class="captcha-row">
<input type="text" id="captchaCode" placeholder="请输入图形验证码" maxlength="4" required style="flex:1">
<canvas id="captchaCanvas" class="captcha-canvas" width="100" height="44" title="点击刷新"></canvas>
</div>
<p class="tip">点击图片刷新验证码</p>
</div>
<div class="form-group">
<label for="smsCode">短信验证码</label>
<div class="sms-row">
<input type="text" id="smsCode" placeholder="请输入短信验证码" maxlength="6" required>
<button type="button" class="btn-sms" id="sendBtn">发送验证码</button>
</div>
</div>
<div class="form-group">
<label for="password">设置密码</label>
<input type="password" id="password" placeholder="6位及以上密码" minlength="6" required>
<p class="tip">密码至少6位建议混合字母和数字</p>
</div>
<button type="submit" class="btn" id="submitBtn">注册</button>
</form>
<div class="links">
<a href="login.html">已有账号?立即登录</a>
</div>
<div id="loading" class="loading">注册中...</div>
</div>
<script>
const API = 'http://localhost:3000/api/auth';
let captchaKey = '';
let smsCountdown = 0;
let smsTimer = null;
const form = document.getElementById('regForm');
const msg = document.getElementById('msg');
const sendBtn = document.getElementById('sendBtn');
const submitBtn = document.getElementById('submitBtn');
const canvas = document.getElementById('captchaCanvas');
const ctx = canvas.getContext('2d');
// Generate a simple random captcha
function generateCaptcha() {
captchaKey = Math.random().toString(36).slice(2, 10);
const code = Math.random().toString(36).slice(2, 6).toUpperCase();
localStorage.setItem('captcha_' + captchaKey, code);
drawCaptcha(code);
}
function drawCaptcha(code) {
ctx.clearRect(0, 0, 100, 44);
ctx.fillStyle = '#f9fafb';
ctx.fillRect(0, 0, 100, 44);
ctx.font = 'bold 22px monospace';
ctx.fillStyle = '#1a1a2e';
// slight rotation effect
ctx.save();
ctx.translate(10, 30);
ctx.rotate(-0.1);
ctx.fillText(code.slice(0, 2), 0, 0);
ctx.restore();
ctx.save();
ctx.translate(55, 30);
ctx.rotate(0.1);
ctx.fillText(code.slice(2), 0, 0);
ctx.restore();
//干扰线
ctx.strokeStyle = '#ddd';
for (let i = 0; i < 3; i++) {
ctx.beginPath();
ctx.moveTo(Math.random() * 100, Math.random() * 44);
ctx.lineTo(Math.random() * 100, Math.random() * 44);
ctx.stroke();
}
}
canvas.addEventListener('click', generateCaptcha);
generateCaptcha();
function showMsg(text, type) {
msg.textContent = text;
msg.className = 'msg ' + type;
msg.style.display = 'block';
}
function hideMsg() { msg.style.display = 'none'; }
sendBtn.addEventListener('click', async () => {
if (smsCountdown > 0) return;
const phone = document.getElementById('phone').value.trim();
const captchaInput = document.getElementById('captchaCode').value.trim().toUpperCase();
if (!/^1[3-9]\d{9}$/.test(phone)) { showMsg('请输入正确的手机号', 'error'); return; }
if (captchaInput.length !== 4) { showMsg('请输入4位图形验证码', 'error'); return; }
const stored = localStorage.getItem('captcha_' + captchaKey);
if (!stored || stored.toUpperCase() !== captchaInput) {
showMsg('图形验证码错误', 'error');
generateCaptcha();
return;
}
sendBtn.disabled = true;
sendBtn.textContent = '发送中...';
try {
const res = await fetch(API + '/send-code', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone, captchaKey, captchaCode: captchaInput })
});
const data = await res.json();
if (data.code === 0) {
showMsg('验证码已发送,请注意查收', 'success');
smsCountdown = 60;
smsTimer = setInterval(() => {
smsCountdown--;
if (smsCountdown <= 0) {
clearInterval(smsTimer);
sendBtn.textContent = '发送验证码';
sendBtn.disabled = false;
} else {
sendBtn.textContent = smsCountdown + '秒后重试';
}
}, 1000);
} else {
showMsg(data.message || '发送失败', 'error');
sendBtn.textContent = '发送验证码';
sendBtn.disabled = false;
}
} catch {
showMsg('网络错误,请检查服务器', 'error');
sendBtn.textContent = '发送验证码';
sendBtn.disabled = false;
}
});
form.addEventListener('submit', async (e) => {
e.preventDefault();
hideMsg();
const phone = document.getElementById('phone').value.trim();
const smsCode = document.getElementById('smsCode').value.trim();
const password = document.getElementById('password').value;
if (!/^1[3-9]\d{9}$/.test(phone)) { showMsg('请输入正确的手机号', 'error'); return; }
if (!/^\d{4,6}$/.test(smsCode)) { showMsg('请输入4-6位短信验证码', 'error'); return; }
if (password.length < 6) { showMsg('密码至少6位', 'error'); return; }
submitBtn.disabled = true;
submitBtn.textContent = '注册中...';
try {
const res = await fetch(API + '/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone, code: smsCode, password })
});
const data = await res.json();
if (data.code === 0) {
localStorage.setItem('auth_token', data.token);
localStorage.setItem('auth_user', JSON.stringify(data.user));
showMsg('注册成功!正在跳转...', 'success');
setTimeout(() => { window.location.href = 'devices.html'; }, 800);
} else {
showMsg(data.message || '注册失败', 'error');
}
} catch {
showMsg('网络错误,请检查服务器是否启动', 'error');
} finally {
submitBtn.disabled = false;
submitBtn.textContent = '注册';
}
});
</script>
</body>
</html>