小草火锅_H5/app.js · 约 9104 字符
1// 小草火锅 · 逻辑层
2const $=s=>document.querySelector(s), $$=s=>document.querySelectorAll(s);
3const store={
4 get cart(){return JSON.parse(localStorage.getItem('hp_cart')||'[]')},
5 set cart(v){localStorage.setItem('hp_cart',JSON.stringify(v))},
6 get orders(){return JSON.parse(localStorage.getItem('hp_orders')||'[]')},
7 set orders(v){localStorage.setItem('hp_orders',JSON.stringify(v))},
8 get coupons(){return JSON.parse(localStorage.getItem('hp_coupons')||JSON.stringify(COUPONS))},
9 set coupons(v){localStorage.setItem('hp_coupons',JSON.stringify(v))}
10};
11function fmt(n){return '¥'+n.toFixed(0)}
12function cartCount(){return store.cart.reduce((s,i)=>s+i.num,0)}
13function cartTotal(){return store.cart.reduce((s,i)=>s+i.num*i.price,0)}
14function 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)}
15/* 点餐 */
16let curCat='meat', curPot=0, curSpicy='中辣';
17function addDish(id){
18 const d=DISHES.find(x=>x.id==id);if(!d)return;
19 const cart=store.cart;
20 const exist=cart.find(i=>i.id===id&&i.spicy===undefined);
21 if(exist)exist.num++;else cart.push({id,name:d.name,emoji:d.emoji,price:d.price,unit:d.unit,num:1});
22 store.cart=cart;toast('已加入 '+d.name);
23 refreshBar();
24}
25function renderMenu(){
26 $('#pot-list').innerHTML=POTS.map((p,i)=>`
27 <div class="pot-card">
28 <div class="pc-emoji">${p.emoji}</div>
29 <div class="pc-body">
30 <div class="pc-name">${p.name} ${p.tag==='hot'?'<span class="badge badge-hot">招牌</span>':p.tag==='new'?'<span class="badge badge-new">新品</span>':''}</div>
31 <div class="pc-desc">${p.desc}</div>
32 <div class="pc-foot"><span class="pc-price"><em>¥</em>${p.price}</span>
33 <span class="pc-add" style="width:26px;height:26px;border-radius:50%;background:linear-gradient(135deg,#d92626,#ff5a36);color:#fff;display:flex;align-items:center;justify-content:center;font-size:18px;cursor:pointer" onclick="curPot=${i};selectPot()">+</span></div>
34 </div>
35 </div>`).join('');
36 const list=DISHES.filter(d=>d.cat===curCat);
37 $('#dish-list').innerHTML='<h3 style="font-size:14px;color:#8a4a36;margin:8px 0">'+MENU_CATS.find(c=>c.id===curCat).name+'</h3>'+
38 list.map(d=>`
39 <div class="menu-item">
40 <div class="mi-img">${d.emoji}</div>
41 <div class="mi-body">
42 <div class="mi-name">${d.name} ${d.tag==='hot'?'<span class="badge badge-hot">必点</span>':d.tag==='new'?'<span class="badge badge-new">新品</span>':''}</div>
43 <div class="mi-desc">${d.desc}</div><div class="mi-spec">可备注半份/加量/少辣</div>
44 <div class="mi-foot"><span class="mi-price"><em>¥</em>${d.price}<small style="font-size:10px;color:#c08a78">/${d.unit}</small></span><span class="gc-add" style="width:26px;height:26px;border-radius:50%;background:linear-gradient(135deg,#d92626,#ff5a36);color:#fff;display:flex;align-items:center;justify-content:center;font-size:18px;cursor:pointer" onclick="addDish(${d.id})">+</span></div>
45 </div>
46 </div>`).join('');
47}
48function selectPot(){
49 const p=POTS[curPot];
50 let html='<div class="section-title" style="margin:0 0 8px">辣度选择 · '+p.name+'</div><div class="spicy-chips">';
51 p.spicy.forEach(s=>{html+='<span class="spicy-chip'+(s===curSpicy?' sel':'')+'" onclick="curSpicy=this.textContent;$$('.spicy-chip').forEach(x=>x.classList.remove('sel'));this.classList.add('sel');toast('锅底辣度:'+curSpicy)">'+s+'</span>'});
52 html+='</div><div style="font-size:11px;color:#c08a78;margin-top:10px">已选:'+p.name+'('+curSpicy+') · 锅底 ¥'+p.price+'</div>';
53 const sheet=document.createElement('div');
54 sheet.style.cssText='position:fixed;bottom:0;left:50%;transform:translateX(-50%);width:100%;max-width:430px;background:#fff;border-radius:16px 16px 0 0;z-index:201;padding:16px';
55 sheet.innerHTML=html+'<button class="btn btn-primary" style="margin-top:14px" onclick="confirmPot()">确认锅底</button>';
56 const mask=document.createElement('div');
57 mask.style.cssText='position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:200';
58 mask.onclick=()=>{mask.remove();sheet.remove()};
59 document.body.appendChild(mask);document.body.appendChild(sheet);
60 window._sheet=[mask,sheet];
61}
62function confirmPot(){
63 const p=POTS[curPot];
64 const cart=store.cart;
65 const exist=cart.find(i=>i.id==='pot');
66 if(exist){exist.name='锅底·'+p.name;exist.price=p.price;exist.spicy=curSpicy}
67 else cart.unshift({id:'pot',name:'锅底·'+p.name,emoji:p.emoji,price:p.price,num:1,spicy:curSpicy});
68 store.cart=cart;toast('锅底已加入:'+p.name+'('+curSpicy+')');
69 window._sheet.forEach(el=>el.remove());
70 refreshBar();
71}
72function refreshBar(){
73 const c=cartCount();const bar=$('#cart-bar');
74 if(bar){bar.style.display=c?'flex':'none';
75 const el=$('#cb-count');if(el){el.textContent=c;el.style.display=c?'flex':'none'}
76 const t=$('#cb-total');if(t)t.textContent=fmt(cartTotal());}
77}
78function renderCartPage(){
79 const wrap=$('#cart-items');const cart=store.cart;
80 if(!cart.length){wrap.innerHTML='<div class="empty"><span class="e-icon">🍲</span>还没点菜<br>先选个锅底吧~</div>';$('#cart-submit').style.display='none';return}
81 $('#cart-submit').style.display='block';
82 wrap.innerHTML=cart.map((i,idx)=>`
83 <div class="order-item">
84 <div class="oi-img">${i.emoji}</div>
85 <div class="oi-body">
86 <div class="oi-name">${i.name}</div>
87 <div class="oi-spec">${i.spicy?'辣度 '+i.spicy:'可备注半份/少辣'}${i.unit?' · '+i.unit:''}</div>
88 <div class="oi-foot"><span class="oi-price">${fmt(i.price*i.num)}</span>
89 <span style="display:flex;align-items:center;gap:10px">
90 <span style="width:24px;height:24px;border-radius:50%;background:#fbe3d8;display:flex;align-items:center;justify-content:center;font-size:14px;cursor:pointer" onclick="chgQty(${idx},-1)">−</span>
91 <b style="font-size:14px">${i.num}</b>
92 <span style="width:24px;height:24px;border-radius:50%;background:#fbe3d8;display:flex;align-items:center;justify-content:center;font-size:14px;cursor:pointer" onclick="chgQty(${idx},1)">+</span>
93 </span>
94 </div>
95 </div>
96 </div>`).join('');
97 $('#cart-total').textContent=fmt(cartTotal());
98 $('#cart-count').textContent=cartCount();
99}
100function chgQty(idx,d){
101 const cart=store.cart;cart[idx].num+=d;
102 if(cart[idx].num<=0)cart.splice(idx,1);
103 store.cart=cart;renderCartPage();
104}
105function submitOrder(){
106 const cart=store.cart;
107 if(!cart.length){toast('请先点菜');return}
108 if(!cart.find(i=>i.id==='pot')){toast('先选个锅底吧');return}
109 const coupon=store.coupons.length?30:0;
110 const total=Math.max(0,cartTotal()-coupon);
111 const o={id:'HP'+Date.now().toString().slice(-8),items:cart.map(i=>({name:i.name,emoji:i.emoji,num:i.num,price:i.price,spicy:i.spicy})),total,time:new Date().toLocaleString('zh-CN',{hour:'2-digit',minute:'2-digit'}),status:0,table:'A12',code:'取餐码 36'+Math.floor(100+Math.random()*900)};
112 store.orders=[o,...store.orders];store.cart=[];store.coupons=store.coupons.slice(1);
113 toast('下单成功,锅底优先出餐');
114 setTimeout(()=>location.href='orders.html',800);
115}
116function renderOrdersPage(){
117 const wrap=$('#order-list');const orders=store.orders;
118 const steps=[['已下单','✓'],['锅底出锅','🍲'],['涮品出餐','🥩'],['开吃啦','😋']];
119 if(!orders.length){wrap.innerHTML='<div class="empty"><span class="e-icon">🍲</span>还没有订单<br>来一顿热腾腾的火锅吧~</div>';return}
120 wrap.innerHTML=orders.map(o=>`
121 <div class="order-card">
122 <div class="oc-head"><span>${o.time} · 桌号 ${o.table}</span><span class="oc-status">● ${['已下单','备餐中','配送中','已完成'][o.status]}</span></div>
123 <div class="oc-body"><span class="oc-emoji">🍲</span>
124 <div><div class="oc-name">${o.items[0].name}${o.items.length>1?' 等'+o.items.length+'项':''}</div><div class="oc-meta">${o.items.map(i=>i.emoji+i.num).join(' ')}</div></div>
125 <div class="oc-price">¥${o.total}</div>
126 </div>
127 <div class="oc-steps">${steps.map((s,i)=>'<div class="os '+(i<o.status?'done':i===o.status?'now':'')+'"><span class="dot">'+s[1]+'</span>'+s[0]+'</div>').join('')}</div>
128 <div style="font-size:11px;color:#c08a78;margin-top:8px">💡 ${o.code} · 到店报码开吃,蘸料台自助取用</div>
129 </div>`).join('');
130}
131function renderMine(){
132 $('#u-name').textContent=USER.name;$('#u-level').textContent=USER.level;
133 $('#m-points').textContent=USER.points;$('#m-balance').textContent=USER.balance;
134 $('#m-coupons').textContent=USER.coupons+store.coupons.length;
135 $('#m-times').textContent=USER.times;
136}
137