星火纪元 个人成长追踪 v1.0
- Flask + SQLite 自托管,单用户无 PIN - 仪表盘:学科进度环轮播、健康双线图、任务接龙、书架墙、浮动打卡 - 后台:孩子信息、学科/单元课表、健康历史录入、数据管理(重置/新学期) - 徽章由进度实时派生;主题由后台设置
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}家长后台{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class="admin-head">
|
||||
<h1>🛠️ 家长后台</h1>
|
||||
<a href="{{ url_for('admin_export') }}" class="btn-outline">⬇ 导出全部数据(JSON)</a>
|
||||
<a href="{{ url_for('admin_logout') }}" class="btn-outline">退出</a>
|
||||
</div>
|
||||
|
||||
<section class="card">
|
||||
<h2>👦 孩子信息</h2>
|
||||
<form method="post" action="{{ url_for('admin_child_update') }}" class="row wrap">
|
||||
<label>姓名<input name="name" value="{{ child.name }}" placeholder="姓名"></label>
|
||||
<label>出生日期<input type="date" name="birth_date" value="{{ child.birth_date or '' }}"></label>
|
||||
<label>性别
|
||||
<select name="gender">
|
||||
<option value="M" {% if child.gender == 'M' %}selected{% endif %}>男</option>
|
||||
<option value="F" {% if child.gender == 'F' %}selected{% endif %}>女</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>学期起<input type="date" name="term_start" value="{{ child.term_start or '' }}"></label>
|
||||
<label>学期止<input type="date" name="term_end" value="{{ child.term_end or '' }}"></label>
|
||||
<label>进度环轮播(秒)<input name="ring_sec" type="number" value="{{ child.ring_sec or 6 }}" style="width:80px" min="2"></label>
|
||||
<label>学科轮播(秒)<input name="carousel_sec" type="number" value="{{ child.carousel_sec or 8 }}" style="width:80px" min="2"></label>
|
||||
<label>默认主题
|
||||
<select name="default_theme">
|
||||
{% for t,label in themes %}
|
||||
<option value="{{ t }}" {% if child.default_theme == t %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<button>保存</button>
|
||||
</form>
|
||||
{% if age is not none %}
|
||||
<p class="sub">当前年龄约 <b>{{ '%.1f'|format(age) }}</b> 岁 · 标准身高 <b>{{ std_h }} cm</b> · 标准体重 <b>{{ std_w }} kg</b>(WHO 中位参考)</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>🎯 年度目标</h2>
|
||||
<form method="post" action="{{ url_for('admin_goal_update') }}" class="row">
|
||||
<label>读书目标(本/年)
|
||||
<input name="book_target" type="number" value="{{ goal.book_target }}" style="width:90px">
|
||||
</label>
|
||||
<label>运动目标(分钟/周)
|
||||
<input name="week_exercise_min" type="number" value="{{ goal.week_exercise_min }}" style="width:90px">
|
||||
</label>
|
||||
<button>保存</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>📈 健康记录(可补历史数据)</h2>
|
||||
<p class="sub">填入任意日期即可补录历史身高体重;仪表盘「健康综合」会展示最近两年。</p>
|
||||
<form method="post" action="{{ url_for('health_add') }}" class="row wrap">
|
||||
<label>日期<input type="date" name="date" value="{{ now }}" required></label>
|
||||
<label>身高(cm)<input name="height_cm" type="number" step="0.1" placeholder="如 130.5" style="width:90px"></label>
|
||||
<label>体重(kg)<input name="weight_kg" type="number" step="0.1" placeholder="如 27.2" style="width:90px"></label>
|
||||
<label>睡眠(时)<input name="sleep_hours" type="number" step="0.1" style="width:80px"></label>
|
||||
<label>喝水(杯)<input name="water_cups" type="number" style="width:70px"></label>
|
||||
<label>心情<input name="mood" placeholder="如 开心" style="width:90px"></label>
|
||||
<button>添加记录</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>📚 学科与单元(整年课表)</h2>
|
||||
<form method="post" action="{{ url_for('admin_subject_add') }}" class="row">
|
||||
<input name="name" placeholder="学科名,如 语文" required>
|
||||
<input name="icon" placeholder="图标" style="width:70px" value="📘">
|
||||
<input name="color" type="color" value="#3b82f6" style="width:50px">
|
||||
<button>加学科</button>
|
||||
</form>
|
||||
|
||||
{% for s in subjects %}
|
||||
<div class="admin-subject">
|
||||
<div class="admin-subject-head">
|
||||
<span>{{ s.icon }} <b>{{ s.name }}</b></span>
|
||||
<span class="admin-subject-controls">
|
||||
<form method="post" action="{{ url_for('admin_subject_delete', sid=s.id) }}" class="inline"
|
||||
onsubmit="return confirm('删除该学科及其所有单元?')">
|
||||
<button class="mini danger">删除学科</button>
|
||||
</form>
|
||||
</span>
|
||||
</div>
|
||||
<ul class="admin-units">
|
||||
{% for u in s.units %}
|
||||
<li>
|
||||
<span class="unit-state unit-{{ u.status }}">
|
||||
{% if u.status==2 %}已完成{% elif u.status==1 %}进行中{% else %}未开始{% endif %}
|
||||
</span>
|
||||
{{ u.title }}
|
||||
{% if u.target_date %}<span class="muted">(🎯{{ u.target_date }})</span>{% endif %}
|
||||
<form method="post" action="{{ url_for('admin_unit_delete', uid=u.id) }}" class="inline"
|
||||
onsubmit="return confirm('删除该单元?')">
|
||||
<button class="mini danger">×</button>
|
||||
</form>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<form method="post" action="{{ url_for('admin_unit_add') }}" class="row">
|
||||
<input type="hidden" name="subject_id" value="{{ s.id }}">
|
||||
<input name="title" placeholder="新增单元/章节名" required>
|
||||
<input name="target_date" type="date" placeholder="目标完成日">
|
||||
<button>加单元</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>📖 书目(含封面)</h2>
|
||||
<form method="post" action="{{ url_for('book_add') }}" class="row" enctype="multipart/form-data">
|
||||
<input name="title" placeholder="书名" required>
|
||||
<input name="author" placeholder="作者">
|
||||
<input name="total_pages" type="number" placeholder="页数" style="width:80px">
|
||||
<select name="category">
|
||||
{% for c in book_categories %}<option value="{{ c }}">{{ c }}</option>{% endfor %}
|
||||
</select>
|
||||
<input type="file" name="cover" accept="image/*" style="width:170px">
|
||||
<button>加书</button>
|
||||
</form>
|
||||
<p class="sub">支持本地上传封面(png/jpg/gif/webp,<b>不限制大小</b>,离线可见);上传后会<b>自动标准化</b>为统一的 2:3 封面并生成缩略图。也可在下方每本书填图片 URL。<br>
|
||||
<b>分类与评分</b>决定书架墙上的筛选标签和排序,改完记得点「保存」。</p>
|
||||
<table class="tbl">
|
||||
<tr><th>封面</th><th>书名</th><th>作者</th><th>状态</th><th>分类 / 评分</th><th>更新封面</th></tr>
|
||||
{% for b in books %}
|
||||
<tr>
|
||||
<td>{% if b.cover_thumb or b.cover_url %}<img src="{{ b.cover_thumb or b.cover_url }}" alt="" style="height:60px;border-radius:3px">{% else %}<span class="muted">无</span>{% endif %}</td>
|
||||
<td>{{ b.title }}</td>
|
||||
<td>{{ b.author or '—' }}</td>
|
||||
<td>{% if b.status=='done' %}已读完{% elif b.status=='reading' %}在读{% else %}想读{% endif %}</td>
|
||||
<td>
|
||||
<form method="post" action="{{ url_for('admin_book_meta', bid=b.id) }}" class="row" style="margin:0">
|
||||
<select name="category">
|
||||
{% for c in book_categories %}
|
||||
<option value="{{ c }}" {% if (b.category or '未分类') == c %}selected{% endif %}>{{ c }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<select name="rating" title="评分">
|
||||
{% for r in range(6) %}
|
||||
<option value="{{ r }}" {% if (b.rating or 0) == r %}selected{% endif %}>{{ r }}★</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button class="mini">保存</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form method="post" action="{{ url_for('admin_book_cover', bid=b.id) }}" enctype="multipart/form-data" class="row" style="margin:0">
|
||||
<input type="file" name="cover" accept="image/*" style="width:150px">
|
||||
<input name="cover_url" placeholder="或图片URL" value="{{ b.cover_url or '' }}" style="width:130px">
|
||||
<button class="mini">设封面</button>
|
||||
</form>
|
||||
{% if b.cover_url and b.cover_url.startswith('/static/covers/') %}
|
||||
<form method="post" action="{{ url_for('admin_book_cover_delete', bid=b.id) }}" class="inline" style="margin-top:6px" onsubmit="return confirm('删除这张封面?')">
|
||||
<button class="mini danger">删除封面</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>🧹 数据管理</h2>
|
||||
<p class="sub">徽章由进度/读书/运动实时派生,重置或新学期后会自动重新累积,无需手动发徽章。</p>
|
||||
<div class="row" style="gap:12px;flex-wrap:wrap">
|
||||
<form method="post" action="{{ url_for('admin_reset') }}"
|
||||
onsubmit="return confirm('确定清空全部进度 / 打卡 / 健康记录?学科与书目结构保留。此操作不可撤销!')">
|
||||
<button class="mini danger">🗑️ 重置全部数据</button>
|
||||
</form>
|
||||
<form method="post" action="{{ url_for('admin_new_term') }}"
|
||||
onsubmit="return confirm('开启新学期:进度重置为未开始、清空打卡,并把学期起设为今天。确定?')">
|
||||
<button class="mini">🎒 开启新学期</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user