Files
zjpb.net/templates/auth/register.html
Jowe 2067fb1712 feat: v3.0 - 用户系统和收藏功能
核心功能:
- 用户注册/登录系统(用户名+密码)
- 工具收藏功能(一键收藏/取消收藏)
- 收藏分组管理(文件夹)
- 用户中心(个人资料、收藏列表)

数据库变更:
- 新增 users 表(用户信息)
- 新增 folders 表(收藏分组)
- 新增 collections 表(收藏记录)

安全增强:
- Admin 和 User 完全隔离
- 修复14个管理员路由的权限漏洞
- 所有管理功能添加用户类型检查

新增文件:
- templates/auth/register.html - 注册页面
- templates/auth/login.html - 登录页面
- templates/user/profile.html - 用户中心
- templates/user/collections.html - 收藏列表
- create_user_tables.py - 数据库迁移脚本
- USER_SYSTEM_README.md - 用户系统文档
- CHANGELOG_v3.0.md - 版本更新日志

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 19:19:05 +08:00

193 lines
11 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>用户注册 - ZJPB - 自己品吧</title>
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Noto+Sans:wght@400;500;700&display=swap" rel="stylesheet">
<!-- Material Symbols -->
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet">
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
"primary": "#0ea5e9",
"primary-dark": "#0284c7",
"background": "#f8fafc",
"surface": "#ffffff",
"input-bg": "#ffffff",
"input-border": "#e2e8f0",
"text-main": "#0f172a",
"text-secondary": "#334155",
"text-muted": "#64748b",
},
fontFamily: {
"display": ["Space Grotesk", "sans-serif"],
"body": ["Noto Sans", "sans-serif"],
},
},
},
}
</script>
<style>
.glass-panel {
background: rgba(255, 255, 255, 0.75);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
}
.tech-bg-grid {
background-image: radial-gradient(circle at center, rgba(14, 165, 233, 0.04) 0%, transparent 60%),
linear-gradient(rgba(14, 165, 233, 0.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(14, 165, 233, 0.05) 1px, transparent 1px);
background-size: 100% 100%, 40px 40px, 40px 40px;
}
</style>
</head>
<body class="bg-[#f8fafc] font-display text-[#0f172a] min-h-screen flex flex-col items-center justify-center relative overflow-hidden selection:bg-sky-500/20 selection:text-sky-700">
<!-- Ambient Background -->
<div class="absolute inset-0 z-0 tech-bg-grid pointer-events-none"></div>
<div class="absolute top-[-20%] right-[-10%] w-[600px] h-[600px] bg-sky-200/40 rounded-full blur-[120px] pointer-events-none mix-blend-multiply"></div>
<div class="absolute bottom-[-10%] left-[-10%] w-[500px] h-[500px] bg-indigo-100/60 rounded-full blur-[100px] pointer-events-none mix-blend-multiply"></div>
<!-- Main Content Wrapper -->
<div class="w-full max-w-[480px] px-4 z-10 flex flex-col gap-6">
<!-- Back Navigation -->
<a class="group flex items-center gap-2 text-[#64748b] hover:text-[#0f172a] transition-colors w-fit" href="{{ url_for('index') }}">
<div class="flex items-center justify-center w-8 h-8 rounded-full border border-[#e2e8f0] bg-white group-hover:border-sky-500/50 group-hover:bg-sky-500/5 transition-all shadow-sm">
<span class="material-symbols-outlined text-sm">arrow_back</span>
</div>
<span class="text-sm font-medium">返回首页</span>
</a>
<!-- Register Card -->
<div class="glass-panel border border-white rounded-2xl shadow-[0_4px_30px_rgba(0,0,0,0.03)] p-8 md:p-12 relative overflow-hidden ring-1 ring-black/5">
<!-- Decorative Accent -->
<div class="absolute top-0 left-0 w-full h-[2px] bg-gradient-to-r from-transparent via-sky-500/50 to-transparent"></div>
<!-- Header -->
<div class="flex flex-col gap-2 mb-8">
<div class="flex items-center gap-3 mb-2">
<div class="p-2 rounded-lg bg-sky-500/10 text-sky-500">
<span class="material-symbols-outlined text-2xl">person_add</span>
</div>
<span class="text-xs font-bold tracking-widest uppercase text-sky-500">Create Account</span>
</div>
<h1 class="text-3xl font-black tracking-tight text-[#0f172a] leading-tight">用户注册</h1>
<p class="text-[#64748b] text-base font-normal">创建您的账号开始收藏喜欢的AI工具</p>
</div>
<!-- Error Messages -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="mb-6 p-3 rounded-lg bg-red-50 border border-red-200 flex items-start gap-3">
<span class="material-symbols-outlined text-red-500 text-sm mt-0.5">error</span>
<p class="text-red-600 text-sm">{{ message }}</p>
</div>
{% endfor %}
{% endif %}
{% endwith %}
<!-- Form -->
<form method="POST" action="{{ url_for('user_register') }}" class="flex flex-col gap-5">
<!-- Username Field -->
<div class="space-y-2">
<label class="text-[#334155] text-sm font-semibold leading-normal" for="username">用户名</label>
<div class="relative group">
<input class="form-input flex w-full h-12 pl-11 pr-4 rounded-lg text-[#0f172a] focus:outline-0 focus:ring-2 focus:ring-sky-500/20 border border-[#e2e8f0] bg-white focus:border-sky-500 placeholder:text-slate-400 text-base font-normal leading-normal transition-all shadow-sm"
id="username"
name="username"
placeholder="请输入用户名3个字符以上"
type="text"
required
autofocus/>
<div class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 group-focus-within:text-sky-500 transition-colors flex items-center justify-center pointer-events-none">
<span class="material-symbols-outlined text-[20px]">person</span>
</div>
</div>
</div>
<!-- Password Field -->
<div class="space-y-2">
<label class="text-[#334155] text-sm font-semibold leading-normal" for="password">密码</label>
<div class="flex w-full items-stretch rounded-lg group relative">
<input class="form-input flex w-full h-12 pl-11 pr-11 rounded-lg text-[#0f172a] focus:outline-0 focus:ring-2 focus:ring-sky-500/20 border border-[#e2e8f0] bg-white focus:border-sky-500 placeholder:text-slate-400 text-base font-normal leading-normal transition-all shadow-sm z-10"
id="password"
name="password"
placeholder="请输入密码6个字符以上"
type="password"
required/>
<div class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 group-focus-within:text-sky-500 transition-colors flex items-center justify-center pointer-events-none z-20">
<span class="material-symbols-outlined text-[20px]">lock</span>
</div>
<button class="absolute right-0 top-0 h-full px-3 text-slate-400 hover:text-[#334155] transition-colors flex items-center justify-center z-20 focus:outline-none"
type="button"
onclick="togglePassword('password')">
<span class="material-symbols-outlined text-[20px]" id="toggleIcon1">visibility_off</span>
</button>
</div>
</div>
<!-- Confirm Password Field -->
<div class="space-y-2">
<label class="text-[#334155] text-sm font-semibold leading-normal" for="confirm_password">确认密码</label>
<div class="flex w-full items-stretch rounded-lg group relative">
<input class="form-input flex w-full h-12 pl-11 pr-11 rounded-lg text-[#0f172a] focus:outline-0 focus:ring-2 focus:ring-sky-500/20 border border-[#e2e8f0] bg-white focus:border-sky-500 placeholder:text-slate-400 text-base font-normal leading-normal transition-all shadow-sm z-10"
id="confirm_password"
name="confirm_password"
placeholder="请再次输入密码"
type="password"
required/>
<div class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 group-focus-within:text-sky-500 transition-colors flex items-center justify-center pointer-events-none z-20">
<span class="material-symbols-outlined text-[20px]">lock</span>
</div>
<button class="absolute right-0 top-0 h-full px-3 text-slate-400 hover:text-[#334155] transition-colors flex items-center justify-center z-20 focus:outline-none"
type="button"
onclick="togglePassword('confirm_password')">
<span class="material-symbols-outlined text-[20px]" id="toggleIcon2">visibility_off</span>
</button>
</div>
</div>
<!-- Register Button -->
<button class="mt-4 flex w-full h-12 cursor-pointer items-center justify-center overflow-hidden rounded-lg bg-sky-500 text-white text-base font-bold leading-normal tracking-wide hover:bg-sky-600 transition-all active:scale-[0.98] shadow-lg shadow-sky-500/25 hover:shadow-sky-500/40"
type="submit">
<span class="truncate">注册</span>
</button>
</form>
<!-- Footer Meta -->
<div class="mt-8 flex flex-col items-center gap-4 border-t border-slate-100 pt-6">
<p class="text-[#64748b] text-sm text-center">
已有账号?<a href="{{ url_for('user_login') }}" class="text-sky-500 hover:text-sky-600 font-semibold">立即登录</a>
</p>
</div>
</div>
</div>
<script>
function togglePassword(fieldId) {
const passwordInput = document.getElementById(fieldId);
const toggleIcon = document.getElementById(fieldId === 'password' ? 'toggleIcon1' : 'toggleIcon2');
if (passwordInput.type === 'password') {
passwordInput.type = 'text';
toggleIcon.textContent = 'visibility';
} else {
passwordInput.type = 'password';
toggleIcon.textContent = 'visibility_off';
}
}
</script>
</body>
</html>