xiaocao-accounting/app.js · 约 6069 字符
1// 小草记账本 · 交互逻辑
2
3// 首页
4function renderIndex(){
5 const totalIn=RECORDS.filter(r=>r.amount>0).reduce((s,r)=>s+r.amount,0)+8500;
6 const totalOut=Math.abs(RECORDS.filter(r=>r.amount<0).reduce((s,r)=>s+r.amount,0));
7 document.getElementById('ms-balance').textContent='¥'+(totalIn-totalOut).toLocaleString();
8 document.getElementById('ms-in').textContent='¥'+totalIn.toLocaleString();
9 document.getElementById('ms-out').textContent='¥'+totalOut.toLocaleString();
10 document.getElementById('recent-list').innerHTML=RECORDS.slice(0,5).map(r=>`
11 <div class="recent-item">
12 <div class="ri-icon" style="background:${r.color}">${r.icon}</div>
13 <div class="ri-info"><div class="ri-cat">${r.cat}</div><div class="ri-desc">${r.desc} · ${r.time}</div></div>
14 <div class="ri-amount ${r.amount>0?'inc':'exp'}">${r.amount>0?'+':'-'}¥${Math.abs(r.amount)}</div>
15 </div>`).join('');
16 document.getElementById('account-list').innerHTML=ACCOUNTS.map(a=>`
17 <div class="recent-item">
18 <div class="ri-icon" style="background:var(--primary-light)">${a.icon}</div>
19 <div class="ri-info"><div class="ri-cat">${a.name}</div></div>
20 <div class="ri-amount ${a.balance<0?'exp':'inc'}">${a.balance<0?'-':'+'}¥${Math.abs(a.balance).toLocaleString()}</div>
21 </div>`).join('');
22}
23
24// 记一笔
25let recType='out', recCat='', amountVal='', note='';
26function initRecord(){
27 recType=location.search.includes('t=in')?'in':'out';
28 switchTab(recType);
29}
30function switchTab(t){ recType=t;
31 document.getElementById('tab-out').classList.toggle('active',t==='out');
32 document.getElementById('tab-in').classList.toggle('active',t==='in');
33 renderCats();
34}
35function renderCats(){
36 const cats=recType==='out'?CATS_OUT:CATS_IN;
37 if(!recCat||!cats.find(c=>c.name===recCat)) recCat=cats[0].name;
38 document.getElementById('cat-grid').innerHTML=cats.map(c=>`
39 <div class="cat-item ${c.name===recCat?'active':''}" onclick="selectCat('${c.name}')">
40 <div class="ci-icon" style="background:${c.color}">${c.icon}</div>
41 <div class="ci-name">${c.name}</div>
42 </div>`).join('');
43}
44function selectCat(n){ recCat=n; renderCats(); }
45function press(k){
46 if(k==='⌫'){ amountVal=amountVal.slice(0,-1); }
47 else if(k==='.'){ if(!amountVal.includes('.')) amountVal+=amountVal?'.':'0.'; }
48 else { if(amountVal.includes('.')&&amountVal.split('.')[1].length>=2) return; if(amountVal==='0') amountVal=k; else amountVal+=k; }
49 document.getElementById('amount-val').textContent=(parseFloat(amountVal||0)).toFixed(2);
50}
51function saveRecord(){
52 if(!amountVal||parseFloat(amountVal)<=0){ alert('请输入金额'); return; }
53 alert('✅ 记账成功!\n\n'+(recType==='out'?'支出':'收入')+' ¥'+parseFloat(amountVal).toFixed(2)+'\n分类:'+recCat+(note?'\n备注:'+note:''));
54 location.href='index.html';
55}
56
57// 统计
58function renderStats(){
59 const totalIn=RECORDS.filter(r=>r.amount>0).reduce((s,r)=>s+r.amount,0)+8500;
60 const totalOut=Math.abs(RECORDS.filter(r=>r.amount<0).reduce((s,r)=>s+r.amount,0));
61 document.getElementById('s-in').textContent='¥'+totalIn.toLocaleString();
62 document.getElementById('s-out').textContent='¥'+totalOut.toLocaleString();
63 document.getElementById('s-avg').textContent='¥'+Math.round(totalOut/11);
64 document.getElementById('s-budget').textContent='¥'+(7300-totalOut).toLocaleString();
65 const max=Math.max(...WEEK.map(w=>w.amt));
66 document.getElementById('week-chart').innerHTML=WEEK.map(w=>`
67 <div class="bar ${w.active?'active':''}" style="height:${w.amt/max*90+4}px">
68 <div class="bar-val">${w.amt}</div>
69 <div class="bar-label">周${w.day}</div>
70 </div>`).join('');
71 const pieData=[
72 {name:'餐饮',amt:1280,color:'#faad14'},{name:'购物',amt:980,color:'#ff4d4f'},
73 {name:'居家',amt:1200,color:'#52c41a'},{name:'交通',amt:286,color:'#13c2c2'},
74 {name:'娱乐',amt:468,color:'#722ed1'},{name:'其他',amt:1469,color:'#999'},
75 ];
76 const total=pieData.reduce((s,p)=>s+p.amt,0);
77 document.getElementById('pie-legend').innerHTML=pieData.map(p=>`
78 <div class="pl-row">
79 <span class="pl-dot" style="background:${p.color}"></span>
80 <span class="pl-name">${p.name}</span>
81 <span class="pl-pct">${Math.round(p.amt/total*100)}%</span>
82 <span class="pl-amt">¥${p.amt.toLocaleString()}</span>
83 </div>`).join('');
84}
85
86// 预算
87function renderBudget(){
88 document.getElementById('budget-list').innerHTML=BUDGETS.map(b=>{
89 const pct=Math.min(100,Math.round(b.used/b.limit*100));
90 const color=b.status==='ok'?'var(--green)':b.status==='warn'?'var(--accent)':'var(--red)';
91 const label=b.status==='ok'?'正常':b.status==='warn'?'警戒':'超支';
92 return `<div class="budget-item">
93 <div class="bi-top"><span class="bi-name">${b.name}</span><span class="bi-status ${b.status}">${label}</span></div>
94 <div class="bi-bar"><div class="bi-fill" style="width:${pct}%;background:${color}"></div></div>
95 <div class="bi-meta"><span>¥${b.used.toLocaleString()} / ¥${b.limit.toLocaleString()}</span><span style="color:${color};font-weight:600">${pct}%</span></div>
96 </div>`;
97 }).join('');
98}
99
100// 多账本
101function renderBooks(){
102 document.getElementById('book-grid').innerHTML=BOOKS.map((b,i)=>`
103 <div class="book-card ${b.active?'active':''}" onclick="switchBook(${i})">
104 <div class="bc-icon">${b.icon}</div>
105 <div class="bc-name">${b.name}${b.active?' · 当前':''}</div>
106 <div class="bc-desc">${b.desc}</div>
107 <div class="bc-balance">¥${b.balance.toLocaleString()}</div>
108 </div>`).join('');
109}
110function switchBook(i){
111 BOOKS.forEach((b,j)=>b.active=(i===j));
112 renderBooks();
113}
114
115// 我的
116function renderMine(){
117 document.getElementById('p-name').textContent=USER.name;
118 document.getElementById('p-days').textContent=USER.days;
119 document.getElementById('p-records').textContent=USER.records;
120 document.getElementById('p-budgets').textContent=USER.budgets;
121}
122