小草生鲜到家_H5/app.js · 约 6555 字符
1// 小草生鲜到家 · 逻辑层
2const $=s=>document.querySelector(s), $$=s=>document.querySelectorAll(s);
3const store={
4 get cart(){return JSON.parse(localStorage.getItem('fr_cart')||'[]')},
5 set cart(v){localStorage.setItem('fr_cart',JSON.stringify(v))},
6 get orders(){return JSON.parse(localStorage.getItem('fr_orders')||'[]')},
7 set orders(v){localStorage.setItem('fr_orders',JSON.stringify(v))},
8 get coupons(){return JSON.parse(localStorage.getItem('fr_coupons')||'3')},
9 set coupons(v){localStorage.setItem('fr_coupons',v)}
10};
11function fmt(n){return '¥'+n.toFixed(2)}
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 goodsById(id){return GOODS.find(g=>g.id==id)}
15function 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)}
16function addToCart(id,qty){
17 const g=goodsById(id);if(!g)return;
18 const cart=store.cart;
19 const exist=cart.find(i=>i.id===id);
20 if(exist)exist.num+=qty||1;else cart.push({id,name:g.name,emoji:g.emoji,price:g.price,unit:g.unit,num:qty||1});
21 store.cart=cart;toast('已加入购物车');
22 const c=cartCount();const el=$('.cb-count');if(el){el.textContent=c;el.style.display=c?'flex':'none'}
23 const t=$('.cb-total b');if(t)t.textContent=fmt(cartTotal());
24}
25function renderCartPage(){
26 const wrap=$('#cart-items');const cart=store.cart;
27 if(!cart.length){wrap.innerHTML='<div class="empty"><span class="e-icon">🛒</span>购物车还是空的<br>去挑点新鲜好菜吧~</div>';$('#cart-submit').style.display='none';return}
28 $('#cart-submit').style.display='block';
29 wrap.innerHTML=cart.map((i,idx)=>`
30 <div class="order-item">
31 <div class="oi-img">${i.emoji}</div>
32 <div class="oi-body">
33 <div class="oi-name">${i.name}</div>
34 <div class="oi-spec">${i.unit} · 冷链配送</div>
35 <div class="oi-foot"><span class="oi-price">${fmt(i.price*i.num)}</span>
36 <span style="display:flex;align-items:center;gap:10px">
37 <span class="sq-btn" style="width:24px;height:24px;border-radius:50%;background:#eef5e8;display:flex;align-items:center;justify-content:center;font-size:14px;cursor:pointer" onclick="chgQty(${idx},-1)">−</span>
38 <b style="font-size:14px">${i.num}</b>
39 <span class="sq-btn" style="width:24px;height:24px;border-radius:50%;background:#eef5e8;display:flex;align-items:center;justify-content:center;font-size:14px;cursor:pointer" onclick="chgQty(${idx},1)">+</span>
40 </span>
41 </div>
42 </div>
43 </div>`).join('');
44 $('#cart-total').textContent=fmt(cartTotal());
45 $('#cart-count').textContent=cartCount();
46 updSummary();
47}
48function chgQty(idx,d){
49 const cart=store.cart;cart[idx].num+=d;
50 if(cart[idx].num<=0)cart.splice(idx,1);
51 store.cart=cart;renderCartPage();
52}
53function updSummary(){
54 const total=cartTotal();
55 const coupon=store.coupons>0?5:0;
56 const ship=total>=RULES.freeShip?0:RULES.shipFee;
57 const tip=total<RULES.freeShip?'还差 '+fmt(RULES.freeShip-total)+' 免运费':'已免运费 🎉';
58 $('#ship-tip').textContent=tip;
59 $('#ship-fee').textContent=ship?'¥'+ship.toFixed(2):'免运费';
60 $('#pay-total').textContent=fmt(Math.max(0,total-coupon+ship));
61}
62function submitOrder(){
63 const cart=store.cart;
64 if(!cart.length){toast('购物车为空');return}
65 if(cartTotal()<RULES.minOrder){toast('满'+RULES.minOrder+'元才可下单');return}
66 const slot=document.querySelector('.time-slot.sel')?document.querySelector('.time-slot.sel').textContent:'立即配送';
67 const total=cartTotal();const ship=total>=RULES.freeShip?0:RULES.shipFee;
68 const o={id:'FR'+Date.now().toString().slice(-8),items:cart.map(i=>({name:i.name,emoji:i.emoji,unit:i.unit,num:i.num,price:i.price})),total:total+ship,ship,slot,time:new Date().toLocaleString('zh-CN',{hour:'2-digit',minute:'2-digit'}),status:1,addr:USER.address};
69 store.orders=[o,...store.orders];
70 store.cart=[];store.coupons=Math.max(0,store.coupons-1);
71 toast('下单成功,等待接单');
72 setTimeout(()=>location.href='orders.html',800);
73}
74function renderOrdersPage(){
75 const wrap=$('#orders-list');const orders=store.orders;
76 if(!orders.length){wrap.innerHTML='<div class="empty"><span class="e-icon">📦</span>还没有订单<br>去下一单尝尝鲜~</div>';return}
77 wrap.innerHTML=orders.map(o=>`
78 <div class="card">
79 <div style="display:flex;justify-content:space-between;font-size:12px;color:#93a58b;margin-bottom:10px">
80 <span>${o.time} · ${o.slot}</span><span style="color:var(--primary);font-weight:600">${['待支付','配送中','已完成'][o.status]}</span>
81 </div>
82 ${o.items.map(i=>'<div style="display:flex;justify-content:space-between;font-size:13px;padding:3px 0"><span>'+i.emoji+' '+i.name+' ×'+i.num+'<br><small style="color:#a9bca0">'+i.unit+'</small></span><b>'+fmt(i.price*i.num)+'</b></div>').join('')}
83 <div style="text-align:right;font-size:14px;font-weight:700;margin-top:8px;border-top:1px dashed #eee;padding-top:8px">合计 <span style="color:var(--red)">${fmt(o.total)}</span><small style="color:#93a58b;font-weight:400">${o.ship?'(含运费¥'+o.ship.toFixed(2)+')':'免运费'}</small></div>
84 <div class="status-steps">
85 ${[['已下单','✓'],['分拣中','📦'],['配送中','🛵'],['已签收','🏠']].map((s,i)=>'<div class="st '+(i<=o.status?'done':'')+'"><span class="dot">'+s[1]+'</span>'+s[0]+'</div>').join('')}
86 </div>
87 <div class="deliver-card">
88 <div class="dc-row"><span>📍 配送至</span><b>${o.addr}</b></div>
89 <div class="dc-row"><span>🚚 预计送达</span><b>${o.slot}</b></div>
90 <div class="dc-tip">💡 生鲜如遇坏果坏菜,48小时内联系客服,秒赔无理由</div>
91 </div>
92 </div>`).join('');
93}
94function renderMine(){
95 $('#u-name').textContent=USER.name;$('#u-level').textContent=USER.level;
96 $('#u-points').textContent=USER.points;$('#u-coupons').textContent=USER.coupons+store.coupons;
97 $('#u-addr').textContent=USER.address;
98 $('#m-points').textContent=USER.points;$('#m-coupons').textContent=USER.coupons+store.coupons;
99}
100