首页/ mobileweb/ 体温记录日历 H5/ 代码预览

💻 代码预览 · 体温记录日历 H5

mw_medical · 上传者 驰旭教育
← 返回详情
js/main.js · 约 10345 字符
1(function(){
2  var KEY_R='temp_records_r13', KEY_S='temp_settings_r13', KEY_SEED='temp_seeded_r13';
3  // 默认阈值(口腔):正常 36.0~37.2;低热>=37.3;高热>=38.1;<36.0 偏低;<35.5 过低
4  var DEF={low:36.0, high:37.2, feverLo:37.3, feverHi:38.1, hypo:35.5};
5  function loadRec(){ try{return JSON.parse(localStorage.getItem(KEY_R))||{};}catch(e){return {};} }
6  function saveRec(o){ localStorage.setItem(KEY_R, JSON.stringify(o)); }
7  function loadSet(){ try{return Object.assign({},DEF,JSON.parse(localStorage.getItem(KEY_S))||{});}catch(e){return Object.assign({},DEF);} }
8  function saveSet(o){ localStorage.setItem(KEY_S, JSON.stringify(o)); }
9  function pad(n){return n<10?'0'+n:''+n;}
10  function iso(y,m,d){return y+'-'+pad(m+1)+'-'+pad(d);}
11  function todayISO(){var t=new Date();return iso(t.getFullYear(),t.getMonth(),t.getDate());}
12
13  // 体温分级
14  function grade(t){
15    var s=loadSet();
16    if(t< s.hypo)   return {cls:'s-lo',     label:'体温过低',ab:1,color:'#3b82f6'};
17    if(t< s.low)    return {cls:'s-lo',     label:'偏低',    ab:1,color:'#3b82f6'};
18    if(t<=s.high)   return {cls:'s-normal', label:'正常',    ab:0,color:'#16a34a'};
19    if(t< s.feverHi)return {cls:'s-feverlo',label:'低热',    ab:1,color:'#f97316'};
20    return                 {cls:'s-feverhi',label:'高热',    ab:1,color:'#ef4444'};
21  }
22
23  var R=loadRec();
24  var view=new Date(); view.setDate(1);
25  var cur=todayISO();
26  var sel=cur;
27
28  function seedDemo(){
29    if(localStorage.getItem(KEY_SEED)) return;
30    var t=new Date(), y=t.getFullYear(), m=t.getMonth();
31    function add(d,tmp,site,time,note){
32      var k=iso(y,m,d);
33      if(!R[k]) R[k]=[];
34      R[k].push({t:tmp,site:site,time:time,note:note,ts:Date.now()});
35    }
36    add(2,36.5,'口腔','早','精神好');
37    add(4,37.8,'口腔','晚','有点乏力');      // 低热
38    add(6,38.6,'口腔','晚','头痛、畏寒');     // 高热
39    add(9,36.2,'腋下','早','');
40    add(11,38.2,'口腔','午','喉咙痛');        // 高热
41    add(14,35.2,'额温','早','手脚发凉');       // 体温过低
42    add(16,36.8,'口腔','早','正常');
43    add(18,37.5,'口腔','晚','轻微出汗');       // 低热
44    add(21,36.4,'口腔','早','');
45    add(24,39.0,'口腔','晚','持续高热');       // 高热
46    add(27,36.6,'腋下','早','');
47    saveRec(R); localStorage.setItem(KEY_SEED,'1');
48  }
49
50  function toast(msg){
51    var el=document.createElement('div'); el.className='toast'; el.textContent=msg;
52    document.body.appendChild(el);
53    requestAnimationFrame(function(){el.classList.add('show');});
54    setTimeout(function(){el.classList.remove('show'); setTimeout(function(){el.remove();},300);},1700);
55  }
56
57  function renderStats(){
58    var y=view.getFullYear(), m=view.getMonth();
59    var days=0, abn=0, hi=0, lo=999;
60    Object.keys(R).forEach(function(k){
61      if(k.indexOf(y+'-'+pad(m+1))!==0) return;
62      var arr=R[k]; if(!arr.length) return;
63      days++;
64      arr.forEach(function(r){ if(grade(r.t).ab) abn++; if(r.t>hi) hi=r.t; if(r.t<lo) lo=r.t; });
65    });
66    document.getElementById('stDays').textContent=days;
67    document.getElementById('stAbn').textContent=abn;
68    document.getElementById('stHi').textContent= hi>0? hi.toFixed(1)+'°':'—';
69    document.getElementById('stLo').textContent= lo<999? lo.toFixed(1)+'°':'—';
70    document.querySelector('.stats .warn').style.visibility = abn>0?'visible':'hidden';
71  }
72
73  function renderCalendar(){
74    var y=view.getFullYear(), m=view.getMonth();
75    var first=new Date(y,m,1).getDay(); // 0=日
76    var dim=new Date(y,m+1,0).getDate();
77    var pdim=new Date(y,m,0).getDate();
78    document.getElementById('calTitle').textContent=y+'年 '+(m+1)+'月';
79    var g=document.getElementById('grid'); g.innerHTML='';
80    // 上月补位
81    for(var i=0;i<first;i++){
82      var d=document.createElement('div'); d.className='cell out';
83      d.innerHTML='<span class="d">'+(pdim-first+1+i)+'</span>'; g.appendChild(d);
84    }
85    for(var dd=1;dd<=dim;dd++){
86      var k=iso(y,m,dd); var cell=document.createElement('div'); cell.className='cell';
87      var arr=R[k]||[];
88      var isT=(k===cur), isS=(k===sel);
89      if(isT) cell.className+=' today'; if(isS) cell.className+=' sel';
90      var html='<span class="d">'+dd+'</span>';
91      if(arr.length){
92        // 取当日“最异常”的一条作为代表
93        var worst=arr[0]; arr.forEach(function(r){ if(grade(r.t).ab && !grade(worst.t).ab) worst=r; else if(grade(r.t).ab===grade(worst.t).ab && Math.abs(r.t-37)>Math.abs(worst.t-37)) worst=r; });
94        var g0=grade(worst.t);
95        cell.className+=' has '+g0.cls;
96        html+='<span class="v">'+worst.t.toFixed(1)+'°</span>';
97      }
98      cell.innerHTML=html;
99      (function(kk){ cell.addEventListener('click',function(){ sel=kk; renderAll(); }); })(k);
100      g.appendChild(cell);
101    }
102  }
103
104  function renderDay(){
105    var box=document.getElementById('dayBox');
106    var arr=R[sel]||[];
107    var dt=new Date(sel.replace(/-/g,'/'));
108    var wk=['日','一','二','三','四','五','六'][dt.getDay()];
109    document.getElementById('dayDate').textContent=(sel===cur?'今天 · ':'')+sel+' 周'+wk;
110    if(!arr.length){
111      box.innerHTML='<div class="empty">这一天还没有体温记录<br>点击下方按钮添加一条记录 🌡️</div>';
112      return;
113    }
114    arr.sort(function(a,b){return (b.ts||0)-(a.ts||0);});
115    var h='';
116    arr.forEach(function(r,idx){
117      var g0=grade(r.t);
118      h+='<div class="rec"><div class="badge" style="background:'+g0.color+'"><b>'+r.t.toFixed(1)+'</b><i>'+g0.label+'</i></div>'
119       + '<div class="info"><div class="top">'+r.site+' · '+r.time+'</div>'
120       + '<div class="meta">'+(g0.ab?'⚠ 异常体温':'✓ 体温正常')+'</div>'
121       + (r.note?'<div class="note">备注:'+esc(r.note)+'</div>':'')
122       + '</div><div class="ops"><button class="del" data-i="'+idx+'">删除</button></div></div>';
123    });
124    box.innerHTML=h;
125    box.querySelectorAll('.del').forEach(function(b){
126      b.addEventListener('click',function(){
127        var i=+b.getAttribute('data-i');
128        R[sel].splice(i,1); if(!R[sel].length) delete R[sel];
129        saveRec(R); renderAll(); toast('已删除该记录');
130      });
131    });
132  }
133
134  function esc(s){return String(s).replace(/[&<>"]/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c];});}
135
136  function renderAll(){ renderStats(); renderCalendar(); renderDay(); }
137
138  // ---- 弹层:添加记录 ----
139  var sheet=document.getElementById('sheet'), mask=document.getElementById('mask');
140  var formSite='口腔', formTime='早', editing=sel;
141  function openSheet(){
142    editing=sel;
143    document.getElementById('sfDate').textContent=sel;
144    document.getElementById('sfTemp').value='';
145    document.getElementById('sfNote').value='';
146    setSeg('site',formSite); setSeg('time',formTime);
147    mask.classList.add('show'); sheet.classList.add('show');
148  }
149  function closeSheet(){ mask.classList.remove('show'); sheet.classList.remove('show'); }
150  function setSeg(name,val){
151    document.querySelectorAll('[data-seg="'+name+'"]').forEach(function(b){
152      b.classList.toggle('on', b.getAttribute('data-v')===val);
153    });
154  }
155  document.querySelectorAll('[data-seg="site"]').forEach(function(b){
156    b.addEventListener('click',function(){ formSite=b.getAttribute('data-v'); setSeg('site',formSite); });
157  });
158  document.querySelectorAll('[data-seg="time"]').forEach(function(b){
159    b.addEventListener('click',function(){ formTime=b.getAttribute('data-v'); setSeg('time',formTime); });
160  });
161  document.getElementById('mask').addEventListener('click',closeSheet);
162  document.getElementById('dayAdd').addEventListener('click',openSheet);
163  document.getElementById('sfCancel').addEventListener('click',closeSheet);
164  document.getElementById('sfSave').addEventListener('click',function(){
165    var v=parseFloat(document.getElementById('sfTemp').value);
166    if(isNaN(v)||v<30||v>45){ toast('请输入 30~45 之间的体温'); return; }
167    if(!R[sel]) R[sel]=[];
168    R[sel].push({t:v,site:formSite,time:formTime,note:document.getElementById('sfNote').value.trim(),ts:Date.now()});
169    saveRec(R); closeSheet(); renderAll();
170    toast(grade(v).ab?('已记录 · '+grade(v).label+' ⚠'):'已记录 · 体温正常 ✓');
171  });
172
173  // ---- 弹层:设置 ----
174  var setSheet=document.getElementById('setSheet'), setMask=document.getElementById('setMask');
175  function openSet(){
176    var s=loadSet();
177    document.getElementById('svLow').value=s.low;
178    document.getElementById('svHigh').value=s.high;
179    document.getElementById('svFeverLo').value=s.feverLo;
180    document.getElementById('svFeverHi').value=s.feverHi;
181    document.getElementById('svHypo').value=s.hypo;
182    setMask.classList.add('show'); setSheet.classList.add('show');
183  }
184  function closeSet(){ setMask.classList.remove('show'); setSheet.classList.remove('show'); }
185  document.getElementById('btnSet').addEventListener('click',openSet);
186  document.getElementById('setMask').addEventListener('click',closeSet);
187  document.getElementById('setCancel').addEventListener('click',closeSet);
188  document.getElementById('setSave').addEventListener('click',function(){
189    var g=function(id){var x=parseFloat(document.getElementById(id).value);return isNaN(x)?null:x;};
190    var s={low:g('svLow'),high:g('svHigh'),feverLo:g('svFeverLo'),feverHi:g('svFeverHi'),hypo:g('svHypo')};
191    saveSet(s); closeSet(); renderAll(); toast('阈值已更新,日历已重算');
192  });
193  document.getElementById('setReset').addEventListener('click',function(){
194    localStorage.removeItem(KEY_S); closeSet(); renderAll(); toast('已恢复默认阈值');
195  });
196
197  // ---- 月份切换 ----
198  document.getElementById('prevM').addEventListener('click',function(){ view.setMonth(view.getMonth()-1); renderAll(); });
199  document.getElementById('nextM').addEventListener('click',function(){ view.setMonth(view.getMonth()+1); renderAll(); });
200  document.getElementById('toToday').addEventListener('click',function(){ view=new Date(); view.setDate(1); sel=cur; renderAll(); });
201
202  // init
203  seedDemo(); R=loadRec(); renderAll();
204})();
205