study_platform/templates/auth/register.html
superlishunqin e62a101da0 0422-1010
2025-04-22 22:10:16 +08:00

480 lines
22 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ _('register') }} - 高可用学习平台{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/login.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/register.css') }}">
{% endblock %}
{% block full_content %}
<div class="overlay"></div>
<div class="theme-toggle" id="theme-toggle">
{% if session.get('theme', 'light') == 'light' %}☀️{% else %}🌙{% endif %}
</div>
<div class="language-selector">
<select id="language-select">
<option value="zh" {% if session.get('language', 'zh') == 'zh' %}selected{% endif %}>简体中文</option>
<option value="en" {% if session.get('language', 'zh') == 'en' %}selected{% endif %}>English</option>
</select>
</div>
<div class="main-container">
<div class="login-container">
<div class="logo">
<img src="{{ url_for('static', filename='images/logo.png') }}" alt="Logo" onerror="this.src='/api/placeholder/90/90'">
</div>
<h1>高可用学习平台</h1>
<p class="subtitle">{{ _('create_account') }}</p>
<div class="step-container">
<div class="step {% if not email_verified %}active{% else %}completed{% endif %}" id="step-1">1
<div class="step-label">{{ _('email_verification') }}</div>
</div>
<div class="step {% if email_verified %}active{% endif %}" id="step-2">2
<div class="step-label">{{ _('account_info') }}</div>
</div>
<div class="step" id="step-3">3
<div class="step-label">{{ _('registration_success') }}</div>
</div>
</div>
<div class="registration-step {% if not email_verified %}active{% endif %}" id="step-1-content">
<form id="email-verification-form" method="post" action="{{ url_for('auth.register') }}">
{{ email_form.hidden_tag() }}
<input type="hidden" name="send_code" value="1">
<div class="form-group">
<label for="email">{{ _('email') }}</label>
<div class="verification-container">
<div class="input-with-icon verification-input">
<span class="input-icon">📧</span>
{{ email_form.email(class="form-control", placeholder=_('email_placeholder'), id="verification-email") }}
</div>
<button type="submit" class="send-code-btn" id="send-code-btn">
{{ _('send_verification_code') }}
</button>
</div>
{% for error in email_form.email.errors %}
<div class="validation-message show">{{ error }}</div>
{% endfor %}
<div class="validation-message" id="email-error"></div>
</div>
<div class="form-buttons">
<a href="{{ url_for('auth.login') }}" class="btn btn-login btn-back">{{ _('back_to_login') }}</a>
</div>
</form>
</div>
<div class="registration-step {% if email_verified %}active{% endif %}" id="step-2-content">
<form id="registration-form" method="post" action="{{ url_for('auth.register') }}">
{{ registration_form.hidden_tag() }}
<input type="hidden" name="register" value="1">
<div class="form-group">
<label for="email">{{ _('email') }}</label>
<div class="input-with-icon">
<span class="input-icon">📧</span>
{{ registration_form.email(class="form-control", readonly="readonly", value=verified_email) }}
</div>
</div>
<div class="form-group">
<label for="verification_code">{{ _('verification_code') }}</label>
<div class="input-with-icon">
<span class="input-icon">🔑</span>
{{ registration_form.verification_code(class="form-control", placeholder=_('enter_verification_code')) }}
</div>
{% for error in registration_form.verification_code.errors %}
<div class="validation-message show">{{ error }}</div>
{% endfor %}
<div class="validation-message" id="code-error"></div>
<div class="countdown" id="resend-countdown"></div>
<button type="button" class="btn-link" id="resend-btn" style="display: none; background: none; border: none; color: var(--primary-color); padding: 0; margin-top: 5px; cursor: pointer; text-decoration: underline;">{{ _('resend_code') }}</button>
</div>
<div class="form-group">
<label for="username">{{ _('username') }}</label>
<div class="input-with-icon">
<span class="input-icon">👤</span>
{{ registration_form.username(class="form-control", placeholder=_('enter_username')) }}
</div>
{% for error in registration_form.username.errors %}
<div class="validation-message show">{{ error }}</div>
{% endfor %}
<div class="validation-message" id="username-error"></div>
</div>
<div class="form-group">
<label for="password">{{ _('password') }}</label>
<div class="input-with-icon">
<span class="input-icon">🔒</span>
{{ registration_form.password(class="form-control", placeholder=_('enter_password'), id="register-password") }}
<span class="password-toggle" id="password-toggle">👁️</span>
</div>
{% for error in registration_form.password.errors %}
<div class="validation-message show">{{ error }}</div>
{% endfor %}
<div class="validation-message" id="password-error"></div>
<div class="strength-meter">
<div class="strength-meter-fill" id="strength-meter-fill"></div>
</div>
<div class="password-strength-text" id="password-strength-text"></div>
</div>
<div class="form-group">
<label for="password2">{{ _('repeat_password') }}</label>
<div class="input-with-icon">
<span class="input-icon">🔒</span>
{{ registration_form.password2(class="form-control", placeholder=_('repeat_password_placeholder')) }}
</div>
{% for error in registration_form.password2.errors %}
<div class="validation-message show">{{ error }}</div>
{% endfor %}
<div class="validation-message" id="password2-error"></div>
</div>
<div class="form-buttons">
<button type="button" class="btn btn-login btn-back" id="back-to-step1">{{ _('back') }}</button>
<button type="submit" class="btn btn-login btn-next">{{ _('register') }}</button>
</div>
</form>
</div>
</div>
</div>
<footer>
<p>© 2023 高可用学习平台 | <a href="#">{{ _('privacy_policy') }}</a> | <a href="#">{{ _('terms_of_service') }}</a></p>
</footer>
{% endblock %}
{% block extra_js %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// 主题切换
const themeToggle = document.getElementById('theme-toggle');
themeToggle.addEventListener('click', function() {
const currentTheme = "{{ session.get('theme', 'light') }}";
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
window.location.href = "{{ url_for('main.set_theme', theme='') }}" + newTheme + "?next={{ request.path|urlencode }}";
});
// 语言选择器
const languageSelect = document.getElementById('language-select');
languageSelect.addEventListener('change', function() {
window.location.href = "{{ url_for('main.set_language', lang='') }}" + this.value + "?next={{ request.path|urlencode }}";
});
// 密码可见性切换
const passwordInput = document.getElementById('register-password');
const passwordToggle = document.getElementById('password-toggle');
if (passwordToggle && passwordInput) {
passwordToggle.addEventListener('click', function() {
const type = passwordInput.getAttribute('type') === 'password' ? 'text' : 'password';
passwordInput.setAttribute('type', type);
passwordToggle.innerHTML = type === 'password' ? '👁️' : '👁️‍🗨️';
});
}
// 密码强度检测
if (passwordInput) {
const strengthMeter = document.getElementById('strength-meter-fill');
const strengthText = document.getElementById('password-strength-text');
passwordInput.addEventListener('input', function() {
const password = this.value;
const strength = checkPasswordStrength(password);
// 更新强度指示器
strengthMeter.className = 'strength-meter-fill';
if (password.length === 0) {
strengthMeter.style.width = '0';
strengthText.textContent = '';
} else if (strength < 2) {
strengthMeter.classList.add('weak');
strengthText.textContent = '{{ _("password_weak") }}';
} else if (strength < 3) {
strengthMeter.classList.add('medium');
strengthText.textContent = '{{ _("password_medium") }}';
} else if (strength < 4) {
strengthMeter.classList.add('strong');
strengthText.textContent = '{{ _("password_strong") }}';
} else {
strengthMeter.classList.add('very-strong');
strengthText.textContent = '{{ _("password_very_strong") }}';
}
});
}
// 检查密码强度的函数
function checkPasswordStrength(password) {
let strength = 0;
if (password.length >= 8) strength++;
if (/[a-z]/.test(password)) strength++;
if (/[A-Z]/.test(password)) strength++;
if (/[0-9]/.test(password)) strength++;
if (/[^a-zA-Z0-9]/.test(password)) strength++;
return strength;
}
// 注册表单验证
const registrationForm = document.getElementById('registration-form');
const verificationCodeInput = document.getElementById('verification_code');
const usernameInput = document.getElementById('username');
const password2Input = document.getElementById('password2');
const codeError = document.getElementById('code-error');
const usernameError = document.getElementById('username-error');
const password2Error = document.getElementById('password2-error');
if (registrationForm) {
registrationForm.addEventListener('submit', function(e) {
let isValid = true;
// 验证码验证
if (verificationCodeInput.value.trim() === '') {
codeError.textContent = '{{ _("verification_code_required") }}';
codeError.classList.add('show');
isValid = false;
} else if (verificationCodeInput.value.length !== 6) {
codeError.textContent = '{{ _("verification_code_invalid") }}';
codeError.classList.add('show');
isValid = false;
} else {
codeError.classList.remove('show');
}
// 用户名验证
if (usernameInput.value.trim() === '') {
usernameError.textContent = '{{ _("username_required") }}';
usernameError.classList.add('show');
isValid = false;
} else {
usernameError.classList.remove('show');
}
// 密码匹配验证
if (password2Input.value !== passwordInput.value) {
password2Error.textContent = '{{ _("passwords_not_match") }}';
password2Error.classList.add('show');
isValid = false;
} else {
password2Error.classList.remove('show');
}
if (!isValid) {
e.preventDefault();
}
});
}
// 邮箱验证表单
const emailVerificationForm = document.getElementById('email-verification-form');
const emailInput = document.getElementById('verification-email');
const emailError = document.getElementById('email-error');
const sendCodeBtn = document.getElementById('send-code-btn');
if (emailVerificationForm) {
emailVerificationForm.addEventListener('submit', function(e) {
if (emailInput.value.trim() === '') {
e.preventDefault();
emailError.textContent = '{{ _("email_required") }}';
emailError.classList.add('show');
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailInput.value.trim())) {
e.preventDefault();
emailError.textContent = '{{ _("invalid_email") }}';
emailError.classList.add('show');
} else {
emailError.classList.remove('show');
}
});
// 动态发送验证码
sendCodeBtn.addEventListener('click', function(e) {
if (emailInput.value.trim() === '' || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailInput.value.trim())) {
return; // 让表单自己处理验证
}
e.preventDefault();
// 禁用按钮
sendCodeBtn.disabled = true;
sendCodeBtn.textContent = '{{ _("sending") }}...';
// 发送Ajax请求
const xhr = new XMLHttpRequest();
xhr.open('POST', '{{ url_for("auth.send_verification_code") }}', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
try {
const response = JSON.parse(xhr.responseText);
if (response.success) {
// 显示成功消息
showNotification(response.message, 'success');
// 存储邮箱地址
const verifiedEmail = emailInput.value.trim();
// 显示下一步
document.getElementById('step-1').classList.remove('active');
document.getElementById('step-1').classList.add('completed');
document.getElementById('step-2').classList.add('active');
document.getElementById('step-1-content').classList.remove('active');
document.getElementById('step-2-content').classList.add('active');
// 设置邮箱字段
document.getElementById('email').value = verifiedEmail;
// 启动倒计时
startResendCountdown();
} else {
// 显示错误消息
showNotification(response.message, 'error');
sendCodeBtn.disabled = false;
sendCodeBtn.textContent = '{{ _("send_verification_code") }}';
}
} catch (e) {
showNotification('{{ _("server_error") }}', 'error');
sendCodeBtn.disabled = false;
sendCodeBtn.textContent = '{{ _("send_verification_code") }}';
}
} else {
showNotification('{{ _("server_error") }}', 'error');
sendCodeBtn.disabled = false;
sendCodeBtn.textContent = '{{ _("send_verification_code") }}';
}
}
};
xhr.send('email=' + encodeURIComponent(emailInput.value.trim()));
});
}
// 返回按钮
const backToStep1 = document.getElementById('back-to-step1');
if (backToStep1) {
backToStep1.addEventListener('click', function() {
document.getElementById('step-1').classList.add('active');
document.getElementById('step-1').classList.remove('completed');
document.getElementById('step-2').classList.remove('active');
document.getElementById('step-1-content').classList.add('active');
document.getElementById('step-2-content').classList.remove('active');
});
}
// 重发验证码
const resendBtn = document.getElementById('resend-btn');
const resendCountdown = document.getElementById('resend-countdown');
if (resendBtn) {
resendBtn.addEventListener('click', function() {
// 禁用按钮
resendBtn.style.display = 'none';
// 发送Ajax请求
const xhr = new XMLHttpRequest();
xhr.open('POST', '{{ url_for("auth.send_verification_code") }}', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
const response = JSON.parse(xhr.responseText);
if (response.success) {
showNotification(response.message, 'success');
startResendCountdown();
} else {
showNotification(response.message, 'error');
resendBtn.style.display = 'block';
}
} catch (e) {
showNotification('{{ _("server_error") }}', 'error');
resendBtn.style.display = 'block';
}
} else if (xhr.readyState === 4) {
showNotification('{{ _("server_error") }}', 'error');
resendBtn.style.display = 'block';
}
};
xhr.send('email=' + encodeURIComponent(document.getElementById('email').value));
});
}
// 验证码倒计时
function startResendCountdown() {
let seconds = 60;
resendCountdown.textContent = `{{ _("resend_code_in") }} ${seconds} {{ _("seconds") }}`;
resendCountdown.style.display = 'block';
resendBtn.style.display = 'none';
const interval = setInterval(function() {
seconds--;
resendCountdown.textContent = `{{ _("resend_code_in") }} ${seconds} {{ _("seconds") }}`;
if (seconds <= 0) {
clearInterval(interval);
resendCountdown.style.display = 'none';
resendBtn.style.display = 'block';
}
}, 1000);
}
// 显示通知
function showNotification(message, type) {
// 检查是否已有通知
let notification = document.querySelector('.notification');
if (notification) {
document.body.removeChild(notification);
}
// 创建新通知
notification = document.createElement('div');
notification.className = `notification ${type}`;
const icon = type === 'success' ? '✓' : '✗';
notification.innerHTML = `
<div class="notification-icon">${icon}</div>
<div class="notification-content">
<p>${message}</p>
</div>
`;
document.body.appendChild(notification);
// 显示通知
setTimeout(() => {
notification.classList.add('show');
}, 10);
// 自动隐藏
setTimeout(() => {
notification.classList.remove('show');
setTimeout(() => {
try {
document.body.removeChild(notification);
} catch (e) {
// 忽略可能的错误
}
}, 300);
}, 3000);
}
// 如果是第二步,启动倒计时
{% if email_verified %}
startResendCountdown();
{% endif %}
});
</script>
{% endblock %}