app.js · 约 11417 字符
1// 小草咖啡轻食 · 交互逻辑
2let cart = JSON.parse(localStorage.getItem('xc_coffee_cart') || '[]');
3let cartMode = 'pick';
4function saveCart(){ localStorage.setItem('xc_coffee_cart', JSON.stringify(cart)); }
5function cartCount(){ return cart.reduce((s,i)=>s+i.qty,0); }
6function cartTotal(){ return cart.reduce((s,i)=>s+i.price*i.qty,0); }
7function goodsCard(g){
8 return `<div class="goods-card" onclick="openSpec(${g.id})">
9 <div class="gc-img" style="background:${g.color}">
10 <span class="gc-tag badge ${g.sale?'badge-sale':g.new?'badge-new':'badge-hot'}">${g.sale?'🔥特惠':g.new?'🆕新品':'⭐热销'}</span>
11 ${g.emoji}
12 </div>
13 <div class="gc-body">
14 <div class="gc-name">${g.name}</div>
15 <div class="gc-desc">${g.desc}</div>
16 <div class="gc-bottom">
17 <span class="gc-price">¥${g.price}<small> <s>¥${g.origPrice}</s></small></span>
18 <button class="gc-add" onclick="event.stopPropagation();openSpec(${g.id})">+</button>
19 </div>
20 </div>
21 </div>`;
22}
23
24// 首页
25function renderIndex(){
26 document.getElementById('hot-goods').innerHTML = GOODS.filter(g=>g.hot).slice(0,4).map(goodsCard).join('');
27 document.getElementById('new-goods').innerHTML = GOODS.filter(g=>g.new).slice(0,4).map(goodsCard).join('');
28 document.getElementById('coupon-list').innerHTML = COUPONS.map(c=>`
29 <div class="coupon-card">
30 <div class="cc-left"><div class="cc-amount">${c.amount}</div><div class="cc-cond">${c.cond}</div></div>
31 <div class="cc-info"><div class="cc-name">${c.name}</div><div class="cc-time">🕐 ${c.time}</div></div>
32 <button class="gc-add" style="width:auto;padding:6px 14px;border-radius:18px;background:var(--primary);color:#fff;border:none;font-size:12px;cursor:pointer" onclick="alert('🎟️ 已领取${c.name}!')">领取</button>
33 </div>`).join('');
34}
35
36// 点单
37let catFilter = '咖啡', specGood = null, specQty = 1;
38let specSel = { size:0, sugar:2, ice:2, tops:[] };
39function renderMenu(){
40 document.getElementById('menu-side').innerHTML = CATS.map(c=>`<a href="javascript:void(0)" class="${c===catFilter?'active':''}" onclick="setCat('${c}')">${c}</a>`).join('');
41 const list = GOODS.filter(g=>g.cat===catFilter);
42 document.getElementById('menu-main').innerHTML = `<div class="mm-title">${catFilter}</div>` + list.map(g=>`
43 <div class="menu-item" onclick="openSpec(${g.id})">
44 <div class="mi-img" style="background:${g.color}">${g.emoji}</div>
45 <div class="mi-info">
46 <div class="mi-name">${g.name}</div>
47 <div class="mi-spec">${g.spec} · ${g.desc}</div>
48 <div class="mi-bottom">
49 <span class="mi-price">¥${g.price}</span>
50 <button class="mi-add" onclick="event.stopPropagation();openSpec(${g.id})">+</button>
51 </div>
52 </div>
53 </div>`).join('');
54 updateCartFloat();
55}
56function setCat(c){ catFilter=c; renderMenu(); }
57function updateCartFloat(){
58 const n=document.getElementById('cf-num'), t=document.getElementById('cf-total');
59 if(n){ n.textContent=cartCount(); t.textContent='¥'+cartTotal(); }
60}
61function openSpec(id){
62 specGood = GOODS.find(g=>g.id===id); if(!specGood) return;
63 specQty = 1; specSel = { size:0, sugar:2, ice:2, tops:[] };
64 document.getElementById('sp-name').textContent = specGood.name;
65 document.getElementById('sp-price').textContent = '¥'+specGood.price;
66 const chips = (arr,key,multi)=>{
67 return arr.map((v,i)=>{
68 const on = multi ? specSel[key].includes(i) : specSel[key]===i;
69 return `<span class="chip ${on?'active':''}" onclick="pickSpec(${i},'${key}',${multi})">${v}</span>`;
70 }).join('');
71 };
72 document.getElementById('sp-size').innerHTML = chips(SIZES,'size',false);
73 document.getElementById('sp-sugar').innerHTML = chips(SUGARS,'sugar',false);
74 document.getElementById('sp-ice').innerHTML = chips(ICES,'ice',false);
75 document.getElementById('sp-top').innerHTML = chips(TOPPINGS,'tops',true);
76 document.getElementById('sp-qty-num').textContent = 1;
77 document.getElementById('spec-mask').classList.add('show');
78 document.getElementById('spec-panel').classList.add('show');
79}
80function pickSpec(i,key,multi){
81 if(multi){ specSel[key].includes(i) ? specSel[key].splice(specSel[key].indexOf(i),1) : specSel[key].push(i); }
82 else { specSel[key]=i; }
83 openSpec(specGood.id);
84}
85function changeSpecQty(d){ specQty=Math.max(1,specQty+d); document.getElementById('sp-qty-num').textContent=specQty; }
86function confirmSpec(){
87 const tops = specSel.tops.map(i=>TOPPINGS[i]);
88 const price = specGood.price + tops.length*2;
89 cart.push({id:specGood.id, name:specGood.name, emoji:specGood.emoji, color:specGood.color,
90 spec:SIZES[specSel.size]+' · '+SUGARS[specSel.sugar]+' · '+ICES[specSel.ice]+(tops.length?' · +'+tops.join('/'):''),
91 price, qty:specQty});
92 saveCart(); closeSpec(); updateCartFloat();
93 alert('✅ 已加入购物车:'+specGood.name+' ×'+specQty);
94}
95function closeSpec(){
96 document.getElementById('spec-mask').classList.remove('show');
97 document.getElementById('spec-panel').classList.remove('show');
98}
99
100// 购物车
101function renderCart(){
102 const el=document.getElementById('cart-list');
103 if(!el) return;
104 if(!cart.length){
105 el.innerHTML='<div class="empty"><div class="eicon">🛒</div><div>购物车空空如也,去<a href="menu.html" style="color:var(--primary)">点单</a>吧~</div></div>';
106 } else {
107 el.innerHTML='<div style="background:#fff;border-radius:12px;margin:0 12px;overflow:hidden">'+cart.map((i,idx)=>`
108 <div class="cart-item">
109 <div class="ci-img" style="background:${i.color}">${i.emoji}</div>
110 <div class="ci-info">
111 <div class="ci-name">${i.name}</div>
112 <div class="ci-spec">${i.spec}</div>
113 <div class="ci-price">¥${i.price}</div>
114 </div>
115 <div class="qty"><button onclick="changeCartQty(${idx},-1)">−</button><span>${i.qty}</span><button onclick="changeCartQty(${idx},1)">+</button></div>
116 </div>`).join('')+'</div>';
117 }
118 document.getElementById('total-price').textContent='¥'+cartTotal();
119 document.getElementById('coupon-list').innerHTML = COUPONS.map(c=>`
120 <div class="coupon-card" style="margin:8px 12px">
121 <div class="cc-left"><div class="cc-amount">${c.amount}</div><div class="cc-cond">${c.cond}</div></div>
122 <div class="cc-info"><div class="cc-name">${c.name}</div><div class="cc-time">🕐 ${c.time}</div></div>
123 <span class="badge badge-sale" style="background:#fff7e6;color:var(--accent)">可用</span>
124 </div>`).join('');
125}
126function changeCartQty(idx,d){
127 cart[idx].qty+=d;
128 if(cart[idx].qty<=0) cart.splice(idx,1);
129 saveCart(); renderCart();
130}
131function setMode(m){
132 cartMode=m;
133 document.getElementById('mode-pick').classList.toggle('active',m==='pick');
134 document.getElementById('mode-delivery').classList.toggle('active',m==='delivery');
135}
136function checkout(){
137 if(!cart.length){ alert('购物车为空'); return; }
138 const remark=document.getElementById('remark-input').value;
139 const code='A'+Math.floor(100+Math.random()*900);
140 alert(`✅ 下单成功!\n\n${cartMode==='pick'?'到店自取 · 取餐码:'+code:'外卖配送 · 预计30分钟送达'}\n订单金额:¥${cartTotal()}\n备注:${remark||'无'}\n(演示页)`);
141 cart=[]; saveCart(); renderCart();
142}
143
144// 订单
145let orderFilter='';
146function renderOrders(){
147 const el=document.getElementById('order-list');
148 if(!el) return;
149 let orders=ORDERS;
150 if(orderFilter) orders=orders.filter(o=>o.status===orderFilter);
151 if(!orders.length){ el.innerHTML='<div class="empty"><div class="eicon">📦</div><div>暂无订单</div></div>'; return; }
152 el.innerHTML=orders.map(o=>{
153 const prog=PROGRESS[o.state], cur=o.state==='making'?2:o.state==='shipping'?2:3;
154 return `<div class="order-item">
155 <div class="oi-top"><span style="font-size:13px;color:#999">${o.id}</span><span class="oi-status">${o.status}</span></div>
156 <div class="oi-goods">
157 <div class="oi-img">${o.emoji}</div>
158 <div><div class="oi-name">${o.name}</div><div class="oi-spec">${o.spec}</div></div>
159 </div>
160 ${o.state==='making'?`<div class="pick-code"><span style="font-size:24px">🎫</span><div><div class="pc-num">${o.code}</div><div class="pc-tip">到店出示取餐码取餐</div></div></div>`:''}
161 <div class="progress">${prog.map((p,i)=>`<div class="pr-node ${i<=cur?'active':''}"><div class="pr-dot"></div>${p}</div>${i<prog.length-1?'<div class="pr-line"></div>':''}`).join('')}</div>
162 <div class="oi-bottom" style="margin-top:8px">
163 <span class="oi-total">实付 ¥${o.price}</span>
164 ${o.state==='done'?'<button class="oi-btn" style="padding:6px 14px;border-radius:20px;font-size:12px;border:none;background:#f5f5f5;color:#666;cursor:pointer" onclick="alert('⭐ 评价成功!')">去评价</button>':'<button class="oi-btn" style="padding:6px 14px;border-radius:20px;font-size:12px;border:none;background:linear-gradient(135deg,#7c4a21,#c07a3a);color:#fff;cursor:pointer" onclick="alert('🛒 再次购买')">再次购买</button>'}
165 </div>
166 </div>`;
167 }).join('');
168}
169function filterOrder(el,f){
170 orderFilter=f;
171 document.querySelectorAll('.status-bar a').forEach(a=>{a.style.color='#666';a.style.borderBottom='none';a.style.fontWeight='400'});
172 el.style.color='var(--primary)';el.style.borderBottom='2px solid var(--primary)';el.style.fontWeight='600';
173 renderOrders();
174}
175
176// 会员
177let rechargeSel=1;
178const RECHARGE=[{a:50,b:'送5'},{a:100,b:'送12'},{a:200,b:'送30'},{a:500,b:'送88'}];
179function renderMember(){
180 document.getElementById('mh-level').textContent='🏅 '+USER.level;
181 document.getElementById('mh-desc').textContent='银卡9折 · 生日双倍积分 · 专属新品尝鲜';
182 document.getElementById('mh-points').textContent=USER.points;
183 document.getElementById('mh-coupons').textContent=USER.coupons;
184 document.getElementById('bc-balance').textContent='¥'+USER.balance;
185 document.getElementById('bc-total').textContent='¥680';
186 document.getElementById('recharge-row').innerHTML=RECHARGE.map((r,i)=>`
187 <div class="rc-item ${i===rechargeSel?'active':''}" onclick="pickRecharge(${i})">
188 <div class="rc-amount">¥${r.a}</div><div class="rc-bonus">送${r.b}</div>
189 </div>`).join('');
190 document.getElementById('level-grid').innerHTML=LEVELS.map((l,i)=>`
191 <div class="level-card ${l.name===USER.level?'current':''}">
192 <div class="lc-icon">${l.icon}</div>
193 <div class="lc-name">${l.name}</div>
194 <div class="lc-disc">${l.disc}</div>
195 <div class="lc-disc">${l.need}</div>
196 </div>`).join('');
197}
198function pickRecharge(i){ rechargeSel=i; renderMember(); }
199function recharge(){ alert(`✅ 充值成功!\n\n到账 ¥${RECHARGE[rechargeSel].a},赠送 ${RECHARGE[rechargeSel].b}元\n余额:¥${(USER.balance+RECHARGE[rechargeSel].a).toFixed(1)}(演示页)`); }
200
201// 我的
202function renderMine(){
203 document.getElementById('p-name').textContent=USER.name;
204 document.getElementById('p-level').textContent=USER.level;
205 document.getElementById('p-points').textContent=USER.points;
206 document.getElementById('p-balance').textContent=USER.balance;
207 document.getElementById('p-coupons').textContent=USER.coupons;
208}
209