首页/ mobileweb/ 惠存会员 · 个体工商户门店会员储值系统/ 代码预览

💻 代码预览 · 惠存会员 · 个体工商户门店会员储值系统

mw_shop · 上传者 驰旭教育
← 返回详情
app.js · 约 41932 字符
1/* ============================================================2   惠存会员 · 个体工商户门店会员储值系统  —  应用逻辑(jQuery 单页 SPA)3   ============================================================ */4(function(){5  "use strict";6  var LS_KEY = "hc_state_v1";78  /* ---------------- 状态(运行期持久化) ---------------- */9  function defaultState(){10    return {11      balance: 1280.00,12      gift: 360.00,13      points: 2480,14      signDays: [true,true,true,true,false,false,false],15      signedToday: false,16      coupons: JSON.parse(JSON.stringify(MY_COUPONS)),17      transactions: JSON.parse(JSON.stringify(TRANSACTIONS)),18      transfers: JSON.parse(JSON.stringify(TRANSFERS)),19      mallRedeemed: {},20      inviteCount: 321    };22  }23  function loadState(){24    try{ var s = JSON.parse(localStorage.getItem(LS_KEY)); if(s && typeof s.balance==="number") return s; }catch(e){}25    return defaultState();26  }27  function saveState(s){ try{ localStorage.setItem(LS_KEY, JSON.stringify(s)); }catch(e){} }28  var S = loadState();2930  /* ---------------- 工具 ---------------- */31  function money(n){ return "¥" + (Number(n)||0).toFixed(2); }32  function money0(n){ return "¥" + Math.round(Number(n)||0); }33  function discountText(d){ return d>=1 ? "无折扣" : (d*10).toFixed(1).replace(/\.0$/,"") + "折"; }34  function levelInfo(key){ for(var i=0;i<MEMBER_LEVELS.length;i++){ if(MEMBER_LEVELS[i].key===key) return MEMBER_LEVELS[i]; } return MEMBER_LEVELS[0]; }35  function nowStr(){ var d=new Date(); function p(n){return (n<10?"0":"")+n;} return d.getFullYear()+"-"+p(d.getMonth()+1)+"-"+p(d.getDate())+" "+p(d.getHours())+":"+p(d.getMinutes()); }36  function toast(msg){ var t=$("#toast"); t.text(msg).addClass("show"); clearTimeout(t[0]._t); t[0]._t=setTimeout(function(){ t.removeClass("show"); },1500); }37  function planById(id){ for(var i=0;i<RECHARGE_PLANS.length;i++){ if(RECHARGE_PLANS[i].id===id) return RECHARGE_PLANS[i]; } return null; }38  function mallById(id){ for(var i=0;i<MALL_ITEMS.length;i++){ if(MALL_ITEMS[i].id===id) return MALL_ITEMS[i]; } return null; }39  function poolById(id){ for(var i=0;i<COUPON_POOL.length;i++){ if(COUPON_POOL[i].id===id) return COUPON_POOL[i]; } return null; }40  function getParam(q,key){ if(!q) return ""; var p=q.split("&"); for(var i=0;i<p.length;i++){ var kv=p[i].split("="); if(kv[0]===key) return decodeURIComponent(kv[1]||""); } return ""; }41  function lv(key){ return levelInfo(key); }4243  /* ---------------- 顶部品牌栏 ---------------- */44  function topbar(){45    var ml = lv(MEMBER.level);46    return ''+47    '<div class="topbar">'+48      '<div class="brand">'+49        '<div class="logo">惠</div>'+50        '<div><div class="bname">'+STORE.name+'</div><div class="bsub">'+STORE.sub+'</div></div>'+51      '</div>'+52      '<div class="mini">'+53        '<div class="m"><div class="mk">储值本金</div><div class="mv">'+money0(S.balance)+'</div></div>'+54        '<div class="m"><div class="mk">赠送金</div><div class="mv">'+money0(S.gift)+'</div></div>'+55        '<div class="m"><div class="mk">积分</div><div class="mv">'+S.points+'</div></div>'+56      '</div>'+57    '</div>';58  }5960  /* ---------------- 底部 Tab ---------------- */61  var TABS = [62    {k:"home",   t:"首页", i:"🏠"},63    {k:"recharge",t:"储值", i:"💰"},64    {k:"member", t:"会员", i:"👑"},65    {k:"mall",   t:"商城", i:"🎁"},66    {k:"mine",   t:"我的", i:"👤"}67  ];68  function tabbar(active){69    var h='';70    for(var i=0;i<TABS.length;i++){71      var t=TABS[i]; var on = (t.k===active)?" active":"";72      h+='<a class="'+on.trim()+'" data-to="#/'+t.k+'"><span class="ti">'+t.i+'</span>'+t.t+'</a>';73    }74    return h;75  }7677  /* ---------------- 子页头(带返回) ---------------- */78  function subbar(title){79    return '<div class="subbar"><span class="back" data-to="#/'+ (title==="我的"?"mine":(title==="储值中心"?"recharge":(title==="会员中心"?"member":(title==="积分商城"?"mall":"home")))) +'">‹ 返回</span><span class="t">'+title+'</span></div>';80  }8182  /* ============================================================83     视图:首页84     ============================================================ */85  function viewHome(){86    var ml = lv(MEMBER.level);87    var feats = "";88    for(var i=0;i<FEATURES.length;i++){ var f=FEATURES[i]; feats+='<div class="cell"><div class="gi">'+f.icon+'</div><div class="gt">'+f.title+'</div><div class="gp" style="color:#8a90a2;font-weight:500;font-size:10px;">'+f.desc+'</div></div>'; }89    var plans = "";90    for(var j=0;j<2;j++){ var p=RECHARGE_PLANS[j]; plans+=''+91      '<div class="plan '+(p.hot?"hot":"")+'">'+(p.tag?'<span class="tag">'+p.tag+'</span>':'')+92        '<div class="amt">'+money0(p.amount)+' <small>本金</small></div>'+93        '<div class="gift"><div class="g">赠 '+money0(p.gift)+'</div><div class="gt">+'+p.points+'分 · '+p.coupon.name+'</div></div>'+94      '</div>'; }95    var nts = "";96    for(var k=0;k<NOTICES.length;k++){ var n=NOTICES[k]; nts+='<div class="notice"><div class="nt"><div class="t">'+n.title+'</div><div class="x">'+n.text+'</div></div><div class="tm">'+n.time+'</div></div>'; }97    return topbar()+98      '<div class="banner"><div class="bage">🎉</div><div class="bt">会员日倒计时 '+MEMBER_DAY.countdownDays+' 天</div><div class="bs">'+STORE.memberDay+' 日全店积分翻倍 · 金卡以上再叠加折扣</div></div>'+99      '<div class="section"><h3><span class="bar"></span>快捷入口</h3>'+100        '<div class="quick">'+101          '<div class="q" data-to="#/recharge"><div class="qi">💰</div><div class="qt">储值</div></div>'+102          '<div class="q" data-to="#/mall"><div class="qi">🎁</div><div class="qt">积分商城</div></div>'+103          '<div class="q" data-to="#/coupons"><div class="qi">🎟️</div><div class="qt">我的券</div></div>'+104          '<div class="q" data-to="#/signin"><div class="qi">📅</div><div class="qt">签到</div></div>'+105        '</div>'+106      '</div>'+107      '<div class="section"><h3><span class="bar"></span>功能亮点<span class="more" data-to="#/member">会员中心 ›</span></h3><div class="grid">'+feats+'</div></div>'+108      '<div class="section"><h3><span class="bar"></span>储值推荐<span class="more" data-to="#/recharge">去储值 ›</span></h3>'+plans+'</div>'+109      '<div class="section"><h3><span class="bar"></span>消息通知</h3>'+nts+'</div>'+110      '<div style="height:8px"></div>';111  }112113  /* ============================================================114     视图:储值中心115     ============================================================ */116  function viewRecharge(){117    var ml = lv(MEMBER.level);118    var next = null; for(var i=0;i<MEMBER_LEVELS.length;i++){ if(MEMBER_LEVELS[i].key===MEMBER.level){ next = MEMBER_LEVELS[i+1]||null; break; } }119    var need = next ? Math.max(0, next.minSpend - MEMBER.totalSpend) : 0;120    var pct = next ? Math.min(100, Math.round(MEMBER.totalSpend/next.minSpend*100)) : 100;121    var plans="";122    for(var j=0;j<RECHARGE_PLANS.length;j++){ var p=RECHARGE_PLANS[j];123      plans+=''+124      '<div class="plan '+(p.hot?"hot":"")+'">'+(p.tag?'<span class="tag">'+p.tag+'</span>':'')+125        '<div class="amt">'+money0(p.amount)+' <small>本金</small></div>'+126        '<div class="gift"><div class="g">赠 '+money0(p.gift)+'</div><div class="gt">+'+p.points+'分 · '+p.coupon.name+'</div></div>'+127        '<div data-to="#/recharge/plan?plan='+p.id+'" style="margin-left:10px"><span class="btn sm blue">立即储值</span></div>'+128      '</div>';129    }130    return subbar("储值中心")+131      '<div class="hero"><div class="h1">我的储值余额</div>'+132        '<div style="font-size:30px;font-weight:800;margin-top:6px">'+money0(S.balance+S.gift)+'</div>'+133        '<div class="split" style="margin-top:10px">'+134          '<div class="box" style="background:rgba(255,255,255,.15)"><div class="n">'+money0(S.balance)+'</div><div class="l">本金(可提现)</div></div>'+135          '<div class="box" style="background:rgba(255,255,255,.15)"><div class="n">'+money0(S.gift)+'</div><div class="l">赠送金(营销)</div></div>'+136        '</div>'+137      '</div>'+138      (next?('<div class="section"><div class="row"><span class="k">升级进度('+ml.name+' → '+next.name+')</span><span class="v">'+pct+'%</span></div>'+139        '<div class="progress"><i style="width:'+pct+'%"></i></div>'+140        '<div class="muted">再消费 '+money0(need)+' 即可升级 '+next.name+',享 '+discountText(next.discount)+'</div></div>'):'')+141      '<div class="section"><h3><span class="bar"></span>储值套餐(阶梯赠送)</h3>'+plans+'</div>'+142      '<div class="section"><div class="row"><span class="k">储值专属折扣</span><span class="v">金卡 9 折 · 钻石 8.5 折</span></div>'+143        '<div class="row"><span class="k">赠送金规则</span><span class="v">本金/赠送金分账</span></div>'+144        '<div class="row"><span class="k">余额提醒</span><span class="v">不足自动推送</span></div>'+145        '<div class="row" data-to="#/recharge/record"><span class="k">储值 / 消费明细</span><span class="v" style="color:var(--blue)">查看 ›</span></div>'+146      '</div>'+147      '<div style="height:8px"></div>';148  }149150  /* ============================================================151     视图:储值下单152     ============================================================ */153  function viewPlan(q){154    var p = planById(getParam(q,"plan")) || RECHARGE_PLANS[1];155    return subbar("储值下单")+156      '<div class="section"><div class="plan hot" style="margin-bottom:0">'+(p.tag?'<span class="tag">'+p.tag+'</span>':'')+157        '<div class="amt">'+money0(p.amount)+' <small>本金</small></div>'+158        '<div class="gift"><div class="g">赠 '+money0(p.gift)+'</div><div class="gt">+'+p.points+'分</div></div></div>'+159        '<div class="row"><span class="k">到账本金</span><span class="v">'+money0(p.amount)+'</span></div>'+160        '<div class="row"><span class="k">赠送金</span><span class="v" style="color:var(--red)">'+money0(p.gift)+'</span></div>'+161        '<div class="row"><span class="k">赠送积分</span><span class="v">'+p.points+' 分</span></div>'+162        '<div class="row"><span class="k">赠送券</span><span class="v">'+p.coupon.name+'</span></div>'+163        '<div class="row"><span class="k">实付金额</span><span class="v" style="color:var(--red)">'+money0(p.amount)+'</span></div>'+164      '</div>'+165      '<div class="section"><h3><span class="bar"></span>支付方式</h3>'+166        '<div class="row"><span class="k">🟢 微信支付</span><span class="v">推荐</span></div>'+167        '<div class="row"><span class="k">🔵 支付宝</span><span class="v">可用</span></div>'+168        '<div class="row"><span class="k">💳 余额混合支付</span><span class="v">余额 + 差价</span></div>'+169      '</div>'+170      '<label class="section" style="display:flex;gap:8px;align-items:center;font-size:12px;color:var(--sub)"><input type="checkbox" checked> 我已阅读并同意《储值会员协议》</label>'+171      '<div class="fixedbar"><div class="btn blue" data-act="dopay" data-plan="'+p.id+'">确认支付 '+money0(p.amount)+'</div></div>'+172      '<div style="height:70px"></div>';173  }174175  /* ============================================================176     视图:储值 / 消费明细177     ============================================================ */178  function viewRecord(){179    var totalIn=0,totalOut=0;180    for(var i=0;i<S.transactions.length;i++){ var t=S.transactions[i]; if(t.type==="recharge") totalIn+=t.principal+t.gift; else totalOut+=t.principal; }181    var list="";182    for(var j=0;j<S.transactions.length;j++){ var t=S.transactions[j];183      var cls = t.type==="recharge"?"in":"out";184      var ic = t.type==="recharge"?"⬇️":"⬆️";185      var sign = t.type==="recharge"?"+":"-";186      list+='<div class="tx"><div class="ic '+cls+'">'+ic+'</div>'+187        '<div class="tt"><div class="t">'+t.title+'</div><div class="d">'+t.date+' · '+t.note+'</div></div>'+188        '<div class="am"><div class="'+cls+'">'+sign+money0(t.principal)+'</div>'+189        (t.gift>0?'<div class="sub">赠 '+money0(t.gift)+'</div>':'')+190        '<div class="sub">余 '+money0(t.balanceAfter)+'</div></div></div>';191    }192    return subbar("储值 / 消费明细")+193      '<div class="section"><div class="split">'+194        '<div class="box"><div class="n" style="color:var(--green)">'+money0(totalIn)+'</div><div class="l">累计储值(含赠)</div></div>'+195        '<div class="box"><div class="n" style="color:var(--navy)">'+money0(totalOut)+'</div><div class="l">累计消费</div></div>'+196      '</div></div>'+197      '<div class="section"><h3><span class="bar"></span>交易流水(本金 / 赠送金分列)</h3>'+list+'</div>'+198      '<div style="height:8px"></div>';199  }200201  /* ============================================================202     视图:会员中心203     ============================================================ */204  function viewMember(){205    var ml = lv(MEMBER.level);206    var next=null; for(var i=0;i<MEMBER_LEVELS.length;i++){ if(MEMBER_LEVELS[i].key===MEMBER.level){ next=MEMBER_LEVELS[i+1]||null; break; } }207    var need = next?Math.max(0,next.minSpend-MEMBER.totalSpend):0;208    var pct = next?Math.min(100,Math.round(MEMBER.totalSpend/next.minSpend*100)):100;209    var init = MEMBER.name.charAt(0);210    var权益=''+211      '<div class="grid">'+212        '<div class="cell" data-to="#/birthday"><div class="gi">🎂</div><div class="gt">生日礼</div></div>'+213        '<div class="cell" data-to="#/memberday"><div class="gi">🎉</div><div class="gt">会员日</div></div>'+214        '<div class="cell" data-to="#/recharge"><div class="gi">💰</div><div class="gt">储值专享</div></div>'+215        '<div class="cell" data-to="#/mine"><div class="gi">🛎️</div><div class="gt">专属客服</div></div>'+216      '</div>';217    return subbar("会员中心")+218      '<div class="lvlcard" style="background:linear-gradient(135deg,'+ml.color+','+ml.color+'cc)">'+219        '<div style="display:flex;align-items:center;gap:12px">'+220          '<div style="width:50px;height:50px;border-radius:50%;background:rgba(255,255,255,.25);display:flex;align-items:center;justify-content:center;font-size:24px;font-weight:800">'+init+'</div>'+221          '<div><div class="ln">'+MEMBER.name+'</div><div class="ld">'+MEMBER.phone+' · '+ml.name+'</div></div>'+222          '<div style="margin-left:auto" class="lvl" style="background:#fff;color:'+ml.color+'">'+discountText(ml.discount)+'</div>'+223        '</div>'+224        '<div class="lb"><span>生日 '+MEMBER.birthday+'</span><span>入会 '+MEMBER.joinedAt+'</span></div>'+225      '</div>'+226      (next?('<div class="section"><div class="row"><span class="k">升级进度('+ml.name+' → '+next.name+')</span><span class="v">'+pct+'%</span></div>'+227        '<div class="progress"><i style="width:'+pct+'%"></i></div>'+228        '<div class="muted">再消费 '+money0(need)+' 升级,享 '+discountText(next.discount)+' 与更多权益</div>'+229        '<div class="btn ghost sm" style="margin-top:10px" data-to="#/levels">查看等级权益</div></div>'):'')+230      '<div class="section"><h3><span class="bar"></span>我的权益</h3>'+权益+'</div>'+231      '<div style="height:8px"></div>';232  }233234  /* ============================================================235     视图:会员等级总览236     ============================================================ */237  function viewLevels(){238    var cards="";239    for(var i=0;i<MEMBER_LEVELS.length;i++){ var L=MEMBER_LEVELS[i];240      var b=""; for(var j=0;j<L.benefits.length;j++){ b+='<span>'+L.benefits[j]+'</span>'; }241      cards+='<div class="lvlcard" style="background:linear-gradient(135deg,'+L.color+','+L.color+'cc)">'+242        '<div class="ln">'+L.name+' · '+discountText(L.discount)+'</div>'+243        '<div class="ld">升级条件:累计消费满 '+money0(L.minSpend)+'</div>'+244        '<div class="lb">'+b+'</div></div>';245    }246    return subbar("会员等级")+247      '<div class="section"><h3><span class="bar"></span>4 档等级 · 折扣 0.1~9.9 折自定义</h3>'+cards+'</div>'+248      '<div class="section"><div class="row"><span class="k">自动升级</span><span class="v">累计消费 / 积分达标</span></div>'+249        '<div class="row"><span class="k">升级通知</span><span class="v">短信 + 微信推送</span></div>'+250        '<div class="row"><span class="k">权益生效</span><span class="v">即时同步</span></div></div>'+251      '<div style="height:8px"></div>';252  }253254  /* ============================================================255     视图:积分商城256     ============================================================ */257  function viewMall(){258    var cells="";259    for(var i=0;i<MALL_ITEMS.length;i++){ var g=MALL_ITEMS[i];260      cells+='<div class="cell" data-to="#/mall/detail?id='+g.id+'">'+261        '<div class="gi">'+g.icon+'</div><div class="gt">'+g.name+'</div>'+262        '<div class="gp">'+g.points+' 分</div>'+(g.hot?'<div class="tag" style="margin-top:2px">热</div>':'')+'</div>';263    }264    return subbar("积分商城")+265      '<div class="hero"><div class="h1">我的积分</div><div style="font-size:30px;font-weight:800;margin-top:4px">'+S.points+' 分</div>'+266        '<div class="h2">消费 1 元 = 1 分 · 签到 / 分享 / 储值均可赚分</div></div>'+267      '<div class="section"><h3><span class="bar"></span>积分商城<span class="more" data-to="#/coupons">我的券 ›</span></h3><div class="grid">'+cells+'</div></div>'+268      '<div class="section"><h3><span class="bar"></span>赚积分渠道</h3>'+269        '<div class="row"><span class="k">🛍️ 消费获分</span><span class="v">1 元 = 1 分</span></div>'+270        '<div class="row"><span class="k">💰 储值赠分</span><span class="v">大额额外送</span></div>'+271        '<div class="row"><span class="k">📅 每日签到</span><span class="v">最高 50 分</span></div>'+272        '<div class="row"><span class="k">🤝 老带新</span><span class="v">+200 分</span></div>'+273      '</div>'+274      '<div style="height:8px"></div>';275  }276277  /* ============================================================278     视图:积分商城详情 / 兑换279     ============================================================ */280  function viewMallDetail(q){281    var g = mallById(getParam(q,"id")) || MALL_ITEMS[0];282    return subbar("商品详情")+283      '<div class="section center">'+284        '<div style="font-size:60px">'+g.icon+'</div>'+285        '<div style="font-size:18px;font-weight:800;margin-top:6px">'+g.name+'</div>'+286        '<div class="tag" style="margin-top:6px">'+g.tag+'</div>'+287        '<div class="big gold" style="margin-top:10px">'+g.points+' 分</div>'+288        '<div class="muted">库存 '+g.stock+' 件 · 已兑 '+(S.mallRedeemed[g.id]||0)+' 件</div>'+289      '</div>'+290      '<div class="section"><h3><span class="bar"></span>兑换说明</h3>'+291        '<div class="row"><span class="k">所需积分</span><span class="v">'+g.points+' 分</span></div>'+292        '<div class="row"><span class="k">我的积分</span><span class="v">'+S.points+' 分</span></div>'+293        '<div class="row"><span class="k">兑换方式</span><span class="v">积分兑换 / 余额补差</span></div>'+294        '<div class="row"><span class="k">到账</span><span class="v">存入卡包</span></div>'+295      '</div>'+296      '<div class="fixedbar"><div class="btn '+(S.points>=g.points?"":"ghost")+'" data-act="exchange" data-id="'+g.id+'">'+(S.points>=g.points?("立即兑换 · "+g.points+" 分"):"积分不足")+'</div></div>'+297      '<div style="height:70px"></div>';298  }299300  /* ============================================================301     视图:我的优惠券 + 领券中心302     ============================================================ */303  function cpRow(c, actHtml){304    var leftTxt, leftCls="";305    if(c.type==="discount"){ leftTxt='<div class="v">'+(c.val/10).toFixed(1).replace(/\.0$/,"")+'</div><div class="vt">折</div>'; }306    else if(c.type==="times"){ leftTxt='<div class="v">×'+c.val+'</div><div class="vt">倍</div>'; leftCls="times"; }307    else { leftTxt='<div class="v">'+c.val+'</div><div class="vt">元</div>'; leftCls="dz"; }308    var cond = c.cond>0?("满"+c.cond+"可用"):"无门槛";309    return '<div class="cp '+leftCls+'"><div class="left">'+leftTxt+'</div>'+310      '<div class="right"><div class="cn">'+c.name+'</div><div class="cd">'+cond+'</div><div class="ce">有效期至 '+c.expire+'</div></div>'+311      (actHtml?'<div class="act">'+actHtml+'</div>':'')+'</div>';312  }313  function viewCoupons(){314    var list="";315    for(var i=0;i<S.coupons.length;i++){ var c=S.coupons[i];316      var act = c.status==="used"?'<span class="tag gray">已使用</span>':'<span class="btn sm" data-act="transfer" data-id="'+c.id+'">转赠</span>';317      list+=cpRow(c, act);318    }319    var pool="";320    for(var j=0;j<COUPON_POOL.length;j++){ var p=COUPON_POOL[j];321      pool+=cpRow(p, p.get?'<span class="btn sm blue" data-act="getcoupon" data-id="'+p.id+'">领取</span>':'<span class="tag gray">暂不可领</span>');322    }323    return subbar("我的优惠券")+324      '<div class="section"><h3><span class="bar"></span>我的券('+S.coupons.length+' 张)</h3>'+(list||'<div class="empty">暂无优惠券</div>')+'</div>'+325      '<div class="section"><h3><span class="bar"></span>领券中心</h3>'+pool+'</div>'+326      '<div style="height:8px"></div>';327  }328329  /* ============================================================330     视图:积分明细331     ============================================================ */332  function viewPoints(){333    // 由交易派生积分收支334    var logs=[];335    for(var i=0;i<S.transactions.length;i++){ var t=S.transactions[i];336      if(t.type==="recharge"){ logs.push({d:t.date, t:"储值赠分", v:"+"+planPointsOf(t), c:"green"}); }337    }338    // 签到积分339    var signed=0; for(var k=0;k<S.signDays.length;k++){ if(S.signDays[k]){ signed++; } }340    if(signed>0) logs.push({d:"近 7 日", t:"连续签到 ×"+signed, v:"+"+(signed*5)+" 分", c:"green"});341    var html="";342    for(var m=0;m<logs.length;m++){ html+='<div class="tx"><div class="ic in">⭐</div><div class="tt"><div class="t">'+logs[m].t+'</div><div class="d">'+logs[m].d+'</div></div><div class="am"><div class="in">'+logs[m].v+'</div></div></div>'; }343    return subbar("积分明细")+344      '<div class="hero"><div class="h1">当前积分</div><div style="font-size:30px;font-weight:800;margin-top:4px">'+S.points+' 分</div>'+345        '<div class="h2">100 积分 = 1 元 · 可抵现 / 兑礼 / 兑券</div></div>'+346      '<div class="section"><h3><span class="bar"></span>积分收支</h3>'+(html||'<div class="empty">暂无记录</div>')+'</div>'+347      '<div style="height:8px"></div>';348  }349  function planPointsOf(t){ // 粗略反推:本演示按本金估算赠分350    var p=planById("p2"); return Math.round(t.principal*1.2);351  }352353  /* ============================================================354     视图:签到355     ============================================================ */356  function viewSignin(){357    var cells="";358    for(var i=0;i<SIGNIN.days.length;i++){ var d=SIGNIN.days[i]; var on=S.signDays[i];359      cells+='<div class="s '+(on?"on":"")+'"><div class="sd">'+(on?"✓":("第"+d.day+"天"))+'</div><div class="sr">'+d.label+'</div></div>';360    }361    var signed=0; for(var k=0;k<S.signDays.length;k++){ if(S.signDays[k]) signed++; }362    return subbar("签到")+363      '<div class="section center"><div style="font-size:40px">📅</div><div style="font-size:17px;font-weight:800;margin-top:4px">连续签到 '+signed+' 天</div>'+364        '<div class="muted">每日签到领积分,第 7 天大奖 50 分</div></div>'+365      '<div class="section"><h3><span class="bar"></span>7 日签到</h3><div class="sign">'+cells+'</div>'+366        '<div style="margin-top:14px">'+(S.signedToday?'<div class="btn ghost">今日已签到 ✓</div>':'<div class="btn" data-act="signin">立即签到 +'+(SIGNIN.days[Math.min(signed,SIGNIN.days.length-1)].reward)+' 分</div>')+'</div>'+367      '</div>'+368      '<div style="height:8px"></div>';369  }370371  /* ============================================================372     视图:生日礼373     ============================================================ */374  function viewBirthday(){375    var items="";376    for(var i=0;i<BIRTHDAY.pack.length;i++){ var p=BIRTHDAY.pack[i];377      items+='<div class="row"><span class="k">🎁 '+p.name+'</span><span class="v"><span class="btn sm" data-act="claimbirthday" data-i="'+i+'">领取</span></span></div>';378    }379    return subbar("生日礼")+380      '<div class="banner" style="background:linear-gradient(135deg,#ffd1e8,#ff9ec7);color:#7a2a4d"><div class="bage">🎂</div><div class="bt">'+BIRTHDAY.greet+'</div><div class="bs">生日当月专享 · 情感化唤醒</div></div>'+381      '<div class="section"><h3><span class="bar"></span>生日礼包</h3>'+items+'</div>'+382      '<div class="section"><div class="row"><span class="k">生日月特权</span><span class="v">'+(BIRTHDAY.monthPrivilege?"已开启":"未开启")+'</span></div>'+383        '<div class="row"><span class="k">自动触达</span><span class="v">提前 3 天推送</span></div>'+384        '<div class="row"><span class="k">专属折扣</span><span class="v">生日当月双倍积分</span></div></div>'+385      '<div style="height:8px"></div>';386  }387388  /* ============================================================389     视图:会员日390     ============================================================ */391  function viewMemberDay(){392    var b="";393    for(var i=0;i<MEMBER_DAY.benefits.length;i++){ var x=MEMBER_DAY.benefits[i]; b+='<div class="row"><span class="k">'+x.icon+' '+x.text+'</span><span class="v">✓</span></div>'; }394    return subbar("会员日")+395      '<div class="hero"><div class="h1">会员日 · 每月 '+STORE.memberDay+' 日</div>'+396        '<div style="font-size:30px;font-weight:800;margin-top:4px">倒计时 '+MEMBER_DAY.countdownDays+' 天</div>'+397        '<div class="h2">锁定复购的黄金节点</div></div>'+398      '<div class="section"><h3><span class="bar"></span>当日权益</h3>'+b+'</div>'+399      '<div class="section"><div class="row"><span class="k">积分翻倍</span><span class="v">消费 ×2 积分</span></div>'+400        '<div class="row"><span class="k">专享折扣</span><span class="v">全单 9 折</span></div>'+401        '<div class="row"><span class="k">满赠</span><span class="v">满 100 赠礼</span></div></div>'+402      '<div class="fixedbar"><div class="btn blue" data-act="memberday">预约会员日提醒</div></div>'+403      '<div style="height:70px"></div>';404  }405406  /* ============================================================407     视图:老带新 / 推荐有礼408     ============================================================ */409  function viewInvite(){410    var list="";411    for(var i=0;i<INVITE.list.length;i++){ var x=INVITE.list[i];412      list+='<div class="row"><span class="k">'+x.name+' · '+x.phone+'</span><span class="v">'+x.status+'</span></div>'+413        '<div class="muted" style="margin:-6px 0 6px">奖励:'+x.reward+'</div>';414    }415    return subbar("老带新")+416      '<div class="banner"><div class="bage">🤝</div><div class="bt">'+INVITE.poster.title+'</div><div class="bs">'+INVITE.poster.sub+'</div></div>'+417      '<div class="section"><h3><span class="bar"></span>活动规则</h3><div class="muted">'+INVITE.rule+'</div></div>'+418      '<div class="section"><div class="row"><span class="k">我已成功邀请</span><span class="v">'+S.inviteCount+' 人</span></div></div>'+419      '<div class="section"><h3><span class="bar"></span>邀请记录</h3>'+list+'</div>'+420      '<div class="fixedbar"><div class="btn" data-act="invite">生成专属邀请海报</div></div>'+421      '<div style="height:70px"></div>';422  }423424  /* ============================================================425     视图:员工提成 / 排行榜(老板视角)426     ============================================================ */427  function viewStaff(){428    var rows="";429    var sorted = STAFF.slice().sort(function(a,b){ return b.commission-a.commission; });430    for(var i=0;i<sorted.length;i++){ var s=sorted[i];431      rows+='<div class="rank"><div class="no '+(i===0?"top":"")+'">'+(i+1)+'</div>'+432        '<div class="nm">'+s.name+'<div class="st">开卡 '+s.opened+' · 储值 '+money0(s.recharge)+' · 消费 '+money0(s.consume)+'</div></div>'+433        '<div class="cm">'+money0(s.commission)+'</div></div>';434    }435    return subbar("员工业绩")+436      '<div class="hero"><div class="h1">门店提成总览</div><div style="font-size:22px;font-weight:800;margin-top:4px">本月已发 '+(function(){var t=0;for(var i=0;i<STAFF.length;i++)t+=STAFF[i].commission;return money0(t);})()+'</div>'+437        '<div class="h2">'+STAFF_RULES+'</div></div>'+438      '<div class="section"><h3><span class="bar"></span>业绩排行榜</h3>'+rows+'</div>'+439      '<div class="section"><div class="row"><span class="k">我的身份</span><span class="v">店主(可提现)</span></div>'+440        '<div class="row"><span class="k">佣金到账</span><span class="v">微信零钱 T+1</span></div></div>'+441      '<div style="height:8px"></div>';442  }443444  /* ============================================================445     视图:礼品卡 / 转赠446     ============================================================ */447  function viewGiftCard(){448    var cards="";449    for(var i=0;i<GIFT_CARDS.length;i++){ var g=GIFT_CARDS[i];450      cards+='<div class="cell" style="text-align:left"><div style="font-size:30px">'+g.icon+'</div><div style="font-weight:800;margin-top:4px">'+g.name+'</div>'+451        '<div class="muted">售价 '+money0(g.price)+'</div><div class="btn sm blue" style="margin-top:6px" data-act="buygift" data-id="'+g.id+'">购买</div></div>';452    }453    var tr="";454    for(var j=0;j<S.transfers.length;j++){ var t=S.transfers[j];455      tr+='<div class="row"><span class="k">'+t.from+' → '+t.to+'</span><span class="v">'+t.item+'</span></div><div class="muted" style="margin:-6px 0 6px">'+t.date+'</div>';456    }457    return subbar("礼品卡")+458      '<div class="section"><h3><span class="bar"></span>礼品卡(赠好友)</h3><div class="grid">'+cards+'</div></div>'+459      '<div class="section"><h3><span class="bar"></span>转赠记录</h3>'+(tr||'<div class="empty">暂无转赠</div>')+'</div>'+460      '<div style="height:8px"></div>';461  }462463  /* ============================================================464     视图:经营数据看板(老板视角)465     ============================================================ */466  function viewDashboard(){467    var max=0; for(var i=0;i<DASHBOARD.rechargeTrend.length;i++){ if(DASHBOARD.rechargeTrend[i]>max) max=DASHBOARD.rechargeTrend[i]; }468    var bars="";469    for(var j=0;j<DASHBOARD.rechargeTrend.length;j++){470      var h=Math.round(DASHBOARD.rechargeTrend[j]/max*100);471      bars+='<div style="flex:1;text-align:center"><div style="height:80px;display:flex;align-items:flex-end;justify-content:center"><div style="width:60%;background:var(--gold-grad);border-radius:4px 4px 0 0;height:'+h+'%"></div></div><div class="muted" style="font-size:10px;margin-top:4px">'+DASHBOARD.trendDays[j]+'</div></div>';472    }473    var total=0; for(var k=0;k<DASHBOARD.memberLayers.length;k++) total+=DASHBOARD.memberLayers[k].count;474    var acc=0, conic="";475    for(var m=0;m<DASHBOARD.memberLayers.length;m++){ var pctv=Math.round(DASHBOARD.memberLayers[m].count/total*100); conic+=DASHBOARD.memberLayers[m].color+" "+(m===0?0:acc)+"% "+(acc+pctv)+"% "; acc+=pctv; }476    var legend="";477    for(var n=0;n<DASHBOARD.memberLayers.length;n++){ var L=DASHBOARD.memberLayers[n];478      legend+='<div class="row"><span class="k"><span style="display:inline-block;width:10px;height:10px;border-radius:3px;background:'+L.color+';margin-right:6px"></span>'+L.name+'</span><span class="v">'+L.count+' 人</span></div>';479    }480    return subbar("经营看板")+481      '<div class="section"><div class="donut">'+482        '<div class="d"><div class="big">'+DASHBOARD.totalMembers+'</div><div class="muted">会员总数</div></div>'+483        '<div class="d"><div class="big gold">'+money0(DASHBOARD.storedTotal)+'</div><div class="muted">储值总额</div></div>'+484      '</div><div class="donut" style="margin-top:10px">'+485        '<div class="d"><div class="big green">'+money0(DASHBOARD.todayRevenue)+'</div><div class="muted">今日营收</div></div>'+486        '<div class="d"><div class="big red">'+(DASHBOARD.repurchaseRate*100)+'%</div><div class="muted">复购率</div></div>'+487      '</div></div>'+488      '<div class="section"><h3><span class="bar"></span>近 7 日储值趋势</h3><div style="display:flex">'+bars+'</div></div>'+489      '<div class="section"><h3><span class="bar"></span>会员分层</h3>'+490        '<div style="display:flex;gap:14px;align-items:center">'+491          '<div style="width:110px;height:110px;border-radius:50%;background:conic-gradient('+conic+');position:relative"><div style="position:absolute;inset:22px;background:#fff;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--sub)">占比</div></div>'+492          '<div style="flex:1">'+legend+'</div>'+493        '</div></div>'+494      '<div class="section"><div class="row"><span class="k">数据导出</span><span class="v" style="color:var(--blue)">对账报表 ›</span></div>'+495        '<div class="row"><span class="k">实时同步</span><span class="v">多端一致</span></div></div>'+496      '<div style="height:8px"></div>';497  }498499  /* ============================================================500     视图:我的501     ============================================================ */502  function viewMine(){503    var ml = lv(MEMBER.level);504    var menu=[505      {i:"💰", t:"储值 / 消费明细", to:"#/recharge/record"},506      {i:"🎟️", t:"我的优惠券", to:"#/coupons"},507      {i:"⭐", t:"积分明细", to:"#/points"},508      {i:"📅", t:"每日签到", to:"#/signin"},509      {i:"🎂", t:"生日礼", to:"#/birthday"},510      {i:"🎉", t:"会员日", to:"#/memberday"},511      {i:"🤝", t:"老带新", to:"#/invite"},512      {i:"🎁", t:"礼品卡转赠", to:"#/giftcard"},513      {i:"📊", t:"经营数据看板", to:"#/dashboard"},514      {i:"🔔", t:"消息通知", to:"#/home"}515    ];516    var rows="";517    for(var i=0;i<menu.length;i++){ rows+='<div class="row" data-to="'+menu[i].to+'"><span class="k">'+menu[i].i+' '+menu[i].t+'</span><span class="v" style="color:var(--blue)">›</span></div>'; }518    return subbar("我的")+519      '<div class="lvlcard" style="background:linear-gradient(135deg,'+ml.color+','+ml.color+'cc)">'+520        '<div style="display:flex;align-items:center;gap:12px">'+521          '<div style="width:50px;height:50px;border-radius:50%;background:rgba(255,255,255,.25);display:flex;align-items:center;justify-content:center;font-size:22px;font-weight:800">'+MEMBER.name.charAt(0)+'</div>'+522          '<div><div class="ln">'+MEMBER.name+'</div><div class="ld">'+MEMBER.phone+' · '+ml.name+'('+discountText(ml.discount)+')</div></div>'+523        '</div>'+524        '<div class="split" style="margin-top:12px">'+525          '<div class="box" style="background:rgba(255,255,255,.15)"><div class="n">'+money0(S.balance)+'</div><div class="l">本金</div></div>'+526          '<div class="box" style="background:rgba(255,255,255,.15)"><div class="n">'+money0(S.gift)+'</div><div class="l">赠送金</div></div>'+527          '<div class="box" style="background:rgba(255,255,255,.15)"><div class="n">'+S.points+'</div><div class="l">积分</div></div>'+528        '</div>'+529      '</div>'+530      '<div class="section">'+rows+'</div>'+531      '<div class="section center"><div class="muted">'+STORE.name+' · '+STORE.address+'</div><div class="muted">客服 '+STORE.phone+'</div></div>'+532      '<div style="height:8px"></div>';533  }534535  /* ============================================================536     视图:会员支付折扣演示537     ============================================================ */538  function viewPay(){539    var ml = lv(MEMBER.level);540    var origin=88; var disc=+(origin*ml.discount).toFixed(2);541    return subbar("扫码支付")+542      '<div class="section center"><div style="font-size:40px">💳</div><div class="muted">会员支付自动识别折扣</div>'+543        '<div style="font-size:14px;margin-top:8px">原价 <s>'+money(origin)+'</s></div>'+544        '<div class="big red" style="font-size:34px">'+money(disc)+'</div>'+545        '<div class="tag">'+ml.name+' · '+discountText(ml.discount)+'</div></div>'+546      '<div class="section"><h3><span class="bar"></span>支付明细</h3>'+547        '<div class="row"><span class="k">商品金额</span><span class="v">'+money(origin)+'</span></div>'+548        '<div class="row"><span class="k">会员折扣</span><span class="v" style="color:var(--red)">-'+money((origin-disc).toFixed(2))+'</span></div>'+549        '<div class="row"><span class="k">可用券</span><span class="v">15 元满 50 券</span></div>'+550        '<div class="row"><span class="k">余额抵扣</span><span class="v">'+money0(Math.min(S.balance+S.gift, disc-15))+'</span></div>'+551        '<div class="row"><span class="k">实付</span><span class="v" style="color:var(--red)">'+money(Math.max(0,(disc-15)-(S.balance+S.gift>0?0:0)))+'</span></div>'+552      '</div>'+553      '<div class="section"><div class="row"><span class="k">支付方式</span><span class="v">微信 / 支付宝 / 余额</span></div>'+554        '<div class="row"><span class="k">积分到账</span><span class="v">+'+(Math.round(disc))+' 分</span></div></div>'+555      '<div class="fixedbar"><div class="btn blue" data-act="pay">确认支付 '+money(Math.max(0.01, disc-15))+'</div></div>'+556      '<div style="height:70px"></div>';557  }558559  /* ============================================================560     路由表561     ============================================================ */562  var ROUTES = {563    "home": viewHome,564    "recharge": viewRecharge,565    "recharge/plan": viewPlan,566    "recharge/record": viewRecord,567    "member": viewMember,568    "levels": viewLevels,569    "mall": viewMall,570    "mall/detail": viewMallDetail,571    "coupons": viewCoupons,572    "points": viewPoints,573    "signin": viewSignin,574    "birthday": viewBirthday,575    "memberday": viewMemberDay,576    "invite": viewInvite,577    "staff": viewStaff,578    "giftcard": viewGiftCard,579    "dashboard": viewDashboard,580    "mine": viewMine,581    "pay": viewPay582  };583584  /* ---------------- 渲染 ---------------- */585  function fullRoute(){586    var h = location.hash.replace(/^#\//,"");587    if(!h) return "home";588    return h;589  }590  function render(){591    var full = fullRoute();592    var q = "";593    if(full.indexOf("?")>=0){ q = full.split("?")[1]; full = full.split("?")[0]; }594    var base = full.split("/")[0];595    var fn = ROUTES[full] || viewHome;596    $("#app").html(fn(q));597    var tabBase = (base==="recharge"||base==="member"||base==="mall"||base==="mine")?base:"home";598    $("#tabbar").html(tabbar(tabBase));599    window.scrollTo(0,0);600  }601602  /* ---------------- 全局点击 ---------------- */603  $(document).on("click","[data-to]", function(e){604    e.preventDefault();605    var to = $(this).attr("data-to");606    if(to) location.hash = to;607  });608  $(document).on("click","[data-act]", function(){609    var act = $(this).attr("data-act");610    if(act==="dopay"){611      var p = planById($(this).attr("data-plan")) || RECHARGE_PLANS[1];612      S.balance += p.amount; S.gift += p.gift; S.points += p.points;613      S.transactions.unshift({ id:Date.now(), type:"recharge", title:"储值充值", date:nowStr(), principal:p.amount, gift:p.gift, balanceAfter:S.balance+S.gift, note:"充 "+p.amount+" 送 "+p.gift });614      if(p.coupon){ S.coupons.push({ id:"m"+Date.now(), name:p.coupon.name, type:"fullcut", val:p.coupon.val, cond:(p.coupon.val>=15?50:0), expire:"2026-12-31", status:"unused" }); }615      saveState(S); toast("储值成功,赠送金已到账"); location.hash="#/recharge";616    }617    else if(act==="signin"){618      if(S.signedToday){ toast("今日已签到"); return; }619      var idx=0; for(var i=0;i<S.signDays.length;i++){ if(!S.signDays[i]){ idx=i; break; } if(i===S.signDays.length-1) idx=i; }620      S.signDays[idx]=true; var rw=SIGNIN.days[idx].reward; S.points+=rw; S.signedToday=true; saveState(S); toast("签到成功 +"+rw+" 分"); render();621    }622    else if(act==="getcoupon"){623      var c=poolById($(this).attr("data-id")); if(!c) return;624      S.coupons.push({ id:"m"+Date.now(), name:c.name, type:c.type, val:c.val, cond:c.cond, expire:c.expire, status:"unused" });625      saveState(S); toast("领取成功,已存入卡包"); render();626    }627    else if(act==="exchange"){628      var g=mallById($(this).attr("data-id")); if(!g) return;629      if(S.points<g.points){ toast("积分不足,去赚分吧"); return; }630      S.points-=g.points; S.mallRedeemed[g.id]=(S.mallRedeemed[g.id]||0)+1; saveState(S); toast("兑换成功,已存入卡包"); location.hash="#/coupons";631    }632    else if(act==="transfer"){ toast("转赠链接已生成,可分享给好友"); }633    else if(act==="claimbirthday"){ toast("生日礼包已领取 🎂"); }634    else if(act==="memberday"){ toast("已预约会员日提醒 🔔"); }635    else if(act==="invite"){ toast("专属邀请海报已生成,可分享"); }636    else if(act==="buygift"){ toast("礼品卡购买链接已生成 🎁"); }637    else if(act==="pay"){ toast("支付成功,积分已到账 🎉"); }638  });639640  /* ---------------- 启动 ---------------- */641  $(window).on("hashchange", render);642  $(function(){ if(!location.hash) location.hash="#/home"; render(); });643})();644