Files
memind/public/learn/index.html
T
john acca846856
Memind CI / Test, build, and release guards (pull_request) Failing after 4m17s
feat(seo-geo): 全页面 SEO/GEO 回填、统一检查与 Plaza 注入
引入批量回填与审计工具链,强制 mindspace-geo 元数据,扩展 sitemap/llms 收录 /learn,并为 Plaza 壳页注入 canonical/OG/JSON-LD;本地 593 页已通过 check:mindspace-cover。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-16 09:36:01 +08:00

139 lines
26 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<title>我的学习小助手</title>
<meta name="description" content="TKMind 儿童学习打卡与习惯养成助手,支持每日任务、成长记录与家长协同。">
<meta name="mindspace-cover" content="{&quot;tag&quot;:&quot;学习&quot;,&quot;emoji&quot;:&quot;📚&quot;,&quot;accent&quot;:&quot;#3b5bfd&quot;,&quot;accent2&quot;:&quot;#1c2333&quot;,&quot;subtitle&quot;:&quot;每日任务与成长记录&quot;,&quot;cover&quot;:&quot;https://m.tkmind.cn/brand/tkmind-icon.png&quot;}">
<meta name="mindspace-geo" content='{"summary":"TKMind 儿童学习打卡与习惯养成助手,帮助小朋友完成每日任务、查看成长记录,并与家长后台协同。","keywords":["学习打卡","儿童习惯","TKMind","成长记录","任务清单"],"faq":[{"q":"学习小助手能做什么?","a":"提供每日任务清单、完成打卡、历史记录、日历与成长统计,帮助培养学习习惯。"},{"q":"家长如何配置任务?","a":"家长可访问家长后台添加学员、设置任务模板与查看统计数据。"}]}'>
<meta name="mindspace-page-data-page-id" content="dcb1f3f0-db96-4dab-a009-7a9c393ebe77">
<script>window.__MINDSPACE_PAGE_DATA__={"pageId":"dcb1f3f0-db96-4dab-a009-7a9c393ebe77","accessMode":"public"};</script>
<style>*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}
body{font-family:-apple-system,BlinkMacSystemFont,"PingFang SC",sans-serif;background:#f2f4fa;color:#1c2333;min-height:100vh;padding-bottom:70px;padding-bottom:calc(70px + constant(safe-area-inset-bottom));padding-bottom:calc(70px + env(safe-area-inset-bottom))}
.wrap{max-width:560px;margin:0 auto;padding:16px 14px}
header.h{padding:16px 2px 4px}
h1{font-size:22px;font-weight:700}
.sub{font-size:13px;color:#6b7280;margin-top:3px}
.card{background:#fff;border-radius:16px;padding:16px;margin-top:12px;box-shadow:0 1px 3px rgba(28,35,51,.06)}
.big{font-size:26px;font-weight:700}
.bar{height:10px;background:#e8ebf2;border-radius:5px;overflow:hidden;margin-top:8px}
.bar i{display:block;height:100%;background:linear-gradient(90deg,#22c55e,#16a34a);border-radius:5px;transition:width .4s}
.cat{margin-top:14px}
.cat-title{font-size:13px;font-weight:700;padding:3px 10px;border-radius:999px;display:inline-flex;align-items:center;gap:5px}
.task{display:flex;align-items:center;gap:12px;padding:12px 2px;border-bottom:1px solid #f0f2f7;cursor:pointer}
.task:last-child{border-bottom:none}
.task .ck{width:26px;height:26px;border-radius:50%;border:2px solid #cbd5e1;flex:none;display:flex;align-items:center;justify-content:center;font-size:15px;color:#fff}
.task.done .ck{background:#16a34a;border-color:#16a34a}
.task .info{flex:1;min-width:0}
.task .t{font-size:15px;font-weight:600}
.task.done .t{color:#9ca3af;text-decoration:line-through}
.task .d{font-size:12px;color:#6b7280;margin-top:2px}
.task .pt{font-size:12px;color:#f59e0b;font-weight:700;flex:none}
.tabbar{position:fixed;left:0;right:0;bottom:0 !important;top:auto !important;background:#fff;border-top:1px solid #e5e8f0;display:flex;z-index:20;padding:6px 0 8px;padding:6px 0 calc(8px + constant(safe-area-inset-bottom));padding:6px 0 calc(8px + env(safe-area-inset-bottom))}
.overlay{top:0 !important;left:0 !important;right:0 !important;bottom:0 !important}
.tabbar a{flex:1;text-align:center;text-decoration:none;color:#9ca3af;font-size:11px;padding:4px 0}
.tabbar a.on{color:#3b5bfd;font-weight:700}
.tabbar .ic{font-size:20px;display:block}
button{font-family:inherit;border:none;cursor:pointer;border-radius:12px;font-size:14px;font-weight:600;padding:10px 16px;background:#3b5bfd;color:#fff}
button:active{transform:scale(.97)}
button.ghost{background:#e8edff;color:#3b5bfd}
button.danger{background:#ffe9ee;color:#e11d48}
button:disabled{opacity:.4;cursor:not-allowed}
input,select,textarea{font-family:inherit;width:100%;border:2px solid #e5e8f0;border-radius:10px;padding:10px 12px;font-size:15px;outline:none;background:#fff;color:#1c2333}
input:focus,select:focus,textarea:focus{border-color:#3b5bfd}
label{display:block;font-size:13px;font-weight:600;margin:12px 0 5px}
.overlay{position:fixed;inset:0;background:rgba(20,24,38,.5);z-index:40;display:flex;align-items:center;justify-content:center;padding:20px}
.modal{background:#fff;border-radius:18px;padding:24px;width:100%;max-width:360px;max-height:85vh;overflow:auto}
.statcheck{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:12px}
.sc{background:#fff;border-radius:14px;padding:13px 8px;text-align:center;box-shadow:0 1px 3px rgba(28,35,51,.06)}
.sc .n{font-size:20px;font-weight:700}
.sc .l{font-size:11px;color:#6b7280;margin-top:2px}
.cal{display:grid;grid-template-columns:repeat(7,1fr);gap:6px;margin-top:10px}
.cal .w{text-align:center;font-size:11px;color:#9ca3af;padding:4px 0}
.cal .cell{text-align:center;padding:7px 0;border-radius:10px;font-size:14px;position:relative}
.cal .cell.has{font-weight:700}
.cal .cell.g{background:#e7f8ee;color:#16a34a}.cal .cell.y{background:#fff4dd;color:#d97706}.cal .cell.r{background:#ffe9ee;color:#e11d48}.cal .cell.n{color:#c3c9d6}
.cal .today{outline:2px solid #3b5bfd}
.legend{display:flex;gap:12px;font-size:11px;color:#6b7280;margin-top:10px;flex-wrap:wrap}
.dot{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:4px;vertical-align:-1px}
.feed{position:fixed;left:50%;top:38%;transform:translate(-50%,-50%);background:#fff;border-radius:20px;padding:28px 30px;text-align:center;z-index:50;box-shadow:0 10px 40px rgba(28,35,51,.25);animation:pop .3s}
@keyframes pop{from{transform:translate(-50%,-50%) scale(.8);opacity:0}to{transform:translate(-50%,-50%) scale(1);opacity:1}}
.feed .em{font-size:44px}.feed .t1{font-size:18px;font-weight:700;margin-top:8px}.feed .t2{font-size:13px;color:#6b7280;margin-top:6px;line-height:1.6}
.feed .pt{color:#f59e0b;font-weight:700;margin-top:8px;font-size:15px}
table{width:100%;border-collapse:collapse;font-size:13px}
th,td{padding:8px 6px;border-bottom:1px solid #f0f2f7;text-align:left}
th{font-size:11px;color:#6b7280}
.row{display:flex;gap:10px;align-items:center}
.row>*{flex:1}
.pw{height:8px;background:#e8ebf2;border-radius:4px;overflow:hidden;min-width:60px}
.pw i{display:block;height:100%;background:#3b5bfd}
.chip{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;border-radius:999px;border:2px solid #e5e8f0;font-size:13px;cursor:pointer;margin:0 8px 8px 0;background:#fff}
.chip.on{border-color:#3b5bfd;background:#e8edff;color:#3b5bfd;font-weight:700}
.hbtn{display:inline-flex;align-items:center;gap:4px;font-size:12px;font-weight:600;color:#3b5bfd;background:#e8edff;padding:6px 13px;border-radius:999px;text-decoration:none;cursor:pointer;flex:none;white-space:nowrap}
.hbtn:active{transform:scale(.95)}
.err{background:#ffe9ee;color:#e11d48;padding:10px;border-radius:10px;font-size:13px;margin-top:10px;display:none}
.tabs{display:flex;gap:8px;margin-top:14px}
.tabs button{flex:1;background:#e8ebf2;color:#6b7280}
.tabs button.on{background:#3b5bfd;color:#fff}
.empty{text-align:center;color:#9ca3af;font-size:13px;padding:18px 0;line-height:1.7}</style>
<script src="/assets/page-data-client.js?v=20260915j"></script>
<script src="/learn/learning-assistant-data.js?v=20260915j"></script>
<link rel="icon" type="image/png" href="/brand/tkmind-icon.png">
<style id="mindspace-platform-brand-style">
[data-mindspace-page-tag="platform-brand"] {
display: block !important;
opacity: 1 !important;
visibility: visible !important;
color: #9aa3a0 !important;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif !important;
font-size: 12px !important;
font-weight: 500 !important;
letter-spacing: 0.02em !important;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
}
</style>
</head>
<body>
<div class="wrap" id="app"><div class="card"><div class="empty">加载中…</div></div></div>
<div class="tabbar" id="tabbar"></div>
<script>
var state={tab:'today',child:null,children:[],data:null,cal:null,grow:null};
function esc(s){return String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')}
function pickChild(){if(state.children.length<=1)return state.children[0]?state.children[0].id:1;var saved=localStorage.getItem('learn_child');var c=state.children.find(function(x){return String(x.id)===saved});if(!c){var s='<div class="card"><h1>我是谁呀?</h1>';state.children.forEach(function(x){s+='<div class="chip" style="margin-top:12px;font-size:16px;padding:12px 20px" onclick="chooseChild('+x.id+')">'+esc(x.emoji)+' '+esc(x.name)+'</div>'});s+='</div>';return s}return c.id}
function chooseChild(id){localStorage.setItem('learn_child',String(id));location.reload()}
function showSwitcher(){var s='<div class="overlay" onclick="if(event.target===this)this.remove()"><div class="modal"><h3 style="font-size:17px">👤 切换学员</h3><div style="margin-top:8px">';state.children.forEach(function(x){s+='<div class="chip" style="width:100%;justify-content:center;margin:8px 0 0;font-size:16px;padding:12px 20px'+(x.id==state.child?';border-color:#3b5bfd;background:#e8edff;font-weight:700':'')+'" onclick="chooseChild('+x.id+')">'+esc(x.emoji)+' '+esc(x.name)+(x.id==state.child?' ✓':'')+'</div>'});s+='</div><button class="ghost" style="margin-top:16px;width:100%" onclick="this.closest(\'.overlay\').remove()">取消</button></div></div>';document.body.insertAdjacentHTML('beforeend',s)}
function hdrActions(){var cur=state.children.find(function(x){return x.id==state.child});return '<div style="display:flex;gap:8px;flex:none;padding-top:6px"><span class="hbtn" onclick="showSwitcher()">⇄ '+(cur?'切换学员':'学员')+'</span><a class="hbtn" href="/learn/parent.html">家长端</a></div>'}
function greet(){var h=new Date().getHours();return h<11?'早上好':h<14?'中午好':h<18?'下午好':'晚上好'}
function pad(n){return n<10?'0'+n:''+n}
function wdCN(d){return ['日','一','二','三','四','五','六'][d]}
function dowOf(ds){return new Date(Date.UTC(+ds.slice(0,4),+ds.slice(5,7)-1,+ds.slice(8,10))).getUTCDay()}
function renderToday(){var d=state.data;var p=d.total?Math.round(d.done/d.total*100):0;var s='<header class="h"><div style="display:flex;justify-content:space-between;align-items:flex-start;gap:10px"><div><h1>'+greet()+''+esc(d.child.name)+' '+esc(d.child.emoji)+'</h1><div class="sub">'+d.date.replace(/-/g,'/')+' 周'+wdCN(dowOf(d.date))+'</div></div>'+hdrActions()+'</div></header><div class="card"><div class="row" style="align-items:baseline"><div><div class="big">'+d.done+' / '+d.total+'</div><div class="sub">今天已完成</div></div><div style="text-align:right"><div class="big" style="color:#f59e0b">⭐ '+d.energy+'</div><div class="sub">我的能量</div></div></div><div class="bar"><i style="width:'+p+'%"></i></div>'+(d.total&&d.done===d.total?'<div style="text-align:center;margin-top:10px;font-size:15px;font-weight:700;color:#16a34a">⭐ 今天全部完成,太棒啦!</div>':'')+'</div>';var byCat={},order=[];d.tasks.forEach(function(t){var k=t.category_name||'其他';if(!byCat[k]){byCat[k]={color:t.category_color||'#6b7280',list:[]};order.push(k)}byCat[k].list.push(t)});if(!d.tasks.length)s+='<div class="card"><div class="empty">今天没有任务<br>好好休息,或找爸爸妈妈安排~</div></div>';order.forEach(function(k){s+='<div class="cat"><span class="cat-title" style="background:'+byCat[k].color+'1a;color:'+byCat[k].color+'">'+esc(k)+'</span><div class="card">';byCat[k].list.forEach(function(t){var needVal=(t.task_type!=='check');var sub=[];if(t.description)sub.push(esc(t.description));if(needVal)sub.push('目标:'+esc(t.target_value||'')+(t.unit?esc(t.unit):''));if(t.start_date)sub.push('📅 '+t.start_date.replace(/-/g,'/')+' 开始');if(t.status==='done'){sub.unshift('✓ '+(t.completed_time||'')+' 完成'+(needVal&&t.value_submitted?''+esc(t.value_submitted)+(t.unit?esc(t.unit):''):''));}s+='<div class="task'+(t.status==='done'?' done':'')+'" onclick="doTask('+t.id+')"><div class="ck"'+(t.status==='pending'?' style="border-color:#e11d48"':'')+'>'+(t.status==='done'?'✓':'')+'</div><div class="info"><div class="t">'+esc(t.title)+'</div>'+(sub.length?'<div class="d">'+sub.join('<br>')+'</div>':'')+'</div><div class="pt">+'+t.points+'</div></div>'});s+='</div></div>'});if(d.streak>0)s+='<div class="card" style="text-align:center"><span style="font-size:15px">🔥 连续打卡 <b style="color:#f59e0b">'+d.streak+'</b> 天,继续保持!</span></div>';return s}
function renderCal(){var m=state.cal.month;var first=m+'-01';var dim=new Date(Date.UTC(+m.slice(0,4),+m.slice(5,7),0)).getUTCDate();var lead=dowOf(first);var map={};state.cal.days.forEach(function(x){map[x.date]=x});var s='<header class="h"><div style="display:flex;justify-content:space-between;align-items:flex-start;gap:10px"><div><h1>我的日历 📅</h1><div class="sub">'+m.replace('-',' 年 ')+' 月</div></div>'+hdrActions()+'</div></header><div class="card"><div style="display:flex;justify-content:space-between;align-items:center"><button class="ghost" style="padding:6px 14px" onclick="calShift(-1)">← 上月</button><button class="ghost" style="padding:6px 14px" onclick="calShift(1)">下月 →</button></div><div class="cal">';['日','一','二','三','四','五','六'].forEach(function(w){s+='<div class="w">'+w+'</div>'});for(var i=0;i<lead;i++)s+='<div></div>';var today=new Date(Date.now()+8*3600e3).toISOString().slice(0,10);for(var d=1;d<=dim;d++){var ds=m+'-'+pad(d);var st=map[ds];var cls='cell ';var mark='';if(st){cls+=st.status==='green'?'g':st.status==='yellow'?'y':st.status==='red'?'r':'n';if(st.total){cls+=' has';if(st.status==='red')mark='<i style="position:absolute;top:4px;right:5px;width:7px;height:7px;border-radius:50%;background:#e11d48;box-shadow:0 0 4px #e11d48"></i>';else if(st.status==='yellow')mark='<i style="position:absolute;top:4px;right:5px;width:7px;height:7px;border-radius:50%;background:#f59e0b"></i>';}}if(ds===today)cls+=' today';s+='<div class="'+cls+'" style="position:relative;'+(st&&st.status==='red'?'box-shadow:0 0 0 2px #e11d48 inset':'')+'" onclick="showDay(\''+ds+'\')">'+d+mark+'</div>'}s+='</div><div class="legend"><span><i class="dot" style="background:#16a34a"></i>全部完成</span><span><i class="dot" style="background:#f59e0b"></i>部分完成</span><span><i class="dot" style="background:#e11d48"></i>有遗漏(红点)</span><span><i class="dot" style="background:#c3c9d6"></i>无任务</span><div style="width:100%;text-align:center;margin-top:4px;color:#3b5bfd">点任意日期查看当天详情</div></div></div><div id="dayDetail"></div>';return s}
async function showDay(ds){var j=await LearnStore.getDay(state.child,ds);var el=document.getElementById('dayDetail');var isToday=ds===new Date(Date.now()+8*3600e3).toISOString().slice(0,10);var stTxt='';var stColor='';var done=j.tasks.filter(function(t){return t.status==='done'}).length;if(j.tasks.length){if(done===j.tasks.length){stTxt='全部完成 ✓';stColor='#16a34a'}else if(done>0){stTxt='完成 '+done+'/'+j.tasks.length+(ds<new Date(Date.now()+8*3600e3).toISOString().slice(0,10)?',有遗漏':'');stColor=ds<new Date(Date.now()+8*3600e3).toISOString().slice(0,10)?'#e11d48':'#d97706'}else{stTxt=ds<new Date(Date.now()+8*3600e3).toISOString().slice(0,10)?'全部未完成 ✕':'待完成';stColor=ds<new Date(Date.now()+8*3600e3).toISOString().slice(0,10)?'#e11d48':'#d97706'}}var s='<div class="card"><div class="row" style="align-items:center;margin-bottom:6px"><b style="font-size:15px;flex:1">'+ds.replace(/-/g,'/')+' 周'+wdCN(dowOf(ds))+(isToday?'(今天)':'')+'</b>'+(stTxt?'<span style="font-size:12px;font-weight:700;color:'+stColor+'">'+stTxt+'</span>':'')+'</div>';if(!j.tasks.length)s+='<div class="empty">那天没有任务</div>';j.tasks.forEach(function(t){var overdue=t.status==='pending'&&ds<new Date(Date.now()+8*3600e3).toISOString().slice(0,10);s+='<div class="task'+(t.status==='done'?' done':'')+'"><div class="ck"'+(overdue?' style="border-color:#e11d48;color:#e11d48;font-size:12px"':'')+'>'+(t.status==='done'?'✓':(overdue?'✕':''))+'</div><div class="info"><div class="t">'+esc(t.title)+'</div><div class="d"'+((overdue)?' style="color:#e11d48"':'')+'>'+esc(t.category_name||'')+(t.status==='done'?' · '+(t.completed_time||'')+' 完成':(overdue?' · 未完成':' · 待完成')+(t.value_submitted?' · '+esc(t.value_submitted)+(t.unit||''):''))+'</div></div></div>'});if(j.tasks.length)s+='<div class="sub" style="margin-top:10px">完成率:'+Math.round(done/j.tasks.length*100)+'%</div>';s+='</div>';el.innerHTML=s}
function renderGrow(){var g=state.grow;var s='<header class="h"><div style="display:flex;justify-content:space-between;align-items:flex-start;gap:10px"><div><h1>我的成长 🌱</h1></div>'+hdrActions()+'</div></header><div class="statcheck"><div class="sc"><div class="n" style="color:#f59e0b">⭐ '+g.energy+'</div><div class="l">我的能量</div></div><div class="sc"><div class="n" style="color:#ef4444">🔥 '+g.streak+'</div><div class="l">连续打卡</div></div><div class="sc"><div class="n">'+g.totalDone+'</div><div class="l">累计完成</div></div></div><div class="card"><div class="sub" style="margin-bottom:6px">💪 历史最长连续:<b>'+g.best+'</b> 天 全勤天数:<b>'+g.fullDays+'</b> 天</div></div><div class="card"><div class="t" style="font-weight:700;font-size:15px;margin-bottom:8px">近 30 天分类表现</div>';if(!g.byCategory.length)s+='<div class="empty">还没有记录,快去完成任务吧!</div>';g.byCategory.forEach(function(c){var p=c.total?Math.round(c.done/c.total*100):0;s+='<div style="margin:10px 0"><div class="row" style="align-items:center"><span style="flex:0 0 64px;font-size:13px;font-weight:600">'+esc(c.name)+'</span><div class="pw" style="flex:1"><i style="width:'+p+'%"></i></div><span style="flex:0 0 40px;text-align:right;font-size:12px;color:#6b7280">'+p+'%</span></div></div>'});s+='</div>';return s}
function renderHist(){var h=state.hist;var today=new Date(Date.now()+8*3600e3).toISOString().slice(0,10);var cycleTxt='';if(h.cycleStart){cycleTxt='<br>本期:'+h.cycleStart.replace(/-/g,'/')+' 起'+(h.cycleEnd?',至 '+h.cycleEnd.replace(/-/g,'/'):'(长期)')+' · 仅显示本期内有任务的日期'}var s='<header class="h"><div style="display:flex;justify-content:space-between;align-items:flex-start;gap:10px"><div><h1>我的记录 📖</h1><div class="sub">本期任务完成情况'+(cycleTxt||'(请爸爸妈妈先配置本期任务)')+'</div></div>'+hdrActions()+'</div></header>';if(!h.cycleStart)s+='<div class="card"><div class="empty">还没有本期任务<br>请爸爸妈妈先在家长后台配置</div></div>';else if(!h.data.length)s+='<div class="card"><div class="empty">本期还没有记录<br>完成今天的任务,就会出现在这里哦</div></div>';var daysAsc=h.data.slice().reverse();daysAsc.forEach(function(day,idx){var label=day.date===today?'今天':(day.date===addDaysLocal(today,-1)?'昨天':(parseInt(day.date.slice(5),10))+'月'+parseInt(day.date.slice(8),10)+'日');var w='周'+wdCN(dowOf(day.date));var stTxt=day.status==='green'?'全部完成':(day.done>0?'完成 '+day.done+'/'+day.total:(day.date<today?'有遗漏':'待完成'));var stColor=day.status==='green'?'#16a34a':day.status==='red'?'#e11d48':'#d97706';var stBg=day.status==='green'?'#e7f8ee':day.status==='red'?'#ffe9ee':'#fff4dd';var open=day.date===today;var bodyId='hb'+idx;var dot=day.status==='green'?'🟢':(day.status==='red'?'🔴':'🟡');s+='<div class="card"><div class="row" style="align-items:center;cursor:pointer" onclick="toggleDay(\''+bodyId+'\',this)"><div style="flex:1;cursor:pointer"><span style="font-size:16px;font-weight:700;cursor:pointer">'+label+'</span> <span class="sub">'+w+' '+dot+'</span></div><span style="font-size:12px;font-weight:700;color:'+stColor+';background:'+stBg+';padding:3px 10px;border-radius:999px;cursor:pointer">'+stTxt+'</span><span style="flex:0 0 16px;text-align:right;color:#9ca3af;font-size:12px;cursor:pointer" class="arr">'+(open?'▾':'▸')+'</span></div><div id="'+bodyId+'" style="'+(open?'':'display:none')+';margin-top:4px">';day.tasks.forEach(function(t){var sub=[];if(t.status==='done')sub.push('✓ '+(t.completed_time||'')+' 完成'+(t.task_type!=='check'&&t.value_submitted?''+esc(t.value_submitted)+(t.unit?esc(t.unit):''):''));else if(t.status==='pending'&&day.date<today)sub.push('✕ 未完成');else sub.push('待完成');if(t.remark)sub.push('📝 '+esc(t.remark));s+='<div class="task'+(t.status==='done'?' done':'')+'"><div class="ck">'+(t.status==='done'?'✓':(t.status==='pending'&&day.date<today?'✕':''))+'</div><div class="info"><div class="t">'+esc(t.title)+'</div><div class="d">'+esc(t.category_name||'')+' · '+sub.join(' · ')+'</div></div></div>'});s+='</div></div>'});return s}
function toggleDay(id,el){var b=document.getElementById(id);var arr=el.querySelector('.arr')||el.parentElement.querySelector('.arr');if(b.style.display==='none'){b.style.display='block';if(arr)arr.textContent='▾'}else{b.style.display='none';if(arr)arr.textContent='▸'}}
function addDaysLocal(ds,n){var d=new Date(ds+'T00:00:00Z');d.setUTCDate(d.getUTCDate()+n);return d.toISOString().slice(0,10)}
function renderTabbar(){var t=[['today','☀️','今日'],['hist','📖','记录'],['cal','📅','日历'],['grow','🌱','成长']];return t.map(function(x){return '<a href="javascript:void(0)" class="'+(state.tab===x[0]?'on':'')+'" onclick="go(\''+x[0]+'\')"><span class="ic">'+x[1]+'</span>'+x[2]+'</a>'}).join('')}
async function go(tab){state.tab=tab;await load();render()}
async function load(){if(!state.children.length){state.children=(await LearnStore.getChildren()).data}if(state.child==null){var c=pickChild();if(typeof c!=='number')return;state.child=c}if(state.child==null)return;if(state.tab==='today')state.data=await LearnStore.getToday(state.child);if(state.tab==='hist')state.hist=await LearnStore.getHistory(state.child);if(state.tab==='cal'&&!state.cal)state.cal=await LearnStore.getCalendar(state.child);if(state.tab==='grow')state.grow=await LearnStore.getGrowth(state.child)}
function render(){var app=document.getElementById('app');if(!state.children.length){app.innerHTML='<div class="card"><div class="empty">还没有小朋友的信息<br>请爸爸妈妈先去 <a href="/learn/parent.html" style="color:#3b5bfd">家长后台</a> 添加</div></div>';document.getElementById('tabbar').innerHTML='';return}if(state.child==null){app.innerHTML=pickChild();document.getElementById('tabbar').innerHTML='';return}var s=state.tab==='today'?renderToday():state.tab==='hist'?renderHist():state.tab==='cal'?renderCal():renderGrow();app.innerHTML=s;document.getElementById('tabbar').innerHTML=renderTabbar()}
async function calShift(n){var m=state.cal.month.split('-');var y=+m[0],mo=+m[1]+n;if(mo<1){mo=12;y--}if(mo>12){mo=1;y++}state.cal=await LearnStore.getCalendar(state.child,y+'-'+pad(mo));render()}
function doTask(id){var t=null;state.data.tasks.forEach(function(x){if(x.id===id)t=x});if(!t)(state.data.pastPending||[]).forEach(function(x){if(x.id===id)t=x});if(!t)return;if(t.status==='done')return;if(t.task_type==='check'){submit(id)}else{var unit=t.unit||'';var label=t.task_type==='count'?'完成了多少'+(unit?esc(unit):''):(t.task_type==='duration'?'坚持了多少'+(unit||'分钟'):'得了多少分');var def=t.value_submitted||(t.task_type==='duration'?(t.target_value||''):(t.target_value||''));document.getElementById('app').insertAdjacentHTML('beforeend','<div class="overlay" onclick="if(event.target===this)this.remove()"><div class="modal"><h3 style="font-size:17px">✏️ '+esc(t.title)+'</h3><label>'+label+'</label><input id="vInput" inputmode="numeric" value="'+esc(def)+'" placeholder="填一个数字吧"><label>想说的悄悄话(选填)</label><textarea id="vRemark" rows="2" style="font-size:14px"></textarea><div class="row" style="margin-top:16px"><button class="ghost" onclick="this.closest(\'.overlay\').remove()">再等等</button><button onclick="submitVal('+id+')">✓ 完成打卡</button></div></div></div>');var inp=document.getElementById('vInput');if(inp)inp.focus()}}
function submitVal(id){var v=document.getElementById('vInput').value;var r=document.getElementById('vRemark').value;submit(id,v,r)}
async function submit(id,value,remark){try{var j=await LearnStore.checkin(id,value,remark);var s='<div class="feed" onclick="this.remove()"><div class="em">🎉</div><div class="t1">完成啦!</div><div class="t2">今天已经完成 <b>'+j.done+'</b> / '+j.total+' 项<br>'+(j.allDone?'⭐ 太棒了,今天全部完成!':'再完成 '+(j.total-j.done)+' 项,今天就是全绿!')+'</div><div class="pt">+'+j.points+' 能量</div><div class="t2" style="margin-top:12px;font-size:11px;color:#c3c9d6">点一下继续</div></div>';document.body.insertAdjacentHTML('beforeend',s);await load();render()}catch(e){alert(e.message)}}
(async function(){try{await LearnStore.init();await load();render()}catch(e){document.getElementById('app').innerHTML='<div class="card"><div class="empty">'+esc(e.message)+'</div></div>'}})();
</script>
<p data-mindspace-page-tag="platform-brand">TKMind · 智趣</p>
<section data-mindspace-seo-outline="1" aria-label="页面提纲" style="margin:32px auto 20px;padding:20px 16px;max-width:920px;border-top:1px solid rgba(15,23,42,.08);color:#64748b;font-size:14px;line-height:1.75"><h2 style="font-size:17px;color:#334155;margin:0 0 8px">内容概要</h2>
<p style="margin:0 0 16px">TKMind 儿童学习打卡与习惯养成助手,帮助小朋友完成每日任务、查看成长记录,并与家长后台协同。</p>
<h2 style="font-size:17px;color:#334155;margin:16px 0 8px">学习小助手能做什么?</h2>
<p style="margin:0 0 12px">提供每日任务清单、完成打卡、历史记录、日历与成长统计,帮助培养学习习惯。</p>
<h2 style="font-size:17px;color:#334155;margin:16px 0 8px">家长如何配置任务?</h2>
<p style="margin:0 0 12px">家长可访问家长后台添加学员、设置任务模板与查看统计数据。</p></section>
</body>
</html>