feat: 问题数据迁移到数据库;学员详情页URL导航改造;侧边栏统一
- 问题从文件系统迁移到数据库 problems 表 - 移除 PROBLEMS_DIR 配置和文件读取逻辑 - student.html 完整重写:编辑/添加/删除问题,生成方案进度显示 - 学员详情页支持独立URL访问 (/student/<id>) - 统一侧边栏到 base.html - 更新文档:DEPLOYMENT_SOP, MODELS, STRUCTURE, FRONTEND_ARCH - 部署到生产环境 v1.2.0
This commit is contained in:
@@ -13,7 +13,7 @@ logger = logging.getLogger(__name__)
|
||||
@login_required_json
|
||||
def classes_page():
|
||||
"""班级管理页面"""
|
||||
return render_template("classes.html")
|
||||
return render_template("classes.html", active_nav="classes")
|
||||
|
||||
|
||||
@main_bp.route("/api/classes", methods=["GET"])
|
||||
|
||||
+124
-22
@@ -13,10 +13,10 @@ from flask import (
|
||||
session,
|
||||
)
|
||||
from app.routes import main_bp
|
||||
from app.models import db, Student, PracticePlan
|
||||
from app.models import db, Student, PracticePlan, StudentProblem
|
||||
from app.services.plan_generator import generate_practice_plan, generate_ai_report
|
||||
from app.services.pdf_generator import generate_pdf
|
||||
from app.routes.auth import login_required_json
|
||||
from app.routes.auth import login_required_json, admin_required
|
||||
|
||||
|
||||
def sse_format(data):
|
||||
@@ -33,6 +33,114 @@ def get_student_plans(student_id):
|
||||
return jsonify([p.to_dict() for p in plans])
|
||||
|
||||
|
||||
@main_bp.route("/api/plans", methods=["GET"])
|
||||
@login_required_json
|
||||
def get_all_plans():
|
||||
"""获取所有方案(支持多条件筛选)
|
||||
|
||||
查询参数:
|
||||
- class_id: 班级ID
|
||||
- problem_ids: Problem.id 列表,逗号分隔
|
||||
- template_id: 模板ID
|
||||
- is_typical: 是否典型 (true/false)
|
||||
- student_name: 学员姓名(模糊匹配)
|
||||
"""
|
||||
import json as json_module
|
||||
|
||||
query = PracticePlan.query
|
||||
|
||||
# 按班级筛选
|
||||
class_id = request.args.get('class_id', type=int)
|
||||
if class_id:
|
||||
query = query.join(Student).filter(Student.class_id == class_id)
|
||||
|
||||
# 按模板筛选
|
||||
template_id = request.args.get('template_id', type=int)
|
||||
if template_id:
|
||||
query = query.filter(PracticePlan.template_id == template_id)
|
||||
|
||||
# 按典型状态筛选
|
||||
is_typical = request.args.get('is_typical')
|
||||
if is_typical and is_typical.lower() == 'true':
|
||||
query = query.filter(PracticePlan.is_typical == True)
|
||||
|
||||
# 按学员姓名模糊筛选
|
||||
student_name = request.args.get('student_name')
|
||||
if student_name:
|
||||
query = query.join(Student).filter(Student.name.like(f'%%{student_name}%%'))
|
||||
|
||||
# 按问题筛选(通过 problem_id 关联到学员的问题)
|
||||
problem_ids = request.args.get('problem_ids')
|
||||
if problem_ids:
|
||||
problem_id_list = [int(pid.strip()) for pid in problem_ids.split(',') if pid.strip()]
|
||||
if problem_id_list:
|
||||
# 筛选:方案对应的学员有指定问题之一的
|
||||
# 使用子查询避免笛卡尔积导致的重复
|
||||
from sqlalchemy import exists
|
||||
query = query.join(Student).filter(
|
||||
exists().where(
|
||||
(StudentProblem.student_id == Student.id) &
|
||||
(StudentProblem.problem_id.in_(problem_id_list))
|
||||
)
|
||||
)
|
||||
|
||||
plans = query.order_by(PracticePlan.created_at.desc()).all()
|
||||
return jsonify([p.to_dict() for p in plans])
|
||||
|
||||
|
||||
@main_bp.route("/api/plans/<int:plan_id>/typical", methods=["POST"])
|
||||
@login_required_json
|
||||
def toggle_plan_typical(plan_id):
|
||||
"""切换方案的典型状态"""
|
||||
plan = PracticePlan.query.get_or_404(plan_id)
|
||||
plan.is_typical = not plan.is_typical
|
||||
db.session.commit()
|
||||
return jsonify({"success": True, "is_typical": plan.is_typical})
|
||||
|
||||
|
||||
@main_bp.route("/plans")
|
||||
@login_required_json
|
||||
def plans_page():
|
||||
"""方案管理页面"""
|
||||
return render_template("plans.html", active_nav="plans")
|
||||
|
||||
|
||||
@main_bp.route("/api/admin/fix-plan-templates", methods=["GET"])
|
||||
@admin_required
|
||||
def fix_plan_templates():
|
||||
"""临时接口:修复所有方案的模板关联"""
|
||||
from app.models import Template, PracticePlan
|
||||
|
||||
simple_template = Template.query.filter_by(name='简单文字版').first()
|
||||
formal_template = Template.query.filter_by(name='正式报告版').first()
|
||||
|
||||
if not simple_template or not formal_template:
|
||||
return jsonify({"error": "模板没找到"}), 400
|
||||
|
||||
plans = PracticePlan.query.order_by(PracticePlan.created_at.desc()).all()
|
||||
if plans:
|
||||
plans[0].template_id = simple_template.id
|
||||
for plan in plans[1:]:
|
||||
plan.template_id = formal_template.id
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({"ok": True, "updated": len(plans)})
|
||||
|
||||
|
||||
@main_bp.route("/plan/<int:plan_id>")
|
||||
@login_required_json
|
||||
def plan_detail_page(plan_id):
|
||||
"""方案详情页面"""
|
||||
return render_template("plan_detail.html", active_nav="plans")
|
||||
|
||||
|
||||
@main_bp.route("/plan/<int:plan_id>/edit")
|
||||
@login_required_json
|
||||
def plan_edit_page(plan_id):
|
||||
"""方案编辑页面"""
|
||||
return render_template("plan_edit.html", active_nav="plans", plan_id=plan_id)
|
||||
|
||||
|
||||
@main_bp.route("/api/generate-plan", methods=["POST"])
|
||||
@login_required_json
|
||||
def generate_plan():
|
||||
@@ -49,30 +157,24 @@ def generate_plan():
|
||||
return jsonify({"error": "请先记录学员的问题"}), 400
|
||||
|
||||
# 预先收集所有数据,避免在generator中访问数据库
|
||||
problems_dir = current_app.config["PROBLEMS_DIR"]
|
||||
|
||||
# 学员的统一练习时间
|
||||
practice_time = student.practice_time or "30-60分钟"
|
||||
|
||||
problem_data = []
|
||||
for p in problems:
|
||||
# problem_id 已经是完整标识(如 "01_手小"),直接用作文件名
|
||||
problem_file = os.path.join(problems_dir, f"{p.problem_id}.md")
|
||||
|
||||
content = ""
|
||||
if os.path.exists(problem_file):
|
||||
with open(problem_file, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
|
||||
problem_data.append(
|
||||
{
|
||||
"problem_id": p.problem_id,
|
||||
"problem_name": p.problem_name,
|
||||
"severity": p.severity,
|
||||
"level": p.level,
|
||||
"content": content,
|
||||
}
|
||||
)
|
||||
# 使用 Problem 关联获取问题信息
|
||||
problem_obj = p.problem
|
||||
if problem_obj:
|
||||
problem_data.append(
|
||||
{
|
||||
"problem_id": problem_obj.id, # 使用 Problem.id
|
||||
"problem_name": problem_obj.name,
|
||||
"problem_no": problem_obj.no,
|
||||
"severity": p.severity,
|
||||
"level": p.level,
|
||||
"content": problem_obj.content or "",
|
||||
}
|
||||
)
|
||||
time_mapping = {
|
||||
"15分钟": {"total": 15, "basic": 10, "tech": 2, "piece": 3},
|
||||
"30分钟": {"total": 30, "basic": 15, "tech": 5, "piece": 10},
|
||||
@@ -104,7 +206,6 @@ def generate_plan():
|
||||
plan_content = generate_practice_plan(
|
||||
student_name=student.name,
|
||||
problems=problem_data,
|
||||
problems_dir=problems_dir,
|
||||
practice_time=practice_time,
|
||||
)
|
||||
|
||||
@@ -241,6 +342,7 @@ def generate_plan():
|
||||
try:
|
||||
plan = PracticePlan(
|
||||
student_id=student_id,
|
||||
template_id=template_id,
|
||||
content=json.dumps(plan_content, ensure_ascii=False),
|
||||
)
|
||||
db.session.add(plan)
|
||||
|
||||
+29
-5
@@ -33,7 +33,7 @@ def add_student_problem(student_id):
|
||||
|
||||
# 添加或更新问题
|
||||
for p in problems:
|
||||
problem_id = p.get("problem_id")
|
||||
problem_id = p.get("problem_id") # 这是 problems.id
|
||||
submitted_ids.add(problem_id)
|
||||
|
||||
# 检查是否已存在
|
||||
@@ -50,7 +50,6 @@ def add_student_problem(student_id):
|
||||
problem = StudentProblem(
|
||||
student_id=student_id,
|
||||
problem_id=problem_id,
|
||||
problem_name=p.get("problem_name"),
|
||||
severity=p.get("severity"),
|
||||
level=p.get("level"),
|
||||
)
|
||||
@@ -70,13 +69,38 @@ def clear_student_problems(student_id):
|
||||
|
||||
|
||||
@main_bp.route(
|
||||
"/api/students/<int:student_id>/problems/<problem_id>", methods=["DELETE"]
|
||||
"/api/students/<int:student_id>/problems/<int:student_problem_id>", methods=["DELETE"]
|
||||
)
|
||||
@login_required_json
|
||||
def delete_single_problem(student_id, problem_id):
|
||||
def delete_single_problem(student_id, student_problem_id):
|
||||
"""删除学员的单个问题"""
|
||||
StudentProblem.query.filter_by(
|
||||
student_id=student_id, problem_id=problem_id
|
||||
id=student_problem_id
|
||||
).delete()
|
||||
db.session.commit()
|
||||
return jsonify({"message": "删除成功"})
|
||||
|
||||
|
||||
@main_bp.route(
|
||||
"/api/students/<int:student_id>/problems/<int:student_problem_id>", methods=["PUT"]
|
||||
)
|
||||
@login_required_json
|
||||
def update_single_problem(student_id, student_problem_id):
|
||||
"""更新学员的单个问题(严重程度和级别)"""
|
||||
student = Student.query.get_or_404(student_id)
|
||||
data = request.get_json()
|
||||
|
||||
problem = StudentProblem.query.filter_by(
|
||||
id=student_problem_id
|
||||
).first()
|
||||
|
||||
if not problem:
|
||||
return jsonify({"error": "问题记录不存在"}), 404
|
||||
|
||||
if "severity" in data:
|
||||
problem.severity = data["severity"]
|
||||
if "level" in data:
|
||||
problem.level = data["level"]
|
||||
|
||||
db.session.commit()
|
||||
return jsonify({"message": "更新成功", "problem": problem.to_dict()})
|
||||
|
||||
+57
-198
@@ -5,6 +5,7 @@ import shutil
|
||||
from datetime import datetime
|
||||
from flask import request, jsonify, render_template, current_app, session, redirect
|
||||
from app.routes import main_bp
|
||||
from app.models import db, Problem, StudentProblem
|
||||
from app.config import load_api_config, save_api_config
|
||||
from app.routes.auth import login_required_json, admin_required
|
||||
|
||||
@@ -13,14 +14,14 @@ from app.routes.auth import login_required_json, admin_required
|
||||
@login_required_json
|
||||
def settings():
|
||||
"""问题配置页面 - 所有登录用户可访问"""
|
||||
return render_template("settings.html")
|
||||
return render_template("settings.html", active_nav="settings")
|
||||
|
||||
|
||||
@main_bp.route("/api-settings")
|
||||
@admin_required
|
||||
def api_settings_page():
|
||||
"""API设置页面 - 仅管理员"""
|
||||
return render_template("api_settings.html")
|
||||
return render_template("api_settings.html", active_nav="api-settings")
|
||||
|
||||
|
||||
# ==================== API配置接口 ====================
|
||||
@@ -81,241 +82,99 @@ def update_api_config():
|
||||
@main_bp.route("/api/problems", methods=["GET"])
|
||||
@login_required_json
|
||||
def get_problems():
|
||||
"""获取问题列表(从文件夹动态读取)"""
|
||||
problems_dir = current_app.config.get("PROBLEMS_DIR")
|
||||
"""获取问题列表(从数据库读取)"""
|
||||
from app.models import Problem
|
||||
|
||||
problems = []
|
||||
if problems_dir and os.path.exists(problems_dir):
|
||||
for filename in sorted(os.listdir(problems_dir)):
|
||||
if (
|
||||
filename.endswith(".md")
|
||||
and not filename.startswith("模板")
|
||||
and not filename.startswith("针对性练习建议")
|
||||
):
|
||||
name = filename.replace(".md", "")
|
||||
if "汇总" in name:
|
||||
continue
|
||||
|
||||
parts = name.split("_", 1)
|
||||
if len(parts) == 2:
|
||||
problem_id = parts[0]
|
||||
problem_name = parts[1]
|
||||
|
||||
category = "技术类"
|
||||
filepath = os.path.join(problems_dir, filename)
|
||||
try:
|
||||
with open(filepath, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
if "认知类" in content:
|
||||
category = "认知类"
|
||||
elif "节奏类" in content:
|
||||
category = "节奏类"
|
||||
elif "表现类" in content:
|
||||
category = "表现类"
|
||||
elif "习惯类" in content:
|
||||
category = "习惯类"
|
||||
elif "综合类" in content:
|
||||
category = "综合类"
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
mtime = os.path.getmtime(filepath)
|
||||
except:
|
||||
mtime = 0
|
||||
|
||||
problems.append(
|
||||
{
|
||||
"id": problem_id,
|
||||
"name": problem_name,
|
||||
"category": category,
|
||||
"file": filename,
|
||||
"mtime": mtime,
|
||||
}
|
||||
)
|
||||
|
||||
return jsonify(sorted(problems, key=lambda x: x["id"]))
|
||||
problems = Problem.query.order_by(Problem.no).all()
|
||||
return jsonify([p.to_dict() for p in problems])
|
||||
|
||||
|
||||
@main_bp.route("/api/problems/<problem_id>", methods=["GET"])
|
||||
@main_bp.route("/api/problems/<int:problem_id>", methods=["GET"])
|
||||
@login_required_json
|
||||
def get_problem_detail(problem_id):
|
||||
"""获取单个问题的详细信息"""
|
||||
problems_dir = current_app.config.get("PROBLEMS_DIR")
|
||||
from app.models import Problem
|
||||
|
||||
if problems_dir and os.path.exists(problems_dir):
|
||||
for filename in os.listdir(problems_dir):
|
||||
if filename.startswith(f"{problem_id}_") and filename.endswith(".md"):
|
||||
filepath = os.path.join(problems_dir, filename)
|
||||
with open(filepath, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
return jsonify(
|
||||
{"id": problem_id, "filename": filename, "content": content}
|
||||
)
|
||||
problem = Problem.query.get(problem_id)
|
||||
if not problem:
|
||||
return jsonify({"error": "问题不存在"}), 404
|
||||
|
||||
return jsonify({"error": "问题不存在"}), 404
|
||||
return jsonify({
|
||||
"id": problem.id,
|
||||
"no": problem.no,
|
||||
"name": problem.name,
|
||||
"category": problem.category,
|
||||
"content": problem.content,
|
||||
})
|
||||
|
||||
|
||||
@main_bp.route("/api/problems", methods=["POST"])
|
||||
@admin_required
|
||||
def create_problem():
|
||||
"""创建新问题 - 仅管理员"""
|
||||
"""创建新问题"""
|
||||
from app.models import Problem
|
||||
|
||||
data = request.get_json()
|
||||
problem_id = data.get("id", "").strip()
|
||||
problem_name = data.get("name", "").strip()
|
||||
no = data.get("no", "").strip()
|
||||
name = data.get("name", "").strip()
|
||||
category = data.get("category", "技术类")
|
||||
content = data.get("content", "")
|
||||
|
||||
if not problem_id or not problem_name:
|
||||
return jsonify({"error": "ID和名称不能为空"}), 400
|
||||
if not no or not name:
|
||||
return jsonify({"error": "编号和名称不能为空"}), 400
|
||||
|
||||
# 格式化ID
|
||||
problem_id = problem_id.zfill(2)
|
||||
# 检查编号是否已存在
|
||||
existing = Problem.query.filter_by(no=no).first()
|
||||
if existing:
|
||||
return jsonify({"error": "该编号已存在"}), 400
|
||||
|
||||
problems_dir = current_app.config.get("PROBLEMS_DIR")
|
||||
filename = f"{problem_id}_{problem_name}.md"
|
||||
filepath = os.path.join(problems_dir, filename)
|
||||
# 创建问题
|
||||
problem = Problem(no=no, name=name, category=category, content=content)
|
||||
db.session.add(problem)
|
||||
db.session.commit()
|
||||
|
||||
if os.path.exists(filepath):
|
||||
return jsonify({"error": "问题已存在"}), 400
|
||||
|
||||
# 生成默认内容
|
||||
content = f"""# {problem_name}
|
||||
|
||||
> 所属系列:钢琴学习常见问题针对性练习建议
|
||||
> 配合目标体系使用,针对性补齐短板
|
||||
|
||||
---
|
||||
|
||||
## 问题表现
|
||||
|
||||
- 请描述具体表现症状
|
||||
|
||||
## 原因分析
|
||||
|
||||
- 可能的原因1
|
||||
- 可能的原因2
|
||||
|
||||
## 针对性练习方案
|
||||
|
||||
### 日常基础练习
|
||||
|
||||
| 练习名称 | 时长 | 频率 | 目的 |
|
||||
|---------|------|------|------|
|
||||
| 练习1 | 10分钟 | 每天 | 目的1 |
|
||||
| 练习2 | 5分钟 | 每天 | 目的2 |
|
||||
|
||||
### 具体操作
|
||||
|
||||
```
|
||||
练习1:练习名称
|
||||
- 步骤1
|
||||
- 步骤2
|
||||
- 步骤3
|
||||
```
|
||||
|
||||
## 练习提醒
|
||||
|
||||
### ⚠️ 禁忌
|
||||
|
||||
- 禁忌1
|
||||
- 禁忌2
|
||||
|
||||
### ✓ 正确做法
|
||||
|
||||
- 正确做法1
|
||||
- 正确做法2
|
||||
|
||||
## 评估标准
|
||||
|
||||
| 等级 | 标准 |
|
||||
|------|------|
|
||||
| 入门 | 达到的标准 |
|
||||
| 进阶 | 达到的标准 |
|
||||
| 熟练 | 达到的标准 |
|
||||
| 精通 | 达到的标准 |
|
||||
|
||||
---
|
||||
|
||||
> **版本**:V1.0
|
||||
> **创建时间**:{datetime.now().strftime("%Y-%m-%d")}
|
||||
> **适用场景**:成人钢琴集体课学员个性化辅导"""
|
||||
|
||||
with open(filepath, "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
|
||||
return jsonify({"message": "创建成功", "id": problem_id, "name": problem_name})
|
||||
return jsonify({"message": "创建成功", "id": problem.id, "no": problem.no, "name": problem.name})
|
||||
|
||||
|
||||
@main_bp.route("/api/problems/<problem_id>", methods=["PUT"])
|
||||
@main_bp.route("/api/problems/<int:problem_id>", methods=["PUT"])
|
||||
@login_required_json
|
||||
def update_problem(problem_id):
|
||||
"""更新问题"""
|
||||
data = request.get_json()
|
||||
new_name = data.get("name", "").strip()
|
||||
new_content = data.get("content", "").strip()
|
||||
from app.models import Problem
|
||||
|
||||
if not new_name:
|
||||
return jsonify({"error": "名称不能为空"}), 400
|
||||
|
||||
problems_dir = current_app.config.get("PROBLEMS_DIR")
|
||||
|
||||
# 找到旧文件
|
||||
old_filename = None
|
||||
for f in os.listdir(problems_dir):
|
||||
if f.startswith(f"{problem_id}_") and f.endswith(".md"):
|
||||
old_filename = f
|
||||
break
|
||||
|
||||
if not old_filename:
|
||||
problem = Problem.query.get(problem_id)
|
||||
if not problem:
|
||||
return jsonify({"error": "问题不存在"}), 404
|
||||
|
||||
old_filepath = os.path.join(problems_dir, old_filename)
|
||||
new_filename = f"{problem_id}_{new_name}.md"
|
||||
new_filepath = os.path.join(problems_dir, new_filename)
|
||||
|
||||
# 如果名称改变,需要重命名文件
|
||||
if old_filename != new_filename:
|
||||
if os.path.exists(new_filepath):
|
||||
return jsonify({"error": "同名问题已存在"}), 400
|
||||
os.rename(old_filepath, new_filepath)
|
||||
filepath = new_filepath
|
||||
else:
|
||||
filepath = old_filepath
|
||||
|
||||
# 更新内容
|
||||
with open(filepath, "w", encoding="utf-8") as f:
|
||||
f.write(new_content)
|
||||
data = request.get_json()
|
||||
if "name" in data:
|
||||
problem.name = data["name"].strip()
|
||||
if "category" in data:
|
||||
problem.category = data["category"]
|
||||
if "content" in data:
|
||||
problem.content = data["content"]
|
||||
|
||||
db.session.commit()
|
||||
return jsonify({"message": "更新成功"})
|
||||
|
||||
|
||||
@main_bp.route("/api/problems/<problem_id>", methods=["DELETE"])
|
||||
@main_bp.route("/api/problems/<int:problem_id>", methods=["DELETE"])
|
||||
@admin_required
|
||||
def delete_problem(problem_id):
|
||||
"""删除问题"""
|
||||
problems_dir = current_app.config.get("PROBLEMS_DIR")
|
||||
from app.models import Problem
|
||||
|
||||
# 找到文件
|
||||
filename = None
|
||||
for f in os.listdir(problems_dir):
|
||||
if f.startswith(f"{problem_id}_") and f.endswith(".md"):
|
||||
filename = f
|
||||
break
|
||||
|
||||
if not filename:
|
||||
problem = Problem.query.get(problem_id)
|
||||
if not problem:
|
||||
return jsonify({"error": "问题不存在"}), 404
|
||||
|
||||
filepath = os.path.join(problems_dir, filename)
|
||||
# 检查是否有关联数据
|
||||
from app.models import StudentProblem
|
||||
if StudentProblem.query.filter_by(problem_db_id=problem_id).first():
|
||||
return jsonify({"error": "该问题已被学员使用,无法删除"}), 400
|
||||
|
||||
# 移动到备份目录
|
||||
trash_dir = os.path.join(problems_dir, "bk")
|
||||
os.makedirs(trash_dir, exist_ok=True)
|
||||
|
||||
import time
|
||||
|
||||
backup_name = f"{filename.replace('.md', '')}_{int(time.time())}.md"
|
||||
shutil.move(filepath, os.path.join(trash_dir, backup_name))
|
||||
db.session.delete(problem)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({"message": "删除成功"})
|
||||
|
||||
|
||||
+31
-2
@@ -15,18 +15,46 @@ from app.routes import main_bp
|
||||
from app.models import (
|
||||
db,
|
||||
Student,
|
||||
Class,
|
||||
PracticePlan,
|
||||
PROBLEM_LIST,
|
||||
SEVERITY_LEVELS,
|
||||
PRACTICE_TIME_OPTIONS,
|
||||
User,
|
||||
Class,
|
||||
)
|
||||
from app.routes.auth import login_required_json, check_login
|
||||
|
||||
|
||||
@main_bp.route("/")
|
||||
def index():
|
||||
"""首页 - 学员列表"""
|
||||
"""首页"""
|
||||
if not check_login():
|
||||
return redirect("/login")
|
||||
|
||||
student_count = Student.query.count()
|
||||
class_count = Class.query.count()
|
||||
plan_count = PracticePlan.query.count()
|
||||
|
||||
return render_template(
|
||||
"home.html",
|
||||
student_count=student_count,
|
||||
class_count=class_count,
|
||||
plan_count=plan_count,
|
||||
active_nav="home",
|
||||
)
|
||||
|
||||
|
||||
@main_bp.route("/student/<int:student_id>")
|
||||
@login_required_json
|
||||
def student_detail_page(student_id):
|
||||
"""学员详情页"""
|
||||
student = Student.query.get_or_404(student_id)
|
||||
return render_template("student.html", student=student, active_nav="students")
|
||||
|
||||
|
||||
@main_bp.route("/students")
|
||||
def students_page():
|
||||
"""学员列表页"""
|
||||
if not check_login():
|
||||
return redirect("/login")
|
||||
|
||||
@@ -35,6 +63,7 @@ def index():
|
||||
problem_list=PROBLEM_LIST,
|
||||
severity_levels=SEVERITY_LEVELS,
|
||||
practice_time_options=PRACTICE_TIME_OPTIONS,
|
||||
active_nav="students",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ def init_default_templates():
|
||||
def templates_page():
|
||||
"""模板管理页面"""
|
||||
from flask import render_template
|
||||
return render_template("templates.html")
|
||||
return render_template("templates.html", active_nav="templates")
|
||||
|
||||
|
||||
@templates_bp.route("/templates", methods=["GET"])
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ from app.routes.auth import login_required_json, admin_required
|
||||
@admin_required
|
||||
def users_page():
|
||||
"""用户管理页面"""
|
||||
return render_template("users.html")
|
||||
return render_template("users.html", active_nav="users")
|
||||
|
||||
|
||||
@main_bp.route("/api/users", methods=["GET"])
|
||||
|
||||
Reference in New Issue
Block a user