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

259 lines
7.4 KiB
HTML
Raw Permalink 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.
{% extends 'base_new.html' %}
{% block title %}我的收藏 - ZJPB{% endblock %}
{% block extra_css %}
<style>
.collections-container {
margin-top: 32px;
}
.collections-header {
background: var(--bg-white);
border: 1px solid var(--border-color);
border-radius: var(--radius-lg);
padding: 24px;
margin-bottom: 24px;
}
.collections-header h1 {
font-size: 28px;
font-weight: 700;
margin-bottom: 16px;
}
/* 文件夹标签 */
.folder-tabs {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.folder-tab {
padding: 8px 16px;
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
background: var(--bg-white);
color: var(--text-secondary);
text-decoration: none;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
}
.folder-tab:hover {
border-color: var(--primary-blue);
color: var(--primary-blue);
}
.folder-tab.active {
background: var(--primary-blue);
border-color: var(--primary-blue);
color: white;
}
/* 工具网格 */
.tools-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 32px;
}
.tool-card {
background: var(--bg-white);
border: 1px solid var(--border-color);
border-radius: var(--radius-lg);
padding: 20px;
text-decoration: none;
color: var(--text-primary);
transition: all 0.2s;
display: block;
}
.tool-card:hover {
border-color: var(--primary-blue);
box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
.tool-header {
display: flex;
gap: 12px;
margin-bottom: 12px;
}
.tool-logo {
width: 48px;
height: 48px;
border-radius: var(--radius-md);
object-fit: cover;
flex-shrink: 0;
}
.tool-info h3 {
font-size: 16px;
font-weight: 600;
margin-bottom: 4px;
}
.tool-desc {
font-size: 13px;
color: var(--text-secondary);
margin-bottom: 12px;
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.tool-tags {
display: flex;
gap: 6px;
flex-wrap: wrap;
}
.tool-tag {
padding: 4px 10px;
background: var(--bg-page);
color: var(--text-secondary);
border-radius: 4px;
font-size: 12px;
}
.empty-state {
background: var(--bg-white);
border: 1px solid var(--border-color);
border-radius: var(--radius-lg);
padding: 80px 40px;
text-align: center;
color: var(--text-muted);
}
.empty-icon {
font-size: 80px;
margin-bottom: 20px;
}
/* 分页 */
.pagination {
display: flex;
justify-content: center;
gap: 8px;
margin-top: 32px;
}
.pagination a,
.pagination span {
padding: 8px 12px;
border: 1px solid var(--border-color);
border-radius: var(--radius-sm);
background: var(--bg-white);
color: var(--text-primary);
text-decoration: none;
font-size: 14px;
transition: all 0.2s;
}
.pagination a:hover {
border-color: var(--primary-blue);
color: var(--primary-blue);
}
.pagination .active {
background: var(--primary-blue);
border-color: var(--primary-blue);
color: white;
}
</style>
{% endblock %}
{% block content %}
<div class="main-content">
<div class="collections-container">
<!-- 头部 -->
<div class="collections-header">
<h1>我的收藏</h1>
<!-- 文件夹标签 -->
<div class="folder-tabs">
<a href="/user/collections" class="folder-tab {% if not current_folder_id %}active{% endif %}">
📂 全部收藏 ({{ pagination.total }})
</a>
<a href="/user/collections?folder_id=none" class="folder-tab {% if current_folder_id == 'none' %}active{% endif %}">
📄 未分类
</a>
{% for folder in folders %}
<a href="/user/collections?folder_id={{ folder.id }}" class="folder-tab {% if current_folder_id == folder.id|string %}active{% endif %}">
{{ folder.icon }} {{ folder.name }} ({{ folder.count }})
</a>
{% endfor %}
</div>
</div>
<!-- 收藏网格 -->
{% if collections %}
<div class="tools-grid">
{% for collection in collections %}
<a href="/site/{{ collection.site.code }}" class="tool-card">
<div class="tool-header">
{% if collection.site.logo %}
<img src="{{ collection.site.logo }}" alt="{{ collection.site.name }}" class="tool-logo">
{% else %}
<div class="tool-logo" style="background: linear-gradient(135deg, #0ea5e9 0%, #8b5cf6 100%);"></div>
{% endif %}
<div class="tool-info">
<h3>{{ collection.site.name }}</h3>
</div>
</div>
<p class="tool-desc">{{ collection.site.short_desc or collection.site.description }}</p>
{% if collection.site.tags %}
<div class="tool-tags">
{% for tag in collection.site.tags[:3] %}
<span class="tool-tag">{{ tag.name }}</span>
{% endfor %}
</div>
{% endif %}
</a>
{% endfor %}
</div>
<!-- 分页 -->
{% if pagination.pages > 1 %}
<div class="pagination">
{% if pagination.has_prev %}
<a href="?page={{ pagination.prev_num }}{% if current_folder_id %}&folder_id={{ current_folder_id }}{% endif %}">上一页</a>
{% endif %}
{% for page_num in pagination.iter_pages() %}
{% if page_num %}
{% if page_num == pagination.page %}
<span class="active">{{ page_num }}</span>
{% else %}
<a href="?page={{ page_num }}{% if current_folder_id %}&folder_id={{ current_folder_id }}{% endif %}">{{ page_num }}</a>
{% endif %}
{% else %}
<span>...</span>
{% endif %}
{% endfor %}
{% if pagination.has_next %}
<a href="?page={{ pagination.next_num }}{% if current_folder_id %}&folder_id={{ current_folder_id }}{% endif %}">下一页</a>
{% endif %}
</div>
{% endif %}
{% else %}
<!-- 空状态 -->
<div class="empty-state">
<div class="empty-icon">📦</div>
<h2 style="font-size: 20px; margin-bottom: 8px;">暂无收藏</h2>
<p>去首页发现喜欢的AI工具吧</p>
<a href="/" style="display: inline-block; margin-top: 20px; padding: 10px 20px; background: var(--primary-blue); color: white; border-radius: var(--radius-md); text-decoration: none;">浏览工具</a>
</div>
{% endif %}
</div>
</div>
{% endblock %}