fix: polish authentication and responsive controls

This commit is contained in:
2026-08-05 11:43:43 +08:00
parent f6e36d737d
commit 1245a8ec1e
9 changed files with 102 additions and 29 deletions
+7 -3
View File
@@ -6,15 +6,17 @@
<span class="font-bold text-textMain">{{ pagination.page }}</span> / {{ pagination.pages }} 页
</div>
{% if pagination.pages > 1 %}
<div class="flex flex-nowrap items-center gap-2 overflow-x-auto pb-1">
<div class="flex w-full min-w-0 flex-nowrap items-center gap-2 overflow-x-auto pb-1 sm:w-auto">
{% set prev_page = pagination.prev_num if pagination.has_prev else pagination.page %}
<a
class="ui-btn ui-btn-sm ui-btn-secondary {{ 'pointer-events-none opacity-50' if not pagination.has_prev }}"
href="{{ url_for(endpoint, page=prev_page) }}"
aria-disabled="{{ 'false' if pagination.has_prev else 'true' }}"
aria-label="上一页"
title="上一页"
>
<span class="material-symbols-outlined text-lg">chevron_left</span>
上一页
<span class="hidden sm:inline">上一页</span>
</a>
<div class="flex items-center gap-1">
{% for page in pagination.iter_pages(left_edge=1, left_current=1, right_current=2, right_edge=1) %}
@@ -34,8 +36,10 @@
class="ui-btn ui-btn-sm ui-btn-secondary {{ 'pointer-events-none opacity-50' if not pagination.has_next }}"
href="{{ url_for(endpoint, page=next_page) }}"
aria-disabled="{{ 'false' if pagination.has_next else 'true' }}"
aria-label="下一页"
title="下一页"
>
下一页
<span class="hidden sm:inline">下一页</span>
<span class="material-symbols-outlined text-lg">chevron_right</span>
</a>
</div>
+1 -1
View File
@@ -1,4 +1,4 @@
{% extends "base.html" %}
{% extends "auth_base.html" %}
{% block title %}接受注册邀请{% endblock %}
{% block content %}
<section class="mx-auto max-w-md rounded-xl border border-line bg-white p-5 shadow-panel sm:p-6">
+23 -21
View File
@@ -10,28 +10,30 @@
<p class="mt-2 text-sm text-textSub">管理用户账号、注册状态和预测记录。</p>
</div>
<section class="mt-6 rounded-xl border border-line bg-white p-5 shadow-panel">
<h2 class="font-extrabold">用户注册</h2>
<form method="post" action="{{ url_for('main.update_registration_setting') }}" class="mt-3 flex items-center gap-3">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<label class="text-sm">
<input type="checkbox" name="allow_registration" {{ 'checked' if registration_allowed }}>
允许自助注册
</label>
<button class="ui-btn ui-btn-sm ui-btn-primary">保存</button>
</form>
</section>
<div class="mt-6 grid gap-6 xl:grid-cols-2">
<section class="rounded-xl border border-line bg-white p-5 shadow-panel">
<h2 class="font-extrabold">用户注册</h2>
<form method="post" action="{{ url_for('main.update_registration_setting') }}" class="mt-3 flex items-center gap-3">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<label class="text-sm">
<input type="checkbox" name="allow_registration" {{ 'checked' if registration_allowed }}>
允许自助注册
</label>
<button class="ui-btn ui-btn-sm ui-btn-primary">保存</button>
</form>
</section>
<section class="mt-6 rounded-xl border border-line bg-white p-5 shadow-panel">
<h2 class="font-extrabold">邀请注册</h2>
<p class="mt-1 text-sm text-textSub">即使关闭自助注册,也可向指定邮箱发送一次性注册链接。</p>
<form method="post" action="{{ url_for('main.admin_send_registration_invitation') }}" class="mt-4 flex flex-col gap-2 sm:flex-row" novalidate>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<label class="sr-only" for="inviteEmail">受邀邮箱</label>
<input id="inviteEmail" name="email" type="email" required autocomplete="email" class="min-w-0 flex-1 rounded-lg border border-line px-3 py-2 text-sm" placeholder="受邀人的邮箱地址">
<button class="ui-btn ui-btn-sm ui-btn-primary shrink-0">发送注册链接</button>
</form>
</section>
<section class="rounded-xl border border-line bg-white p-5 shadow-panel">
<h2 class="font-extrabold">邀请注册</h2>
<p class="mt-1 text-sm text-textSub">即使关闭自助注册,也可向指定邮箱发送一次性注册链接。</p>
<form method="post" action="{{ url_for('main.admin_send_registration_invitation') }}" class="mt-4 flex flex-col gap-2 sm:flex-row sm:items-center" novalidate>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<label class="sr-only" for="inviteEmail">受邀邮箱</label>
<input id="inviteEmail" name="email" type="email" required autocomplete="email" class="min-w-0 w-full rounded-lg border border-line px-3 py-2 text-sm sm:w-80" placeholder="受邀人的邮箱地址">
<button class="ui-btn ui-btn-sm ui-btn-primary shrink-0">发送注册链接</button>
</form>
</section>
</div>
<section class="mt-6 rounded-xl border border-line bg-white p-5 shadow-panel">
<div class="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
+16
View File
@@ -0,0 +1,16 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}账户验证{% endblock %} | 供水管道健康评估系统</title>
<meta name="theme-color" content="#005EB8">
<link rel="icon" href="{{ url_for('static', filename='favicon.svg') }}" type="image/svg+xml">
<link href="{{ url_for('static', filename='css/app.css') }}" rel="stylesheet">
</head>
<body class="min-h-screen bg-page text-textMain">
<main class="mx-auto flex min-h-screen max-w-md items-center px-4 py-5 sm:px-5">
{% block content %}{% endblock %}
</main>
</body>
</html>
+20
View File
@@ -143,6 +143,16 @@
</button>
</div>
</div>
<div>
<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">lock</span>
<input id="registerPasswordConfirm" name="password_confirm" type="password" minlength="12" required data-field-label="确认密码" autocomplete="new-password" {{ 'disabled' if not allow_registration }} class="w-full pl-11 pr-12 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="请再次输入密码" />
<button class="password-toggle" type="button" data-password-toggle="registerPasswordConfirm" aria-label="显示确认密码" aria-pressed="false" tabindex="-1" {{ 'disabled' if not allow_registration }}>
<span class="material-symbols-outlined" aria-hidden="true">visibility</span>
</button>
</div>
</div>
<div>
<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">
@@ -327,6 +337,16 @@
showAppNotification(`密码至少需要 ${minLength}`, 'error', undefined, shortPassword);
markFieldError(shortPassword);
shortPassword.focus();
return;
}
const password = form.querySelector('[name="password"]');
const confirmation = form.querySelector('[name="password_confirm"]');
if (password && confirmation && password.value !== confirmation.value) {
event.preventDefault();
showAppNotification('两次密码输入不一致', 'error', undefined, confirmation);
markFieldError(confirmation);
confirmation.focus();
}
});
});
+1 -1
View File
@@ -1,4 +1,4 @@
{% extends "base.html" %}
{% extends "auth_base.html" %}
{% block title %}重置密码{% endblock %}
{% block content %}
<section class="mx-auto max-w-md rounded-xl border border-line bg-white p-5 shadow-panel sm:p-6">