小草待办清单_H5/app.js · 约 6042 字符
1// 小草待办清单 · 逻辑层
2const $=s=>document.querySelector(s), $$=s=>document.querySelectorAll(s);
3const store={
4 get tasks(){return JSON.parse(localStorage.getItem('td_tasks')||'[]')},
5 set tasks(v){localStorage.setItem('td_tasks',JSON.stringify(v))},
6 get habits(){return JSON.parse(localStorage.getItem('td_habits')||'[]')},
7 set habits(v){localStorage.setItem('td_habits',JSON.stringify(v))}
8};
9const dataTasks=()=>store.tasks.length?store.tasks:TASKS;
10const dataHabits=()=>store.habits.length?store.habits:HABITS;
11function toast(msg){const t=document.createElement('div');t.textContent=msg;t.style.cssText='position:fixed;top:20%;left:50%;transform:translateX(-50%);background:rgba(0,0,0,.78);color:#fff;padding:10px 20px;border-radius:20px;font-size:13px;z-index:999;transition:opacity .3s';document.body.appendChild(t);setTimeout(()=>{t.style.opacity='0';setTimeout(()=>t.remove(),300)},1200)}
12function toggleTask(id){
13 const t=dataTasks();const i=t.findIndex(x=>x.id==id);
14 if(i<0)return;t[i].done=!t[i].done;store.tasks=t;renderHome();
15}
16function renderHome(){
17 const t=dataTasks();const total=t.length;const done=t.filter(x=>x.done).length;
18 const pct=Math.round(done/total*100);
19 $('#th-ring').innerHTML='<b>'+done+'/'+total+'<small>已完成</small></b>';
20 $('#th-info').innerHTML='<b>今日 '+total+' 项 · 完成 '+pct+'%</b><span>高效的一天从待办开始 · 已连续打卡 12 天</span>';
21 const todo=t.filter(x=>!x.done).slice(0,5);
22 $('#task-list').innerHTML=todo.map(x=>`
23 <div class="task-item${x.done?' done':''}" onclick="toggleTask(${x.id})">
24 <span class="ti-check">${x.done?'✓':''}</span>
25 <div class="ti-body"><div class="ti-name">${x.name}</div><div class="ti-meta">${LISTS.find(l=>l.id===x.list).emoji} ${LISTS.find(l=>l.id===x.list).name} · ${x.time}</div></div>
26 <span class="ti-pri" style="background:${x.pri==='高'?'#fee2e2':x.pri==='中'?'#fef3c7':'#e0e7ff'}">${x.pri==='高'?'🔴':x.pri==='中'?'🟡':'🟢'}</span>
27 </div>`).join('')||'<div class="empty">今日待办已清空 🎉</div>';
28}
29function renderTasks(){
30 let cur='all';
31 function draw(){
32 const t=dataTasks();
33 const list=cur==='all'?t:t.filter(x=>x.list===cur);
34 const groups={};
35 list.forEach(x=>{const k=LISTS.find(l=>l.id===x.list).name;(groups[k]=groups[k]||[]).push(x)});
36 $('#task-list').innerHTML=Object.keys(groups).map(k=>
37 '<div class="list-group-title">'+k+'</div>'+
38 groups[k].map(x=>`
39 <div class="task-item${x.done?' done':''}" onclick="toggleTask(${x.id})">
40 <span class="ti-check">${x.done?'✓':''}</span>
41 <div class="ti-body"><div class="ti-name">${x.name}</div><div class="ti-meta">${x.time} · ${x.pri}优先级</div></div>
42 </div>`).join('')).join('');
43 }
44 $('#filter-row').innerHTML='<span class="filter-chip sel">全部</span>'+LISTS.map(l=>'<span class="filter-chip" data-id="'+l.id+'">'+l.emoji+' '+l.name+'</span>').join('');
45 $$('#filter-row span').forEach(c=>c.onclick=()=>{cur=c.dataset.id||'all';$$('#filter-row span').forEach(x=>x.classList.remove('sel'));c.classList.add('sel');draw()});
46 draw();
47}
48function renderCalendar(){
49 $('#cal-head').innerHTML='<span class="cal-nav" onclick="toast('已切换到上月')">‹</span><b>2026年8月</b><span class="cal-nav" onclick="toast('已切换到下月')">›</span>';
50 $('#cal-week').innerHTML=CAL_DAYS.map(d=>'<div class="cal-week">'+d+'</div>').join('');
51 let days='';let first=new Date(2026,7,1).getDay();
52 for(let i=0;i<first;i++)days+='<div class="cal-day dim"></div>';
53 for(let d=1;d<=31;d++){
54 const n=DAY_TASKS[d%7];
55 days+='<div class="cal-day'+(d===12?' today':'')+(n>0?' has-task':'')+'" onclick="toast(''+(n>0?'8月'+d+'日有'+n+'项待办':'8月'+d+'日无安排')+'')">'+d+(n>0?'<span class="cnt">'+n+'</span>':'')+'</div>';
56 }
57 $('#cal-grid').innerHTML=days;
58}
59/* 番茄钟 */
60let T={sec:25*60,run:false,timer:null,round:2};
61function fmtT(s){const m=Math.floor(s/60),sec=s%60;return String(m).padStart(2,'0')+':'+String(sec).padStart(2,'0')}
62function tick(){
63 if(T.sec<=0){clearInterval(T.timer);T.run=false;toast('🍅 番茄完成!休息5分钟');T.sec=5*60;$('#fh-state').textContent='休息中';return}
64 T.sec--;$('#fh-timer').textContent=fmtT(T.sec);
65}
66function toggleFocus(){
67 if(T.run){clearInterval(T.timer);T.run=false;$('#fh-state').textContent='已暂停'}
68 else{T.run=true;T.timer=setInterval(tick,1000);$('#fh-state').textContent='专注中 · 加油!'}
69}
70function renderFocus(){
71 $('#fh-timer').textContent=fmtT(T.sec);
72 $('#fh-round').textContent='今日第 '+T.round+' 个番茄 · 已完成 2/8';
73}
74function renderHabits(){
75 const h=dataHabits();
76 $('#habit-list').innerHTML=h.map(x=>`
77 <div class="habit-card">
78 <div class="hc-emoji">${x.emoji}</div>
79 <div class="hc-body"><div class="hc-name">${x.name}</div><div class="hc-meta">累计 ${x.total} 天 · 本周 ${x.done?7:4}/7</div></div>
80 <div class="hc-streak"><b>${x.streak}</b><span>连续天数</span></div>
81 <span class="hc-check${x.done?' done':''}" onclick="const h=dataHabits();const i=h.findIndex(y=>y.name==='${x.name}');if(i>=0){h[i].done=!h[i].done;store.habits=h;renderHabits()};toast('${x.done?'今日已打卡':'打卡成功!连续'+x.streak+'天'}')">${x.done?'✓':'✓'}</span>
82 </div>`).join('');
83}
84function renderMine(){
85 $('#u-name').textContent=USER.name;$('#u-level').textContent=USER.level;
86 $('#m-focus').textContent=USER.focus;$('#m-habit').textContent=USER.habit;
87 $('#m-total').textContent=USER.total;$('#m-points').textContent=USER.points;
88 $('#memo-list').innerHTML=MEMOS.map(m=>`<div class="task-item"><span class="ti-check" style="border:none;background:var(--primary-light)">${m.emoji}</span><div class="ti-body"><div class="ti-name">${m.name}</div><div class="ti-meta">${m.date} · 还有 ${m.days} 天</div></div><b style="color:var(--accent)">${m.days}天</b></div>`).join('');
89}
90