first commit

This commit is contained in:
2026-02-27 10:37:11 +08:00
commit 74f19aad0b
86 changed files with 18642 additions and 0 deletions

369
JesusChrist/app.py Normal file
View File

@@ -0,0 +1,369 @@
import os
import sqlite3
import random
import string
from datetime import datetime
from flask import Flask, render_template, request, redirect, url_for, g, flash, jsonify
app = Flask(__name__)
app.secret_key = 'admin-secret-key-2026'
# 连接主应用的数据库
DATABASE = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'instance', 'database.db')
def get_db():
db = getattr(g, '_database', None)
if db is None:
db = g._database = sqlite3.connect(DATABASE)
db.row_factory = sqlite3.Row
return db
@app.teardown_appcontext
def close_connection(exception):
db = getattr(g, '_database', None)
if db is not None:
db.close()
def init_db():
"""确保 system_notification 和 invite_code 表存在"""
with app.app_context():
db = get_db()
db.executescript('''
CREATE TABLE IF NOT EXISTS system_notification (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT NOT NULL,
author_id INTEGER NOT NULL,
pinned INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS invite_code (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT UNIQUE NOT NULL,
user_id INTEGER NOT NULL,
application_id INTEGER NOT NULL,
used INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
used_at TIMESTAMP
);
''')
db.commit()
# PLACEHOLDER_ROUTES
def generate_invite_code():
return 'TC-' + ''.join(random.choices(string.ascii_uppercase + string.digits, k=8))
def add_notification(db, user_id, ntype, content, from_user='', post_id=0):
db.execute('INSERT INTO notification (user_id, type, content, from_user, post_id, read) VALUES (?,?,?,?,?,0)',
(user_id, ntype, content, from_user, post_id))
# ========== 后台首页 ==========
@app.route('/admin')
def dashboard():
db = get_db()
stats = {
'users': db.execute('SELECT COUNT(*) as c FROM user').fetchone()['c'],
'contests': db.execute('SELECT COUNT(*) as c FROM contest').fetchone()['c'],
'exams': db.execute('SELECT COUNT(*) as c FROM exam').fetchone()['c'],
'posts': db.execute('SELECT COUNT(*) as c FROM post').fetchone()['c'],
'pending_teacher': db.execute("SELECT COUNT(*) as c FROM teacher_application WHERE status='pending'").fetchone()['c'],
'pending_contest': db.execute("SELECT COUNT(*) as c FROM contest_application WHERE status='pending'").fetchone()['c'],
}
return render_template('dashboard.html', stats=stats)
# ========== 杯赛管理 ==========
@app.route('/admin/competitions')
def competitions():
db = get_db()
contests = db.execute('SELECT * FROM contest ORDER BY created_at DESC').fetchall()
return render_template('competitions.html', competitions=contests)
@app.route('/admin/competitions/stop/<int:id>', methods=['POST'])
def stop_competition(id):
db = get_db()
db.execute("UPDATE contest SET status = 'ended' WHERE id = ?", (id,))
db.commit()
return redirect(url_for('competitions'))
@app.route('/admin/competitions/activate/<int:id>', methods=['POST'])
def activate_competition(id):
db = get_db()
db.execute("UPDATE contest SET status = 'upcoming' WHERE id = ?", (id,))
db.commit()
return redirect(url_for('competitions'))
# ========== 杯赛申请 ==========
@app.route('/admin/competition_apps')
def competition_apps():
db = get_db()
apps = db.execute('''
SELECT ca.*, u.name as applicant_name FROM contest_application ca
JOIN user u ON ca.user_id = u.id
ORDER BY ca.applied_at DESC
''').fetchall()
return render_template('competition_apps.html', apps=apps)
@app.route('/admin/competition_apps/approve/<int:id>', methods=['POST'])
def approve_competition_app(id):
db = get_db()
ca = db.execute('SELECT * FROM contest_application WHERE id = ?', (id,)).fetchone()
if not ca or ca['status'] != 'pending':
flash('该申请已处理', 'error')
return redirect(url_for('competition_apps'))
# 创建杯赛
db.execute('INSERT INTO contest (name, organizer, description, start_date, end_date, status, participants, created_by) VALUES (?,?,?,?,?,?,?,?)',
(ca['name'], ca['organizer'], ca['description'], '待定', '待定', 'upcoming', 0, ca['user_id']))
contest_id = db.execute('SELECT last_insert_rowid()').fetchone()[0]
# 创建成员关系owner
db.execute('INSERT INTO contest_membership (user_id, contest_id, role) VALUES (?,?,?)', (ca['user_id'], contest_id, 'owner'))
# 创建杯赛讨论群
db.execute('INSERT INTO chat_room (type, name, creator_id, contest_id) VALUES (?,?,?,?)',
('contest', ca['name'] + ' 讨论群', ca['user_id'], contest_id))
room_id = db.execute('SELECT last_insert_rowid()').fetchone()[0]
db.execute('INSERT INTO chat_room_member (room_id, user_id, role) VALUES (?,?,?)', (room_id, ca['user_id'], 'admin'))
# 更新申请状态
db.execute("UPDATE contest_application SET status = 'approved', reviewed_at = ? WHERE id = ?", (datetime.utcnow(), id))
add_notification(db, ca['user_id'], 'contest_result', f'您申请举办的杯赛「{ca["name"]}」已通过审核!', from_user='系统')
db.commit()
flash('申请已批准,杯赛已创建', 'success')
return redirect(url_for('competition_apps'))
@app.route('/admin/competition_apps/reject/<int:id>', methods=['POST'])
def reject_competition_app(id):
db = get_db()
ca = db.execute('SELECT * FROM contest_application WHERE id = ?', (id,)).fetchone()
if not ca or ca['status'] != 'pending':
flash('该申请已处理', 'error')
return redirect(url_for('competition_apps'))
db.execute("UPDATE contest_application SET status = 'rejected', reviewed_at = ? WHERE id = ?", (datetime.utcnow(), id))
add_notification(db, ca['user_id'], 'contest_result', f'您申请举办的杯赛「{ca["name"]}」未通过审核。', from_user='系统')
db.commit()
flash('申请已拒绝', 'success')
return redirect(url_for('competition_apps'))
# PLACEHOLDER_TEACHER_ROUTES
# ========== 教师申请(邀请码流程) ==========
@app.route('/admin/teacher_apps')
def teacher_apps():
db = get_db()
apps = db.execute('''
SELECT ta.*, u.name as username, c.name as contest_name
FROM teacher_application ta
JOIN user u ON ta.user_id = u.id
JOIN contest c ON ta.contest_id = c.id
ORDER BY ta.applied_at DESC
''').fetchall()
return render_template('teacher_apps.html', apps=apps)
@app.route('/admin/teacher_apps/approve/<int:id>', methods=['POST'])
def approve_teacher_app(id):
db = get_db()
ta = db.execute('SELECT * FROM teacher_application WHERE id = ?', (id,)).fetchone()
if not ta or ta['status'] != 'pending':
flash('该申请已处理', 'error')
return redirect(url_for('teacher_apps'))
# 检查是否已是杯赛成员
existing = db.execute('SELECT id FROM contest_membership WHERE user_id = ? AND contest_id = ?',
(ta['user_id'], ta['contest_id'])).fetchone()
if existing:
db.execute("UPDATE teacher_application SET status = 'rejected', reviewed_at = ? WHERE id = ?", (datetime.utcnow(), id))
db.commit()
flash('用户已是杯赛成员,申请已拒绝', 'error')
return redirect(url_for('teacher_apps'))
# 生成邀请码
code = generate_invite_code()
while db.execute('SELECT id FROM invite_code WHERE code = ?', (code,)).fetchone():
code = generate_invite_code()
db.execute('INSERT INTO invite_code (code, user_id, application_id) VALUES (?,?,?)', (code, ta['user_id'], ta['id']))
db.execute("UPDATE teacher_application SET status = 'approved', reviewed_at = ? WHERE id = ?", (datetime.utcnow(), id))
# 获取杯赛名称
contest = db.execute('SELECT name FROM contest WHERE id = ?', (ta['contest_id'],)).fetchone()
contest_name = contest['name'] if contest else ''
# 查找或创建私聊室,发送邀请码消息
admin_id = db.execute("SELECT id FROM user WHERE role = 'admin' LIMIT 1").fetchone()
sender_id = admin_id['id'] if admin_id else 1
# 查找已有私聊
room = db.execute('''
SELECT cr.id FROM chat_room cr
WHERE cr.type = 'private'
AND cr.id IN (SELECT room_id FROM chat_room_member WHERE user_id = ?)
AND cr.id IN (SELECT room_id FROM chat_room_member WHERE user_id = ?)
''', (sender_id, ta['user_id'])).fetchone()
if room:
room_id = room['id']
else:
db.execute('INSERT INTO chat_room (type, creator_id) VALUES (?,?)', ('private', sender_id))
room_id = db.execute('SELECT last_insert_rowid()').fetchone()[0]
db.execute('INSERT INTO chat_room_member (room_id, user_id, role) VALUES (?,?,?)', (room_id, sender_id, 'member'))
db.execute('INSERT INTO chat_room_member (room_id, user_id, role) VALUES (?,?,?)', (room_id, ta['user_id'], 'member'))
# 发送私聊消息
msg = f'恭喜!您申请成为杯赛「{contest_name}」老师已通过审核。\n请使用以下邀请码激活您的教师身份:\n\n🎫 邀请码:{code}\n\n请前往「申请成为老师」页面,在邀请码输入框中输入此码完成激活。\n注意:此邀请码仅限您本人使用,且仅限一次。'
db.execute('INSERT INTO message (room_id, sender_id, type, content) VALUES (?,?,?,?)', (room_id, sender_id, 'system', msg))
add_notification(db, ta['user_id'], 'teacher_result',
f'您申请成为杯赛「{contest_name}」老师已通过审核,请查看私聊消息获取邀请码。', from_user='系统')
db.commit()
flash(f'申请已批准,邀请码 {code} 已通过私聊发送给老师', 'success')
return redirect(url_for('teacher_apps'))
@app.route('/admin/teacher_apps/reject/<int:id>', methods=['POST'])
def reject_teacher_app(id):
db = get_db()
ta = db.execute('SELECT * FROM teacher_application WHERE id = ?', (id,)).fetchone()
if not ta or ta['status'] != 'pending':
flash('该申请已处理', 'error')
return redirect(url_for('teacher_apps'))
db.execute("UPDATE teacher_application SET status = 'rejected', reviewed_at = ? WHERE id = ?", (datetime.utcnow(), id))
contest = db.execute('SELECT name FROM contest WHERE id = ?', (ta['contest_id'],)).fetchone()
contest_name = contest['name'] if contest else ''
add_notification(db, ta['user_id'], 'teacher_result',
f'您申请成为杯赛「{contest_name}」老师未通过审核。', from_user='系统')
db.commit()
flash('申请已拒绝', 'success')
return redirect(url_for('teacher_apps'))
# PLACEHOLDER_REMAINING_ROUTES
# ========== 考试管理 ==========
@app.route('/admin/exams')
def exams():
db = get_db()
exams = db.execute('''
SELECT e.*, c.name as contest_name, u.name as creator_name
FROM exam e
LEFT JOIN contest c ON e.contest_id = c.id
LEFT JOIN user u ON e.creator_id = u.id
ORDER BY e.created_at DESC
''').fetchall()
return render_template('exams.html', exams=exams)
@app.route('/admin/exams/stop/<int:id>', methods=['POST'])
def stop_exam(id):
db = get_db()
db.execute("UPDATE exam SET status = 'closed' WHERE id = ?", (id,))
db.commit()
flash('考试已停止', 'success')
return redirect(url_for('exams'))
@app.route('/admin/exams/activate/<int:id>', methods=['POST'])
def activate_exam(id):
db = get_db()
db.execute("UPDATE exam SET status = 'available' WHERE id = ?", (id,))
db.commit()
flash('考试已恢复', 'success')
return redirect(url_for('exams'))
# ========== 用户管理 ==========
@app.route('/admin/users')
def users():
db = get_db()
all_users = db.execute('SELECT * FROM user ORDER BY created_at DESC').fetchall()
return render_template('users.html', users=all_users)
@app.route('/admin/users/toggle_ban/<int:id>', methods=['POST'])
def toggle_ban_user(id):
db = get_db()
cur = db.execute('SELECT is_banned FROM user WHERE id = ?', (id,)).fetchone()
new_val = 0 if cur['is_banned'] else 1
db.execute('UPDATE user SET is_banned = ? WHERE id = ?', (new_val, id))
db.commit()
flash('用户状态已更新', 'success')
return redirect(url_for('users'))
@app.route('/admin/users/set_role/<int:id>', methods=['POST'])
def set_user_role(id):
role = request.form.get('role', 'student')
db = get_db()
db.execute('UPDATE user SET role = ? WHERE id = ?', (role, id))
db.commit()
flash('角色已更新', 'success')
return redirect(url_for('users'))
# ========== 帖子管理 ==========
@app.route('/admin/posts')
def posts():
db = get_db()
all_posts = db.execute('''
SELECT p.*, u.name as author_name FROM post p
LEFT JOIN user u ON p.author_id = u.id
ORDER BY p.created_at DESC
''').fetchall()
return render_template('posts.html', posts=all_posts)
@app.route('/admin/posts/delete/<int:id>', methods=['POST'])
def delete_post(id):
db = get_db()
db.execute('DELETE FROM post WHERE id = ?', (id,))
db.commit()
flash('帖子已删除', 'success')
return redirect(url_for('posts'))
@app.route('/admin/posts/toggle_pin/<int:id>', methods=['POST'])
def toggle_pin_post(id):
db = get_db()
cur = db.execute('SELECT pinned FROM post WHERE id = ?', (id,)).fetchone()
new_val = 0 if cur['pinned'] else 1
db.execute('UPDATE post SET pinned = ? WHERE id = ?', (new_val, id))
db.commit()
flash('帖子置顶状态已更新', 'success')
return redirect(url_for('posts'))
# PLACEHOLDER_NOTIFICATION_ROUTES
# ========== 公告管理 ==========
@app.route('/admin/notifications')
def notifications():
db = get_db()
anns = db.execute('''
SELECT sn.*, u.name as author_name FROM system_notification sn
LEFT JOIN user u ON sn.author_id = u.id
ORDER BY sn.pinned DESC, sn.created_at DESC
''').fetchall()
return render_template('notifications.html', announcements=anns)
@app.route('/admin/notifications/create', methods=['POST'])
def create_notification():
title = request.form.get('title', '').strip()
content = request.form.get('content', '').strip()
pinned = 1 if request.form.get('pinned') else 0
if not title or not content:
flash('标题和内容不能为空', 'error')
return redirect(url_for('notifications'))
db = get_db()
admin = db.execute("SELECT id FROM user WHERE role = 'admin' LIMIT 1").fetchone()
author_id = admin['id'] if admin else 1
now = datetime.utcnow()
db.execute('INSERT INTO system_notification (title, content, author_id, pinned, created_at, updated_at) VALUES (?,?,?,?,?,?)',
(title, content, author_id, pinned, now, now))
db.commit()
flash('公告已发布', 'success')
return redirect(url_for('notifications'))
@app.route('/admin/notifications/edit/<int:id>', methods=['POST'])
def edit_notification(id):
title = request.form.get('title', '').strip()
content = request.form.get('content', '').strip()
pinned = 1 if request.form.get('pinned') else 0
if not title or not content:
flash('标题和内容不能为空', 'error')
return redirect(url_for('notifications'))
db = get_db()
db.execute('UPDATE system_notification SET title=?, content=?, pinned=?, updated_at=? WHERE id=?',
(title, content, pinned, datetime.utcnow(), id))
db.commit()
flash('公告已更新', 'success')
return redirect(url_for('notifications'))
@app.route('/admin/notifications/delete/<int:id>', methods=['POST'])
def delete_notification(id):
db = get_db()
db.execute('DELETE FROM system_notification WHERE id = ?', (id,))
db.commit()
flash('公告已删除', 'success')
return redirect(url_for('notifications'))
if __name__ == '__main__':
init_db()
app.run(debug=True, port=5001)

Binary file not shown.

View File

@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>后台管理 - {% block title %}{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.sidebar { min-height: calc(100vh - 56px); }
.nav-link.active { background-color: #0d6efd; color: #fff !important; border-radius: 6px; }
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="{{ url_for('dashboard') }}">🛡️ 管理后台</a>
</div>
</nav>
<div class="container-fluid">
<div class="row">
<nav class="col-md-2 bg-light sidebar py-3">
<div class="nav flex-column nav-pills">
<a class="nav-link text-dark mb-1" href="{{ url_for('dashboard') }}">📊 仪表盘</a>
<a class="nav-link text-dark mb-1" href="{{ url_for('competitions') }}">🏆 杯赛管理</a>
<a class="nav-link text-dark mb-1" href="{{ url_for('competition_apps') }}">📋 杯赛申请</a>
<a class="nav-link text-dark mb-1" href="{{ url_for('teacher_apps') }}">👨‍🏫 教师申请</a>
<a class="nav-link text-dark mb-1" href="{{ url_for('exams') }}">📝 考试管理</a>
<a class="nav-link text-dark mb-1" href="{{ url_for('users') }}">👥 用户管理</a>
<a class="nav-link text-dark mb-1" href="{{ url_for('posts') }}">💬 帖子管理</a>
<a class="nav-link text-dark mb-1" href="{{ url_for('notifications') }}">📢 公告管理</a>
</div>
</nav>
<main class="col-md-10 py-4">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ 'danger' if category == 'error' else 'success' }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</main>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@@ -0,0 +1,50 @@
{% extends "base.html" %}
{% block title %}杯赛申请{% endblock %}
{% block content %}
<h1>杯赛申请</h1>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>比赛名称</th>
<th>申请人</th>
<th>描述</th>
<th>状态</th>
<th>申请时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for app in apps %}
<tr>
<td>{{ app.id }}</td>
<td>{{ app.name }}</td>
<td>{{ app.applicant_name }}</td>
<td>{{ app.description }}</td>
<td>
{% if app.status == 'pending' %}
<span class="badge bg-warning">待处理</span>
{% elif app.status == 'approved' %}
<span class="badge bg-success">已批准</span>
{% else %}
<span class="badge bg-danger">已驳回</span>
{% endif %}
</td>
<td>{{ app.created_at }}</td>
<td>
{% if app.status == 'pending' %}
<form action="{{ url_for('approve_competition_app', id=app.id) }}" method="post" style="display:inline;">
<button type="submit" class="btn btn-success btn-sm">批准</button>
</form>
<form action="{{ url_for('reject_competition_app', id=app.id) }}" method="post" style="display:inline;">
<button type="submit" class="btn btn-danger btn-sm">驳回</button>
</form>
{% else %}
<span class="text-muted">已处理</span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@@ -0,0 +1,43 @@
{% extends "base.html" %}
{% block title %}杯赛管理{% endblock %}
{% block content %}
<h1>杯赛管理</h1>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>名称</th>
<th>描述</th>
<th>状态</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for comp in competitions %}
<tr>
<td>{{ comp.id }}</td>
<td>{{ comp.name }}</td>
<td>{{ comp.description }}</td>
<td>
{% if comp.status == 'active' %}
<span class="badge bg-success">进行中</span>
{% else %}
<span class="badge bg-secondary">已停止</span>
{% endif %}
</td>
<td>{{ comp.created_at }}</td>
<td>
{% if comp.status == 'active' %}
<form action="{{ url_for('stop_competition', id=comp.id) }}" method="post" style="display:inline;">
<button type="submit" class="btn btn-warning btn-sm" onclick="return confirm('确定停止该比赛吗?')">停止</button>
</form>
{% else %}
<span class="text-muted">无操作</span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@@ -0,0 +1,61 @@
{% extends "base.html" %}
{% block title %}仪表盘{% endblock %}
{% block content %}
<h2 class="mb-4">仪表盘</h2>
<div class="row g-4">
<div class="col-md-4">
<div class="card border-primary h-100">
<div class="card-body text-center">
<h1 class="display-4 text-primary">{{ stats.users }}</h1>
<p class="text-muted mb-2">注册用户</p>
<a href="{{ url_for('users') }}" class="btn btn-outline-primary btn-sm">管理用户</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card border-success h-100">
<div class="card-body text-center">
<h1 class="display-4 text-success">{{ stats.contests }}</h1>
<p class="text-muted mb-2">杯赛总数</p>
<a href="{{ url_for('competitions') }}" class="btn btn-outline-success btn-sm">管理杯赛</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card border-info h-100">
<div class="card-body text-center">
<h1 class="display-4 text-info">{{ stats.exams }}</h1>
<p class="text-muted mb-2">考试总数</p>
<a href="{{ url_for('exams') }}" class="btn btn-outline-info btn-sm">管理考试</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card border-warning h-100">
<div class="card-body text-center">
<h1 class="display-4 text-warning">{{ stats.pending_teacher }}</h1>
<p class="text-muted mb-2">待审教师申请</p>
<a href="{{ url_for('teacher_apps') }}" class="btn btn-outline-warning btn-sm">处理申请</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card border-danger h-100">
<div class="card-body text-center">
<h1 class="display-4 text-danger">{{ stats.pending_contest }}</h1>
<p class="text-muted mb-2">待审杯赛申请</p>
<a href="{{ url_for('competition_apps') }}" class="btn btn-outline-danger btn-sm">处理申请</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card border-secondary h-100">
<div class="card-body text-center">
<h1 class="display-4 text-secondary">{{ stats.posts }}</h1>
<p class="text-muted mb-2">论坛帖子</p>
<a href="{{ url_for('posts') }}" class="btn btn-outline-secondary btn-sm">管理帖子</a>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,45 @@
{% extends "base.html" %}
{% block title %}考试管理{% endblock %}
{% block content %}
<h1>考试管理</h1>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>名称</th>
<th>科目</th>
<th>考试日期</th>
<th>状态</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for exam in exams %}
<tr>
<td>{{ exam.id }}</td>
<td>{{ exam.name }}</td>
<td>{{ exam.subject }}</td>
<td>{{ exam.exam_date }}</td>
<td>
{% if exam.status == 'active' %}
<span class="badge bg-success">进行中</span>
{% else %}
<span class="badge bg-secondary">已停止</span>
{% endif %}
</td>
<td>{{ exam.created_at }}</td>
<td>
{% if exam.status == 'active' %}
<form action="{{ url_for('stop_exam', id=exam.id) }}" method="post" style="display:inline;">
<button type="submit" class="btn btn-warning btn-sm" onclick="return confirm('确定停止该考试吗?')">停止</button>
</form>
{% else %}
<span class="text-muted">无操作</span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@@ -0,0 +1,45 @@
{% extends "base.html" %}
{% block title %}帖子管理{% endblock %}
{% block content %}
<h1>帖子管理</h1>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>标题</th>
<th>内容</th>
<th>作者</th>
<th>封禁状态</th>
<th>发布时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for post in posts %}
<tr>
<td>{{ post.id }}</td>
<td>{{ post.title }}</td>
<td>{{ post.content[:50] }}{% if post.content|length > 50 %}...{% endif %}</td>
<td>{{ post.username }}</td>
<td>
{% if post.banned == 0 %}
<span class="badge bg-success">正常</span>
{% else %}
<span class="badge bg-danger">已封禁</span>
{% endif %}
</td>
<td>{{ post.created_at }}</td>
<td>
<form action="{{ url_for('toggle_ban_post', id=post.id) }}" method="post" style="display:inline;">
{% if post.banned == 0 %}
<button type="submit" class="btn btn-warning btn-sm" onclick="return confirm('确定封禁该帖子?')">封禁</button>
{% else %}
<button type="submit" class="btn btn-success btn-sm" onclick="return confirm('确定解封该帖子?')">解封</button>
{% endif %}
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@@ -0,0 +1,48 @@
{% extends "base.html" %}
{% block title %}教师申请{% endblock %}
{% block content %}
<h1>教师申请</h1>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>申请人</th>
<th>理由</th>
<th>状态</th>
<th>申请时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for app in apps %}
<tr>
<td>{{ app.id }}</td>
<td>{{ app.username }}</td>
<td>{{ app.reason }}</td>
<td>
{% if app.status == 'pending' %}
<span class="badge bg-warning">待处理</span>
{% elif app.status == 'approved' %}
<span class="badge bg-success">已批准</span>
{% else %}
<span class="badge bg-danger">已驳回</span>
{% endif %}
</td>
<td>{{ app.created_at }}</td>
<td>
{% if app.status == 'pending' %}
<form action="{{ url_for('approve_teacher_app', id=app.id) }}" method="post" style="display:inline;">
<button type="submit" class="btn btn-success btn-sm">批准</button>
</form>
<form action="{{ url_for('reject_teacher_app', id=app.id) }}" method="post" style="display:inline;">
<button type="submit" class="btn btn-danger btn-sm">驳回</button>
</form>
{% else %}
<span class="text-muted">已处理</span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@@ -0,0 +1,45 @@
{% extends "base.html" %}
{% block title %}用户管理{% endblock %}
{% block content %}
<h1>用户管理</h1>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
<th>角色</th>
<th>封禁状态</th>
<th>注册时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.id }}</td>
<td>{{ user.username }}</td>
<td>{{ user.email }}</td>
<td>{{ user.role }}</td>
<td>
{% if user.banned == 0 %}
<span class="badge bg-success">正常</span>
{% else %}
<span class="badge bg-danger">已封禁</span>
{% endif %}
</td>
<td>{{ user.created_at }}</td>
<td>
<form action="{{ url_for('toggle_ban_user', id=user.id) }}" method="post" style="display:inline;">
{% if user.banned == 0 %}
<button type="submit" class="btn btn-warning btn-sm" onclick="return confirm('确定封禁该用户?')">封禁</button>
{% else %}
<button type="submit" class="btn btn-success btn-sm" onclick="return confirm('确定解封该用户?')">解封</button>
{% endif %}
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}