UI 打磨与功能调整:中国生长标准、日历合并、弹窗去框、轮播记忆位置、小环居中、后台补卡与重置保留
This commit is contained in:
+56
-26
@@ -19,8 +19,8 @@
|
||||
<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 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>默认主题
|
||||
@@ -33,23 +33,10 @@
|
||||
<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>
|
||||
<p class="sub">当前年龄约 <b>{{ '%.1f'|format(age) }}</b> 岁 · 标准身高 <b>{{ std_h }} cm</b> · 标准体重 <b>{{ std_w }} kg</b>(中国儿童生长标准 WS/T 612-2018)</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>
|
||||
@@ -165,17 +152,60 @@
|
||||
|
||||
<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>
|
||||
<p class="sub">徽章由进度/读书/运动实时派生,重置后会自动重新累积,无需手动发徽章。</p>
|
||||
<form method="post" action="{{ url_for('admin_reset') }}"
|
||||
onsubmit="return confirm('确定重置数据?未勾选保留的项将被清空,此操作不可撤销!')">
|
||||
<div class="row" style="gap:18px;flex-wrap:wrap;align-items:flex-start">
|
||||
<label class="chk"><input type="checkbox" name="keep_health" value="1"> 保留健康记录(身高/体重/睡眠/运动)</label>
|
||||
<label class="chk"><input type="checkbox" name="keep_bookshelf" value="1"> 保留书架(书目与阅读记录)</label>
|
||||
</div>
|
||||
<div class="row" style="margin-top:12px">
|
||||
<button class="mini danger">🗑️ 重置数据</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>⚡ 浮动打卡项(可增删改)</h2>
|
||||
<p class="sub">仪表盘右下角的竖排打卡按钮;这里新增 / 改名 / 删除,仪表盘即时更新。</p>
|
||||
<form method="post" action="{{ url_for('admin_checkin_add') }}" class="row wrap">
|
||||
<input name="key" placeholder="标识,如 walk" required style="width:120px">
|
||||
<input name="icon" placeholder="图标" style="width:60px" value="🚶">
|
||||
<input name="name" placeholder="按钮名,如 走路打卡" required>
|
||||
<input name="done_name" placeholder="完成后文案,如 已走路" style="width:120px">
|
||||
<button>加一项</button>
|
||||
</form>
|
||||
{% for k in checkin_kinds %}
|
||||
<div class="admin-subject">
|
||||
<div class="admin-subject-head">
|
||||
<span>{{ k.icon }} <b>{{ k.name }}</b> <span class="muted">({{ k.key }} · 完成显示「{{ k.done_name }}」)</span></span>
|
||||
<span class="admin-subject-controls">
|
||||
<form method="post" action="{{ url_for('admin_checkin_update', kid=k.id) }}" class="inline row" style="margin:0">
|
||||
<input name="icon" value="{{ k.icon }}" style="width:46px">
|
||||
<input name="name" value="{{ k.name }}" style="width:120px">
|
||||
<input name="done_name" value="{{ k.done_name }}" style="width:110px">
|
||||
<button class="mini">保存</button>
|
||||
</form>
|
||||
<form method="post" action="{{ url_for('admin_checkin_delete', kid=k.id) }}" class="inline"
|
||||
onsubmit="return confirm('删除该打卡项及其打卡记录?')">
|
||||
<button class="mini danger">删除</button>
|
||||
</form>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>🗓️ 补卡(补录历史打卡)</h2>
|
||||
<p class="sub">为过去的某一天补记一次打卡;同一天同一项目不会重复记。仪表盘日历会显示该日已打卡。</p>
|
||||
<form method="post" action="{{ url_for('admin_checkin_backfill') }}" class="row wrap">
|
||||
<label>日期<input type="date" name="date" value="{{ now }}" required></label>
|
||||
<select name="kind">
|
||||
{% for k in checkin_kinds %}<option value="{{ k.key }}">{{ k.icon }} {{ k.name }}</option>{% endfor %}
|
||||
</select>
|
||||
<button>补卡</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -7,13 +7,6 @@
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<header class="topbar">
|
||||
<div class="brand">🌱 个人成长系统</div>
|
||||
<nav>
|
||||
<a href="{{ url_for('dashboard') }}">仪表盘</a>
|
||||
<a href="{{ url_for('admin') }}">家长后台</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main class="container">
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
+42
-34
@@ -21,18 +21,18 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<button type="button" class="mini" onclick="openBadges()">🏅 全部徽章</button>
|
||||
<button type="button" class="link-btn" onclick="openBadges()">🏅 点击查看全部徽章 ›</button>
|
||||
</div>
|
||||
<div class="hero-right">
|
||||
<div class="nowbox">
|
||||
<div class="now-date" id="nowDate">—</div>
|
||||
<div class="now-week" id="nowWeek">—</div>
|
||||
<div class="now-time" id="nowTime">--:--:--</div>
|
||||
<div class="now-left" id="nowLeft">{{ days_left_str }}</div>
|
||||
</div>
|
||||
<div class="termcal">
|
||||
<div class="termcal-title">📅 学期日历</div>
|
||||
<div class="termcal-range">{{ term_start_str }} ~ {{ term_end_str }}</div>
|
||||
<div class="termcal-scroll">{{ term_calendar_svg(active_counts, term_start, term_end) | safe }}</div>
|
||||
<div class="termcal-title">📅 日历 <span class="termcal-range-inline" id="calRange">{{ cal_start_str }} ~ {{ cal_end_str }}</span></div>
|
||||
<div class="termcal-scroll">{{ term_calendar_svg(active_counts, cal_start, cal_end) | safe }}</div>
|
||||
<div class="termcal-legend">
|
||||
<span><i class="lg tc-act3"></i>多项</span>
|
||||
<span><i class="lg tc-act1"></i>有打卡</span>
|
||||
@@ -42,14 +42,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-ring">
|
||||
{{ ring_svg(yprog, '#3b82f6', 130, '学年总进度') | safe }}
|
||||
{{ ring_svg(yprog, '#3b82f6', 130, '总进度') | safe }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-2">
|
||||
<section class="card">
|
||||
<h2>🎡 各学科进度一览</h2>
|
||||
<div class="ring-wrap">
|
||||
<div class="ringcar" id="ringCar" data-interval="{{ ring_sec }}">
|
||||
{% for grp in ring_slides %}
|
||||
@@ -68,42 +67,41 @@
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>💗 健康综合</h2>
|
||||
<div class="hstats">
|
||||
<div class="hstat">
|
||||
<div class="hs-label">当前身高</div>
|
||||
<div class="hs-val" style="color:#10b981">{{ cur_h if cur_h is not none else '—' }}<em>cm</em></div>
|
||||
<div class="hs-std">标准 {{ std_h }} cm
|
||||
{%- if cur_h is not none %}
|
||||
<span class="hs-diff {{ 'up' if cur_h >= std_h else 'down' }}">
|
||||
{{ '+' if cur_h >= std_h else '' }}{{ (cur_h - std_h) | round(1) }}
|
||||
</span>
|
||||
{%- endif %}
|
||||
<div class="hstat" onclick="openHealth()" title="点击查看健康图表详情">
|
||||
<div class="hs-label">身高</div>
|
||||
<div class="hs-main">
|
||||
<div class="hs-val" style="color:#10b981">{{ cur_h if cur_h is not none else '—' }}<em>cm</em></div>
|
||||
<div class="hs-std">标准 {{ std_h }} cm
|
||||
{%- if cur_h is not none %}
|
||||
<span class="hs-diff {{ 'up' if cur_h >= std_h else 'down' }}">
|
||||
{{ '+' if cur_h >= std_h else '' }}{{ (cur_h - std_h) | round(1) }}
|
||||
</span>
|
||||
{%- endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hstat">
|
||||
<div class="hs-label">当前体重</div>
|
||||
<div class="hs-val" style="color:#f59e0b">{{ cur_w if cur_w is not none else '—' }}<em>kg</em></div>
|
||||
<div class="hs-std">标准 {{ std_w }} kg
|
||||
{%- if cur_w is not none %}
|
||||
<span class="hs-diff {{ 'up' if cur_w >= std_w else 'down' }}">
|
||||
{{ '+' if cur_w >= std_w else '' }}{{ (cur_w - std_w) | round(1) }}
|
||||
</span>
|
||||
{%- endif %}
|
||||
<div class="hstat" onclick="openHealth()" title="点击查看健康图表详情">
|
||||
<div class="hs-label">体重</div>
|
||||
<div class="hs-main">
|
||||
<div class="hs-val" style="color:#f59e0b">{{ cur_w if cur_w is not none else '—' }}<em>kg</em></div>
|
||||
<div class="hs-std">标准 {{ std_w }} kg
|
||||
{%- if cur_w is not none %}
|
||||
<span class="hs-diff {{ 'up' if cur_w >= std_w else 'down' }}">
|
||||
{{ '+' if cur_w >= std_w else '' }}{{ (cur_w - std_w) | round(1) }}
|
||||
</span>
|
||||
{%- endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-foot">
|
||||
<button type="button" class="mini" onclick="openHealth()">📈 图表详情</button>
|
||||
<p class="sub tiny">按 {{ age }} 岁{{ '男孩' if (child.gender or 'M') == 'M' else '女孩' }}的生长参考中位数估算,仅供参考。</p>
|
||||
</div>
|
||||
<p class="sub tiny">按中国儿童生长标准(WS/T 612-2018){{ age }} 岁{{ '男孩' if (child.gender or 'M') == 'M' else '女孩' }}同龄中位数估算,仅供参考。</p>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="grid-2">
|
||||
<section class="card">
|
||||
<h2>📚 学科进度</h2>
|
||||
<p class="sub">按任务顺序显示:完成 3 个 · 进行中 1 个 · 待完成 3 个。点一下就完成,下一个自动接上。</p>
|
||||
<div class="carousel" id="subjCarousel" data-interval="{{ carousel_sec }}">
|
||||
{% for grp in slides %}
|
||||
<div class="slide" style="display:{% if loop.first %}block{% else %}none{% endif %}">
|
||||
@@ -113,7 +111,7 @@
|
||||
<span class="subject-icon">{{ s.icon }}</span>
|
||||
<span class="subject-name">{{ s.name }}</span>
|
||||
<span class="subject-pct">{{ s.done }}/{{ s.total }} · {{ s.progress }}%</span>
|
||||
<button type="button" class="mini detail-btn" onclick="openDetail({{ s.id }})">详情</button>
|
||||
<button type="button" class="link-btn" onclick="openDetail({{ s.id }})">点击查看详情 ›</button>
|
||||
</div>
|
||||
{{ bar_svg(s.progress, s.color) | safe }}
|
||||
<ul class="tasklist">
|
||||
@@ -162,9 +160,8 @@
|
||||
|
||||
<section class="card">
|
||||
<h2>📖 我的书架墙
|
||||
<button type="button" class="mini detail-btn" onclick="openShelf()">详情</button>
|
||||
<button type="button" class="link-btn" onclick="openShelf()">点击查看详情 ›</button>
|
||||
</h2>
|
||||
<p class="sub">共读完 {{ shelf|length }} 本 · 上排最近读完,下排随机回顾</p>
|
||||
{% if shelf %}
|
||||
<div class="shelf-mini">
|
||||
<div class="shelf-row-label">🆕 最近读完</div>
|
||||
@@ -294,17 +291,20 @@
|
||||
if (!root) return { next: function () {}, prev: function () {} };
|
||||
var slides = Array.prototype.slice.call(root.querySelectorAll(slideSel));
|
||||
var iv = (parseInt(root.dataset.interval, 10) || 8) * 1000;
|
||||
var LS = 'carIdx_' + rootId;
|
||||
var idx = 0, timer = null;
|
||||
try { var sv = parseInt(localStorage.getItem(LS), 10); if (!isNaN(sv)) idx = sv; } catch (e) {}
|
||||
var info = infoId ? document.getElementById(infoId) : null;
|
||||
function show(i) {
|
||||
if (!slides.length) return;
|
||||
idx = (i + slides.length) % slides.length;
|
||||
slides.forEach(function (el, k) { el.style.display = (k === idx) ? display : 'none'; });
|
||||
if (info) info.textContent = (idx + 1) + ' / ' + slides.length + (unit || '');
|
||||
try { localStorage.setItem(LS, idx); } catch (e) {}
|
||||
clearTimeout(timer);
|
||||
if (slides.length > 1) timer = setTimeout(function () { show(idx + 1); }, iv);
|
||||
}
|
||||
show(0);
|
||||
show(idx);
|
||||
return { next: function () { show(idx + 1); }, prev: function () { show(idx - 1); } };
|
||||
}
|
||||
|
||||
@@ -314,6 +314,14 @@
|
||||
function carouselNext() { subjCar.next(); }
|
||||
function carouselPrev() { subjCar.prev(); }
|
||||
|
||||
/* ---------- 学期 / 假期 日历:仪表盘只显示当前所在时段 ---------- */
|
||||
|
||||
/* ---------- 书架墙封面点击打开详情 ---------- */
|
||||
document.querySelectorAll('.shelf-mini .book-cover').forEach(function (el) {
|
||||
el.style.cursor = 'pointer';
|
||||
el.addEventListener('click', openShelf);
|
||||
});
|
||||
|
||||
/* ---------- 弹窗 ---------- */
|
||||
function openDetail(id) {
|
||||
var src = document.getElementById('detail-' + id);
|
||||
|
||||
Reference in New Issue
Block a user