feat: refine email verification login flow

This commit is contained in:
2026-08-04 18:24:05 +08:00
parent 732c1df031
commit 87c792db44
7 changed files with 323 additions and 80 deletions
+17 -18
View File
@@ -14,6 +14,7 @@
<script>
window.__flashMessages = {{ flashed_messages|tojson }};
window.__pageNotice = {{ page_notice|tojson }};
window.__authErrorField = {{ auth_error_field|tojson }};
</script>
<div class="sr-only" aria-hidden="true">
{% for category, message in flashed_messages %}{{ message }}{% endfor %}
@@ -70,10 +71,10 @@
<form method="post" action="{{ url_for('main.login') }}" class="space-y-5" novalidate data-auth-form>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div>
<label class="block text-[11px] tracking-[0.18em] uppercase text-slate-500 mb-2">邮箱</label>
<label class="block text-[11px] tracking-[0.18em] uppercase text-slate-500 mb-2">用户名或邮箱</label>
<div class="relative">
<span class="material-symbols-outlined absolute left-4 top-1/2 -translate-y-1/2 text-slate-400 text-lg">mail</span>
<input name="email" type="email" required data-field-label="邮箱" autocomplete="email" class="w-full pl-11 pr-4 py-3.5 rounded-xl bg-[#eceff3] border border-transparent focus:border-primary focus:ring-0" placeholder="请输入邮箱" />
<span class="material-symbols-outlined absolute left-4 top-1/2 -translate-y-1/2 text-slate-400 text-lg">person</span>
<input name="identifier" required data-field-label="用户名或邮箱" autocomplete="username" class="w-full pl-11 pr-4 py-3.5 rounded-xl bg-[#eceff3] border border-transparent focus:border-primary focus:ring-0" placeholder="请输入用户名或邮箱" />
</div>
</div>
@@ -92,14 +93,14 @@
</div>
<div>
<label class="block text-[11px] tracking-[0.18em] uppercase text-slate-500 mb-2">验证码</label>
<label class="block text-[11px] tracking-[0.18em] uppercase text-slate-500 mb-2">图形验证码</label>
<div class="grid grid-cols-[1fr_92px_44px] gap-3 items-center">
<div class="relative">
<span class="material-symbols-outlined absolute left-4 top-1/2 -translate-y-1/2 text-slate-400 text-lg">verified_user</span>
<input name="captcha" required data-field-label="验证码" class="w-full pl-11 pr-4 py-3.5 rounded-xl bg-[#eceff3] border border-transparent focus:border-primary focus:ring-0" placeholder="请输入验证码" />
<input name="captcha" required data-field-label="图形验证码" class="w-full pl-11 pr-4 py-3.5 rounded-xl bg-[#eceff3] border border-transparent focus:border-primary focus:ring-0" placeholder="请输入图形验证码" />
</div>
<div class="captcha-token rounded-xl bg-blueSoft text-textMain border border-blue-100 h-[50px] flex items-center justify-center font-black tracking-[0.18em] italic" aria-label="验证码" data-captcha-token>{{ captcha }}</div>
<a href="{{ url_for('main.login') }}" tabindex="-1" class="ui-btn ui-btn-field ui-btn-secondary px-0" aria-label="刷新验证码">
<div class="captcha-token rounded-xl bg-blueSoft text-textMain border border-blue-100 h-[50px] flex items-center justify-center font-black tracking-[0.18em] italic" aria-label="图形验证码" data-captcha-token>{{ captcha }}</div>
<a href="{{ url_for('main.login') }}" tabindex="-1" class="ui-btn ui-btn-field ui-btn-secondary px-0" aria-label="刷新图形验证码">
<span class="material-symbols-outlined">refresh</span>
</a>
</div>
@@ -143,14 +144,14 @@
</div>
</div>
<div>
<label class="block text-[11px] tracking-[0.18em] uppercase text-slate-500 mb-2">验证码</label>
<label class="block text-[11px] tracking-[0.18em] uppercase text-slate-500 mb-2">图形验证码</label>
<div class="grid grid-cols-[1fr_92px_44px] gap-3 items-center">
<div class="relative">
<span class="material-symbols-outlined absolute left-4 top-1/2 -translate-y-1/2 text-slate-400 text-lg">verified_user</span>
<input name="captcha" required data-field-label="验证码" {{ 'disabled' if not allow_registration }} class="w-full pl-11 pr-4 py-3.5 rounded-xl bg-[#eceff3] border border-transparent focus:border-primary focus:ring-0 disabled:cursor-not-allowed disabled:bg-slate-100 disabled:text-slate-400" placeholder="请输入验证码" />
<input name="captcha" required data-field-label="图形验证码" {{ 'disabled' if not allow_registration }} class="w-full pl-11 pr-4 py-3.5 rounded-xl bg-[#eceff3] border border-transparent focus:border-primary focus:ring-0 disabled:cursor-not-allowed disabled:bg-slate-100 disabled:text-slate-400" placeholder="请输入图形验证码" />
</div>
<div class="captcha-token rounded-xl bg-blueSoft text-textMain border border-blue-100 h-[50px] flex items-center justify-center font-black tracking-[0.18em] italic" aria-label="验证码" data-captcha-token>{{ captcha }}</div>
<a href="{{ url_for('main.register') }}" tabindex="-1" class="ui-btn ui-btn-field ui-btn-secondary px-0 {{ 'pointer-events-none opacity-50' if not allow_registration }}" aria-label="刷新验证码" aria-disabled="{{ 'true' if not allow_registration else 'false' }}">
<div class="captcha-token rounded-xl bg-blueSoft text-textMain border border-blue-100 h-[50px] flex items-center justify-center font-black tracking-[0.18em] italic" aria-label="图形验证码" data-captcha-token>{{ captcha }}</div>
<a href="{{ url_for('main.register') }}" tabindex="-1" class="ui-btn ui-btn-field ui-btn-secondary px-0 {{ 'pointer-events-none opacity-50' if not allow_registration }}" aria-label="刷新图形验证码" aria-disabled="{{ 'true' if not allow_registration else 'false' }}">
<span class="material-symbols-outlined">refresh</span>
</a>
</div>
@@ -277,21 +278,19 @@
form.querySelectorAll('.auth-input-error').forEach(clearFieldError);
}
function fieldForServerMessage(message) {
function fieldForServerMessage() {
const activeForm = document.querySelector('[data-auth-form]');
if (!activeForm) return null;
if (message.includes('验证码')) return activeForm.querySelector('input[name="captcha"]');
if (message.includes('密码')) return activeForm.querySelector('input[name="password"]');
if (message.includes('邮箱')) return activeForm.querySelector('input[name="email"]');
if (message.includes('用户名') || message.includes('显示名')) return activeForm.querySelector('input[name="username"]');
return null;
return window.__authErrorField
? activeForm.querySelector(`[name="${window.__authErrorField}"]`)
: null;
}
const flashedMessages = window.__flashMessages || [];
const pageNotice = window.__pageNotice;
if (flashedMessages.length) {
const [category, message] = flashedMessages[flashedMessages.length - 1];
const serverField = category === 'error' ? fieldForServerMessage(message) : null;
const serverField = category === 'error' ? fieldForServerMessage() : null;
showAppNotification(message, category === 'error' ? 'error' : 'info', undefined, serverField);
if (category === 'error') {
markFieldError(serverField);