feat: 初始版本,圆角主题与首次管理员引导

This commit is contained in:
2025-12-07 10:53:52 +08:00
commit 63db6a0815
43 changed files with 1293 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% block title %}活动详情{% endblock %}
{% block content %}
<h3>{{ activity.title }}</h3>
<p>{{ activity.description }}</p>
<a class="btn btn-primary" href="{{ url_for('activities.submit', act_id=activity.id) }}">投稿</a>
{% endblock %}

View File

@@ -0,0 +1,10 @@
{% extends 'base.html' %}
{% block title %}活动{% endblock %}
{% block content %}
<h3>活动</h3>
<ul>
{% for a in activities %}
<li><a href="{{ url_for('activities.detail', act_id=a.id) }}">{{ a.title }}</a></li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -0,0 +1,10 @@
{% extends 'base.html' %}
{% block title %}投稿{% endblock %}
{% block content %}
<h3>向 {{ activity.title }} 投稿</h3>
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<div class="mb-3"><label class="form-label">图片</label><input class="form-control" name="images" type="file" accept="image/*" multiple required></div>
<button class="btn btn-primary" type="submit">提交</button>
</form>
{% endblock %}

View File

@@ -0,0 +1,31 @@
{% extends 'base.html' %}
{% block title %}活动管理{% endblock %}
{% block content %}
<h3>活动管理</h3>
<form method="post" class="mb-3">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<div class="mb-2"><input class="form-control" name="title" placeholder="标题" required></div>
<div class="mb-2"><input class="form-control" name="theme" placeholder="主题"></div>
<div class="mb-2"><textarea class="form-control" name="description" placeholder="描述"></textarea></div>
<button class="btn btn-primary" type="submit">创建</button>
</form>
<table class="table">
<tr><th>标题</th><th>状态</th><th>操作</th></tr>
{% for a in activities %}
<tr>
<td>{{ a.title }}</td>
<td>{{ a.status.value }}</td>
<td>
<form method="post" action="{{ url_for('admin.publish_activity', act_id=a.id) }}" style="display:inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<button class="btn btn-success">发布</button>
</form>
<form method="post" action="{{ url_for('admin.close_activity', act_id=a.id) }}" style="display:inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<button class="btn btn-warning">关闭</button>
</form>
</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@@ -0,0 +1,15 @@
{% extends 'base.html' %}
{% block title %}管理员{% endblock %}
{% block content %}
<h3>管理员仪表盘</h3>
<div class="row">
<div class="col">待审核注册:{{ pending_users }}</div>
<div class="col">待审核作品:{{ pending_posts }}</div>
<div class="col">待审核投稿:{{ pending_subs }}</div>
</div>
<hr>
<a class="btn btn-link" href="{{ url_for('admin.review_users') }}">注册审核</a>
<a class="btn btn-link" href="{{ url_for('admin.review_posts') }}">作品审核</a>
<a class="btn btn-link" href="{{ url_for('admin.review_submissions') }}">投稿审核</a>
<a class="btn btn-link" href="{{ url_for('admin.manage_activities') }}">活动管理</a>
{% endblock %}

View File

@@ -0,0 +1,24 @@
{% extends 'base.html' %}
{% block title %}作品审核{% endblock %}
{% block content %}
<h3>作品审核</h3>
<table class="table">
<tr><th>标题</th><th>作者</th><th>操作</th></tr>
{% for p in posts %}
<tr>
<td>{{ p.title }}</td>
<td>{{ p.user.username }}</td>
<td>
<form method="post" action="{{ url_for('admin.approve_post', post_id=p.id) }}" style="display:inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<button class="btn btn-success">通过</button>
</form>
<form method="post" action="{{ url_for('admin.reject_post', post_id=p.id) }}" style="display:inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<button class="btn btn-danger">拒绝</button>
</form>
</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@@ -0,0 +1,24 @@
{% extends 'base.html' %}
{% block title %}投稿审核{% endblock %}
{% block content %}
<h3>投稿审核</h3>
<table class="table">
<tr><th>活动</th><th>用户</th><th>操作</th></tr>
{% for s in submissions %}
<tr>
<td>{{ s.activity.title }}</td>
<td>{{ s.user_id }}</td>
<td>
<form method="post" action="{{ url_for('admin.approve_submission', sub_id=s.id) }}" style="display:inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<button class="btn btn-success">通过</button>
</form>
<form method="post" action="{{ url_for('admin.reject_submission', sub_id=s.id) }}" style="display:inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<button class="btn btn-danger">拒绝</button>
</form>
</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@@ -0,0 +1,24 @@
{% extends 'base.html' %}
{% block title %}注册审核{% endblock %}
{% block content %}
<h3>注册审核</h3>
<table class="table">
<tr><th>用户名</th><th>邮箱</th><th>操作</th></tr>
{% for u in users %}
<tr>
<td>{{ u.username }}</td>
<td>{{ u.email }}</td>
<td>
<form method="post" action="{{ url_for('admin.approve_user', user_id=u.id) }}" style="display:inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<button class="btn btn-success">通过</button>
</form>
<form method="post" action="{{ url_for('admin.reject_user', user_id=u.id) }}" style="display:inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<button class="btn btn-danger">拒绝</button>
</form>
</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% block title %}登录{% endblock %}
{% block content %}
<h3 class="mb-3">登录</h3>
<form class="card p-3" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<div class="mb-3"><label class="form-label">邮箱</label><input class="form-control" name="email" type="email" required></div>
<div class="mb-3"><label class="form-label">密码</label><input class="form-control" name="password" type="password" required></div>
<button class="btn btn-brand" type="submit">登录</button>
</form>
{% endblock %}

View File

@@ -0,0 +1,13 @@
{% extends 'base.html' %}
{% block title %}注册{% endblock %}
{% block content %}
<h3 class="mb-3">注册</h3>
<form class="card p-3" method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<div class="mb-3"><label class="form-label">邮箱</label><input class="form-control" name="email" type="email" required></div>
<div class="mb-3"><label class="form-label">用户名</label><input class="form-control" name="username" required></div>
<div class="mb-3"><label class="form-label">密码</label><input class="form-control" name="password" type="password" required></div>
<div class="mb-3"><label class="form-label">学生身份照片</label><input class="form-control" name="identity_photo" type="file" accept="image/*" required></div>
<button class="btn btn-brand" type="submit">提交</button>
</form>
{% endblock %}

43
app/templates/base.html Normal file
View File

@@ -0,0 +1,43 @@
<!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>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="{{ url_for('static', filename='theme.css') }}" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-white mb-3 mt-2 px-3">
<div class="container-fluid">
<a class="navbar-brand" href="{{ url_for('feed.discover') }}">摄影社</a>
<div>
<a class="btn btn-link" href="{{ url_for('feed.discover') }}">发现</a>
{% if current_user.is_authenticated %}
<a class="btn btn-link" href="{{ url_for('feed.following') }}">关注</a>
<a class="btn btn-link" href="{{ url_for('users.profile', user_id=current_user.id) }}">我的主页</a>
<a class="btn btn-link" href="{{ url_for('users.notifications') }}">通知</a>
<a class="btn btn-link" href="{{ url_for('posts.create') }}">发布作品</a>
<a class="btn btn-link" href="{{ url_for('activities.list_activities') }}">活动</a>
{% if current_user.role == 'admin' %}
<a class="btn btn-link" href="{{ url_for('admin.dashboard') }}">管理员</a>
{% endif %}
<a class="btn btn-link" href="{{ url_for('auth.logout') }}">退出</a>
{% else %}
<a class="btn btn-link" href="{{ url_for('auth.login') }}">登录</a>
<a class="btn btn-brand" href="{{ url_for('auth.register') }}">注册</a>
{% endif %}
</div>
</div>
</nav>
<div class="container">
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alert alert-info">{{ messages[0] }}</div>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@@ -0,0 +1,20 @@
{% extends 'base.html' %}
{% block title %}发现{% endblock %}
{% block content %}
<h3 class="mb-3">发现</h3>
<div class="card-grid">
{% for p in posts %}
<div class="card">
{% set first = p.images[0] if p.images %}
{% if first %}
<a href="{{ url_for('posts.detail', post_id=p.id) }}">
<img class="photo-thumb" src="{{ url_for('main.uploads', filename=first.web_path.split('uploads\\')[-1]) }}" alt="{{ p.title }}" />
</a>
{% endif %}
<div class="p-3">
<a class="text-decoration-none" href="{{ url_for('posts.detail', post_id=p.id) }}">{{ p.title }}</a>
</div>
</div>
{% endfor %}
</div>
{% endblock %}

View File

@@ -0,0 +1,20 @@
{% extends 'base.html' %}
{% block title %}关注{% endblock %}
{% block content %}
<h3 class="mb-3">关注</h3>
<div class="card-grid">
{% for p in posts %}
<div class="card">
{% set first = p.images[0] if p.images %}
{% if first %}
<a href="{{ url_for('posts.detail', post_id=p.id) }}">
<img class="photo-thumb" src="{{ url_for('main.uploads', filename=first.web_path.split('uploads\\')[-1]) }}" alt="{{ p.title }}" />
</a>
{% endif %}
<div class="p-3">
<a class="text-decoration-none" href="{{ url_for('posts.detail', post_id=p.id) }}">{{ p.title }}</a>
</div>
</div>
{% endfor %}
</div>
{% endblock %}

View File

@@ -0,0 +1,19 @@
{% extends 'base.html' %}
{% block title %}发布作品{% endblock %}
{% block content %}
<h3 class="mb-3">发布作品</h3>
<form class="card p-3" method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<div class="mb-3"><label class="form-label">标题</label><input class="form-control" name="title" required></div>
<div class="mb-3"><label class="form-label">介绍</label><textarea class="form-control" name="description"></textarea></div>
<div class="mb-3"><label class="form-label">可见性</label>
<select class="form-select" name="visibility">
<option value="public">公开(需审核)</option>
<option value="followers">仅关注者</option>
<option value="private">仅自己</option>
</select>
</div>
<div class="mb-3"><label class="form-label">图片</label><input class="form-control" name="images" type="file" accept="image/*" multiple required></div>
<button class="btn btn-brand" type="submit">提交</button>
</form>
{% endblock %}

View File

@@ -0,0 +1,26 @@
{% extends 'base.html' %}
{% block title %}作品详情{% endblock %}
{% block content %}
<div class="card mb-3">
<div class="p-3">
<h3 class="mb-2">{{ post.title }}</h3>
<p class="text-secondary">{{ post.description }}</p>
<span class="badge bg-light text-dark">{{ post.visibility.value }}</span>
</div>
<div class="p-3">
<div class="row">
{% for img in post.images %}
<div class="col-md-4 mb-2">
<img src="{{ url_for('main.uploads', filename=img.web_path.split('uploads\\')[-1]) }}" class="photo-thumb" />
</div>
{% endfor %}
</div>
</div>
</div>
<hr>
<form class="card p-3" method="post" action="{{ url_for('comments.create', post_id=post.id) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<div class="mb-3"><textarea class="form-control" name="body" placeholder="发表评论"></textarea></div>
<button class="btn btn-brand" type="submit">评论</button>
</form>
{% endblock %}

View File

@@ -0,0 +1,13 @@
{% extends 'base.html' %}
{% block title %}创建管理员{% endblock %}
{% block content %}
<h3 class="mb-3">首次使用:创建管理员</h3>
<form class="card p-3" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<div class="mb-3"><label class="form-label">邮箱</label><input class="form-control" name="email" type="email" required></div>
<div class="mb-3"><label class="form-label">用户名</label><input class="form-control" name="username" required></div>
<div class="mb-3"><label class="form-label">密码</label><input class="form-control" name="password" type="password" required></div>
<div class="mb-3"><label class="form-label">确认密码</label><input class="form-control" name="confirm" type="password" required></div>
<button class="btn btn-brand" type="submit">创建管理员</button>
</form>
{% endblock %}

View File

@@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% block title %}编辑资料{% endblock %}
{% block content %}
<h3>编辑资料</h3>
<form method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<div class="mb-3"><label class="form-label">简介</label><textarea class="form-control" name="bio"></textarea></div>
<div class="mb-3"><label class="form-label">年级</label><input class="form-control" name="grade"></div>
<div class="mb-3"><label class="form-label">班级</label><input class="form-control" name="class_name"></div>
<button class="btn btn-primary" type="submit">保存</button>
</form>
{% endblock %}

View File

@@ -0,0 +1,10 @@
{% extends 'base.html' %}
{% block title %}通知{% endblock %}
{% block content %}
<h3>通知</h3>
<ul>
{% for n in items %}
<li>{{ n.type }} {{ n.created_at }}</li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -0,0 +1,20 @@
{% extends 'base.html' %}
{% block title %}主页{% endblock %}
{% block content %}
<div class="d-flex justify-content-between align-items-center">
<h3>{{ user.username }}</h3>
{% if current_user.is_authenticated and current_user.id != user.id %}
<a class="btn btn-outline-primary" href="{{ url_for('follows.follow', user_id=user.id) }}">关注</a>
{% endif %}
</div>
<p>{{ user.profile.bio }}</p>
<hr>
<h5>作品</h5>
<div class="row">
{% for p in user.posts %}
<div class="col-md-4">
<a href="{{ url_for('posts.detail', post_id=p.id) }}">{{ p.title }}</a>
</div>
{% endfor %}
</div>
{% endblock %}