Files
zjpb.net/templates/user/profile.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

246 lines
6.6 KiB
HTML

{% extends 'base_new.html' %}
{% block title %}个人中心 - ZJPB{% endblock %}
{% block extra_css %}
<style>
.profile-container {
display: grid;
grid-template-columns: 280px 1fr;
gap: 32px;
margin-top: 32px;
}
/* 侧边栏 */
.sidebar-card {
background: var(--bg-white);
border: 1px solid var(--border-color);
border-radius: var(--radius-lg);
padding: 24px;
align-self: flex-start;
position: sticky;
top: 88px;
}
.profile-avatar {
width: 80px;
height: 80px;
border-radius: 50%;
margin: 0 auto 16px;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%);
color: white;
font-size: 32px;
font-weight: 700;
}
.profile-username {
text-align: center;
font-size: 20px;
font-weight: 700;
margin-bottom: 8px;
}
.profile-bio {
text-align: center;
font-size: 14px;
color: var(--text-secondary);
margin-bottom: 24px;
}
.nav-menu {
list-style: none;
padding: 0;
margin: 0;
}
.nav-menu a {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
border-radius: var(--radius-md);
color: var(--text-primary);
text-decoration: none;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
}
.nav-menu a:hover {
background: var(--bg-page);
}
.nav-menu a.active {
background: rgba(14, 165, 233, 0.1);
color: var(--primary-blue);
}
/* 主内容区 */
.main-card {
background: var(--bg-white);
border: 1px solid var(--border-color);
border-radius: var(--radius-lg);
padding: 32px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 32px;
}
.stat-card {
padding: 20px;
background: var(--bg-page);
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
text-align: center;
}
.stat-value {
font-size: 32px;
font-weight: 700;
color: var(--primary-blue);
margin-bottom: 8px;
}
.stat-label {
font-size: 14px;
color: var(--text-secondary);
}
.recent-section h2 {
font-size: 20px;
font-weight: 700;
margin-bottom: 20px;
}
.collection-item {
display: flex;
gap: 12px;
padding: 16px;
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
margin-bottom: 12px;
text-decoration: none;
color: var(--text-primary);
transition: all 0.2s;
}
.collection-item:hover {
border-color: var(--primary-blue);
box-shadow: var(--shadow-md);
}
.collection-logo {
width: 48px;
height: 48px;
border-radius: var(--radius-md);
object-fit: cover;
flex-shrink: 0;
}
.collection-info h3 {
font-size: 16px;
font-weight: 600;
margin-bottom: 4px;
}
.collection-info p {
font-size: 13px;
color: var(--text-secondary);
margin: 0;
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: var(--text-muted);
}
.empty-state-icon {
font-size: 64px;
margin-bottom: 16px;
}
@media (max-width: 968px) {
.profile-container {
grid-template-columns: 1fr;
}
.sidebar-card {
position: static;
}
}
</style>
{% endblock %}
{% block content %}
<div class="main-content">
<div class="profile-container">
<!-- 侧边栏 -->
<div class="sidebar-card">
<div class="profile-avatar">
{{ current_user.username[0].upper() }}
</div>
<div class="profile-username">{{ current_user.username }}</div>
<div class="profile-bio">{{ current_user.bio or '这个人很懒,什么都没写' }}</div>
<ul class="nav-menu">
<li><a href="/user/profile" class="active">👤 个人资料</a></li>
<li><a href="/user/collections">⭐ 我的收藏</a></li>
</ul>
</div>
<!-- 主内容区 -->
<div>
<div class="main-card">
<h1 style="font-size: 24px; font-weight: 700; margin-bottom: 24px;">个人中心</h1>
<!-- 统计卡片 -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-value">{{ collections_count }}</div>
<div class="stat-label">收藏数</div>
</div>
<div class="stat-card">
<div class="stat-value">{{ folders_count }}</div>
<div class="stat-label">文件夹数</div>
</div>
</div>
<!-- 最近收藏 -->
<div class="recent-section">
<h2>最近收藏</h2>
{% if recent_collections %}
{% for collection in recent_collections %}
<a href="/site/{{ collection.site.code }}" class="collection-item">
{% if collection.site.logo %}
<img src="{{ collection.site.logo }}" alt="{{ collection.site.name }}" class="collection-logo">
{% else %}
<div class="collection-logo" style="background: linear-gradient(135deg, #0ea5e9 0%, #8b5cf6 100%);"></div>
{% endif %}
<div class="collection-info">
<h3>{{ collection.site.name }}</h3>
<p>{{ collection.site.short_desc or collection.site.description }}</p>
</div>
</a>
{% endfor %}
{% else %}
<div class="empty-state">
<div class="empty-state-icon">📦</div>
<p>还没有收藏任何工具</p>
<p style="margin-top: 8px;"><a href="/" style="color: var(--primary-blue);">去首页逛逛</a></p>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}