f9e105acd0
Serve lightweight child/parent learning assistant pages at /learn without MindSpace injection, with a rebuild script and Portal static route. Co-authored-by: Cursor <cursoragent@cursor.com>
117 lines
22 KiB
HTML
117 lines
22 KiB
HTML
<!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="mindspace-page-data-page-id" content="f9c80392-9417-46fd-af89-a91a239d3bfc">
|
||
<script>window.__MINDSPACE_PAGE_DATA__={"pageId":"f9c80392-9417-46fd-af89-a91a239d3bfc","accessMode":"password"};</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}body{padding-bottom:30px}.tabs{position:sticky;top:0;background:#f2f4fa;padding-top:2px;z-index:10}.pl{max-width:720px}</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">
|
||
</head>
|
||
<body>
|
||
<div class="wrap pl" id="app"><div class="card"><div class="empty">加载中…</div></div></div>
|
||
|
||
<script>
|
||
var state={pw:localStorage.getItem('learn_pw')||'',tab:'dash',stats:null,templates:null,categories:null,children:null,editId:null};
|
||
function esc(s){return String(s==null?'':s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')}
|
||
function pad(n){return n<10?'0'+n:''+n}
|
||
var WD=['一','二','三','四','五','六','日'];
|
||
function weekdaysCN(w){return LearnStore.parseWeekdays(w).map(function(x){return WD[+x-1]}).join('、')}
|
||
var TYPE={check:'完成型',count:'数量型',duration:'时长型',score:'成绩型'};
|
||
function renderLogin(){return '<header class="h"><h1>家长后台 🔐</h1><div class="sub">任务配置 · 数据统计 · 习惯分析</div></header><div class="card"><label>管理密码</label><input type="password" id="pw" placeholder="请输入密码" onkeydown="if(event.key===\'Enter\')login()"><div class="err" id="err"></div><button style="width:100%;margin-top:16px" onclick="login()">进入后台</button><div class="sub" style="text-align:center;margin-top:14px"><a href="/learn/" style="color:#3b5bfd">← 返回小朋友页面</a></div></div>'}
|
||
async function login(){state.pw=document.getElementById('pw').value;try{await LearnStore.verifyPassword(state.pw);localStorage.setItem('learn_pw',state.pw);state.tab='dash';await loadAll();render()}catch(e){var el=document.getElementById('err');el.style.display='block';el.textContent=e.message}}
|
||
function renderDash(){var s=state.stats;if(!s)return '<header class="h"><h1>数据看板 📊</h1></header><div class="card"><div class="empty">加载中…</div></div>';var tp=s.today.total?Math.round(s.today.done/s.today.total*100):0;var wp=s.week.total?Math.round(s.week.done/s.week.total*100):0;var st=s.children.length?s.children[0].streak:0;var html='<header class="h"><h1>数据看板 📊</h1><div class="sub">'+s.children.map(function(c){return esc(c.emoji)+' '+esc(c.name)+' 连续 '+c.streak+' 天🔥'}).join(' ')+'</div></header><div class="statcheck"><div class="sc"><div class="n" style="color:'+(tp===100?'#16a34a':'#3b5bfd')+'">'+tp+'%</div><div class="l">今日完成率 '+(s.today.done+'/'+s.today.total)+'</div></div><div class="sc"><div class="n">'+wp+'%</div><div class="l">本周完成率</div></div><div class="sc"><div class="n" style="color:#f59e0b">'+st+'</div><div class="l">连续全勤(天)</div></div></div><div class="card"><div class="t" style="font-weight:700;font-size:15px;margin-bottom:8px">近 7 天趋势</div>';s.trend.forEach(function(d){var p=d.total?Math.round(d.done/d.total*100):0;html+='<div style="margin:8px 0"><div class="row" style="align-items:center"><span style="flex:0 0 58px;font-size:12px;color:#6b7280">'+d.d.slice(5)+'</span><div class="pw" style="flex:1"><i style="width:'+p+'%;background:'+(p===100?'#16a34a':'#3b5bfd')+'"></i></div><span style="flex:0 0 40px;text-align:right;font-size:12px;color:#6b7280">'+p+'%</span></div></div>'});html+='</div>';if(s.missed.length){html+='<div class="card"><div class="t" style="font-weight:700;font-size:15px;margin-bottom:8px">⚠️ 近 30 天容易遗漏</div><table>';s.missed.forEach(function(m){html+='<tr><td>'+esc(m.title)+'</td><td style="color:#e11d48;font-weight:700;text-align:right">漏 '+m.count+' 次</td></tr>'});html+='</table></div>'}if(s.categoryRate.length){html+='<div class="card"><div class="t" style="font-weight:700;font-size:15px;margin-bottom:8px">📚 分类完成率(近 30 天)</div>';s.categoryRate.forEach(function(c){var p=c.total?Math.round(c.done/c.total*100):0;html+='<div style="margin:8px 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+'%;background:'+(p>=90?'#16a34a':p>=70?'#f59e0b':'#e11d48')+'"></i></div><span style="flex:0 0 40px;text-align:right;font-size:12px;color:#6b7280">'+p+'%</span></div></div>'});html+='</div>'}if(s.avgTime.length){html+='<div class="card"><div class="t" style="font-weight:700;font-size:15px;margin-bottom:8px">⏰ 平均完成时间(近 30 天)</div><table>';s.avgTime.forEach(function(a){html+='<tr><td>'+esc(a.name)+'</td><td style="text-align:right;font-weight:600">'+a.time+'</td></tr>'});html+='</table><div class="sub" style="margin-top:8px">提示:越晚完成的科目,遗漏概率往往越高</div></div>'}return html}
|
||
function childLabel(id){var c=state.children.find(function(x){return x.id===id});return c?esc(c.emoji)+' '+esc(c.name):''}
|
||
function renderTemplates(){var html='<header class="h"><h1>任务模板 📋</h1><div class="sub">配置周期规则,系统每天自动生成当日任务</div></header>';if(!state.templates||!state.templates.length)html+='<div class="card"><div class="empty">还没有任务模板<br>点下方按钮创建第一个</div></div>';state.templates.forEach(function(t){html+='<div class="card"><div class="row" style="align-items:flex-start"><div style="flex:1"><div style="font-size:16px;font-weight:700">'+esc(t.title)+'</div><div class="sub" style="margin-top:4px;line-height:1.7">👦 '+childLabel(t.child_id)+' '+(t.category_name?'【'+esc(t.category_name)+'】 ':'')+TYPE[t.task_type]+(t.target_value?' · '+esc(t.target_value)+(t.unit?esc(t.unit):''):'')+'<br>每周:'+weekdaysCN(t.weekdays)+' | 从 '+String(t.start_date).slice(0,10)+' 起'+(t.end_date?' 至 '+String(t.end_date).slice(0,10):'(长期)')+'<br>+'+t.points+' 能量'+(t.description?'<br>要求:'+esc(t.description):'')+'</div></div><div style="display:flex;flex-direction:column;gap:8px;flex:0 0 66px"><button class="ghost" style="padding:7px 10px;font-size:12px" onclick="editTpl('+t.id+')">编辑</button><button class="danger" style="padding:7px 10px;font-size:12px" onclick="delTpl('+t.id+',\''+esc(t.title).replace(/'/g,'')+'\')">停用</button></div></div></div>'});html+='<button style="width:100%;padding:14px;margin-top:14px" onclick="newTpl()">+ 新建任务模板</button>';return html}
|
||
function tplForm(t){var kids;if(t){var cur=state.children.find(function(c){return c.id===t.child_id});kids='<div style="margin-top:2px"><span class="chip on" style="cursor:default">'+esc(cur?cur.emoji+' '+cur.name:'')+'</span><input type="hidden" id="f_child" value="'+t.child_id+'"></div>'}else{kids='<div style="margin-top:2px">'+state.children.map(function(c){return '<span class="chip child on" data-id="'+c.id+'" onclick="this.classList.toggle(\'on\')">'+esc(c.emoji)+' '+esc(c.name)+'</span>'}).join('')+'</div>'}var cats=state.categories.map(function(c){return '<option value="'+c.id+'"'+(t&&t.category_id===c.id?' selected':'')+'>'+esc(c.name)+'</option>'}).join('');var wds=LearnStore.parseWeekdays(t?t.weekdays:'1,2,3,4,5');var wchk=WD.map(function(w,i){return '<span class="chip wd'+(wds.indexOf(String(i+1))>=0?' on':'')+'" onclick="this.classList.toggle(\'on\')">'+w+'</span>'}).join('');return '<div class="overlay" onclick="if(event.target===this)this.remove()"><div class="modal"><h3 style="font-size:17px;margin-bottom:4px">'+(t?'编辑任务模板':'新建任务模板')+'</h3><label>'+(t?'给谁安排':'给谁安排(可多选)')+'</label>'+kids+'<label>分类</label><select id="f_cat"><option value="">(无分类)</option>'+cats+'</select><label>任务名称 *</label><input id="f_title" value="'+(t?esc(t.title):'')+'" placeholder="如:英语单词复习" maxlength="60"><label>任务要求(孩子可见)</label><textarea id="f_desc" rows="2" maxlength="300" style="font-size:14px">'+(t?esc(t.description||''):'')+'</textarea><label>任务类型</label><select id="f_type">'+['check','count','duration','score'].map(function(k){return '<option value="'+k+'"'+(t&&t.task_type===k?' selected':'')+'>'+TYPE[k]+'</option>'}).join('')+'</select><div class="row"><div><label>目标值</label><input id="f_target" value="'+(t?esc(t.target_value||''):'')+'" placeholder="如 20" inputmode="numeric"></div><div><label>单位</label><input id="f_unit" value="'+(t?esc(t.unit||''):'')+'" placeholder="如 题/分钟/个"></div></div><label>重复的星期(点选)</label><div style="margin-top:2px">'+wchk+'</div><div class="row"><div><label>开始日期</label><input id="f_start" type="date" value="'+(t?String(t.start_date).slice(0,10):new Date().toISOString().slice(0,10))+'"></div><div><label>结束日期(可空)</label><input id="f_end" type="date" value="'+(t&&t.end_date?String(t.end_date).slice(0,10):'')+'"></div></div><label>能量值(完成奖励)</label><input id="f_points" type="number" value="'+(t?t.points:10)+'" min="0" max="100"><div class="err" id="f_err"></div><div class="row" style="margin-top:18px"><button class="ghost" onclick="this.closest(\'.overlay\').remove()">取消</button><button onclick="saveTpl('+(t?t.id:0)+')">保存</button></div></div></div>'}
|
||
function newTpl(){document.getElementById('app').insertAdjacentHTML('beforeend',tplForm(null))}
|
||
function editTpl(id){var t=state.templates.find(function(x){return x.id===id});if(t)document.getElementById('app').insertAdjacentHTML('beforeend',tplForm(t))}
|
||
async function delTpl(id,name){if(!confirm('停用「'+name+'」?已生成的历史任务保留,今后不再生成。'))return;try{await LearnStore.parentDelete('templates',id);await loadAll();render()}catch(e){alert(e.message||'停用失败')}}
|
||
async function saveTpl(id){var wds=[];document.querySelectorAll('.overlay .chip.wd.on').forEach(function(c){wds.push(String(WD.indexOf(c.textContent)+1))});var body={category_id:+document.getElementById('f_cat').value||null,title:document.getElementById('f_title').value,description:document.getElementById('f_desc').value,task_type:document.getElementById('f_type').value,target_value:document.getElementById('f_target').value,unit:document.getElementById('f_unit').value,weekdays:wds.join(','),start_date:document.getElementById('f_start').value,end_date:document.getElementById('f_end').value,points:+document.getElementById('f_points').value};try{if(!body.title)throw new Error('任务名称必填');if(!wds.length)throw new Error('请至少选择一个星期');if(id){body.child_id=+document.getElementById('f_child').value;await LearnStore.parentUpdate('templates',id,body)}else{var childIds=[];document.querySelectorAll('.overlay .chip.child.on').forEach(function(c){childIds.push(+c.getAttribute('data-id'))});if(!childIds.length)throw new Error('请至少选择一个小朋友');await LearnStore.parentInsertMany('templates',childIds.map(function(cid){return Object.assign({},body,{child_id:cid})}))}document.querySelector('.overlay').remove();await loadAll();render()}catch(e){var el=document.getElementById('f_err');el.style.display='block';el.textContent=e.message}}
|
||
function renderCats(){var html='<header class="h"><h1>分类管理 🏷️</h1><div class="sub">学习 + 习惯,不限语数外</div></header><div class="card"><table>';state.categories.forEach(function(c){html+='<tr><td><span class="dot" style="background:'+esc(c.color)+'"></span>'+esc(c.name)+'</td><td style="text-align:right"><button class="danger" style="padding:5px 10px;font-size:12px" onclick="delCat('+c.id+',\''+esc(c.name)+'\')">删除</button></td></tr>'});html+='</table></div><div class="card"><label>新增分类</label><div class="row"><input id="c_name" placeholder="如:练琴" maxlength="10"><input id="c_color" type="color" style="flex:0 0 56px;padding:4px" value="#3b5bfd"><button style="flex:0 0 70px" onclick="addCat()">添加</button></div></div>';return html}
|
||
async function addCat(){var n=document.getElementById('c_name').value.trim();if(!n)return;await LearnStore.parentInsert('categories',{name:n,color:document.getElementById('c_color').value});await loadAll();render()}
|
||
async function delCat(id,name){if(!confirm('删除分类「'+name+'」?使用它的模板将变为无分类。'))return;try{await LearnStore.parentDelete('categories',id);await loadAll();render()}catch(e){alert(e.message||'删除失败')}}
|
||
function renderKids(){var html='<header class="h"><h1>孩子管理 👦</h1><div class="sub">儿童端选择孩子后进入</div></header>';state.children.forEach(function(c){html+='<div class="card"><div class="row"><div style="flex:1;font-size:16px;font-weight:700">'+esc(c.emoji)+' '+esc(c.name)+'</div><button class="danger" style="flex:0 0 66px;padding:7px 0;font-size:12px" onclick="delKid('+c.id+',\''+esc(c.name)+'\')">删除</button></div></div>'});html+='<div class="card"><label>添加孩子</label><div class="row"><input id="k_emoji" placeholder="😀" style="flex:0 0 64px" maxlength="2"><input id="k_name" placeholder="昵称,如:小宇" maxlength="10"><button style="flex:0 0 70px" onclick="addKid()">添加</button></div></div>';return html}
|
||
async function addKid(){var n=document.getElementById('k_name').value.trim();if(!n)return;await LearnStore.parentInsert('children',{name:n,emoji:document.getElementById('k_emoji').value||'🙂'});await loadAll();render()}
|
||
async function delKid(id,name){if(!confirm('删除「'+name+'」及其所有任务记录?此操作不可恢复!'))return;try{await LearnStore.parentDelete('children',id);await loadAll();render()}catch(e){alert(e.message||'删除失败')}}
|
||
function renderSet(){return '<header class="h"><h1>设置 ⚙️</h1></header><div class="card"><label>修改管理密码</label><input type="password" id="p_old" placeholder="当前密码"><input type="password" id="p_new" placeholder="新密码(至少3位)" style="margin-top:8px"><div class="err" id="p_err"></div><button style="width:100%;margin-top:14px" onclick="chpw()">保存新密码</button></div><div class="card"><div class="sub" style="line-height:1.8">· 儿童端地址:<a href="/learn/" style="color:#3b5bfd">打开</a><br>· 任务每天首次访问时自动生成<br>· 历史任务保存快照,改模板不影响过去记录<br>· 数据存储:Page Data(口令默认 88888888)</div></div><button class="danger" style="width:100%;padding:12px" onclick="logout()">退出登录</button>'}
|
||
async function chpw(){try{await LearnStore.parentPassword(document.getElementById('p_old').value,document.getElementById('p_new').value);alert('密码已更新');state.pw=document.getElementById('p_new').value;localStorage.setItem('learn_pw',state.pw)}catch(e){var el=document.getElementById('p_err');el.style.display='block';el.textContent=e.message}}
|
||
function logout(){localStorage.removeItem('learn_pw');if(window.LearnStore&&LearnStore.clearAuth)LearnStore.clearAuth();state.pw='';render()}
|
||
async function loadAll(){if(state.tab==='dash')state.stats=await LearnStore.parentStats();if(state.tab==='tpl'){state.templates=(await LearnStore.parentList('templates')).data;state.categories=(await LearnStore.parentList('categories')).data;state.children=(await LearnStore.parentList('children')).data}if(state.tab==='cat'){state.categories=(await LearnStore.parentList('categories')).data}if(state.tab==='kid'){state.children=(await LearnStore.parentList('children')).data}}
|
||
function render(){var app=document.getElementById('app');if(!state.pw){app.innerHTML=renderLogin();return}var tabs=[['dash','📊','看板'],['tpl','📋','模板'],['cat','🏷️','分类'],['kid','👦','孩子'],['set','⚙️','设置']];var body=state.tab==='dash'?renderDash():state.tab==='tpl'?renderTemplates():state.tab==='cat'?renderCats():state.tab==='kid'?renderKids():renderSet();app.innerHTML=body+'<div class="tabs">'+tabs.map(function(t){return '<button class="'+(state.tab===t[0]?'on':'')+'" onclick="go(\''+t[0]+'\')">'+t[1]+' '+t[2]+'</button>'}).join('')+'</div>'}
|
||
async function go(tab){state.tab=tab;try{await loadAll();render()}catch(e){state.pw='';localStorage.removeItem('learn_pw');render()}}
|
||
(async function(){if(state.pw){try{await LearnStore.init({password:state.pw});await loadAll()}catch(e){state.pw='';localStorage.removeItem('learn_pw')}}try{render()}catch(e){document.getElementById('app').innerHTML='<div class="card"><div class="empty">'+esc(e.message)+'</div></div>'}})();
|
||
</script>
|
||
</body>
|
||
</html>
|