首页/ mobileweb/ 云客 · 客户信息管理系统 H5/ 代码预览

💻 代码预览 · 云客 · 客户信息管理系统 H5

mw_admin · 上传者 驰旭教育
← 返回详情
js/main.js · 约 14928 字符
1\
2(function(){
3  var page=(location.pathname.split('/').pop()||'index.html').replace('.html','');
4  document.querySelectorAll('.tabbar a').forEach(function(a){if(a.getAttribute('href')===page+'.html')a.classList.add('on');});
5  function toast(m){var t=document.createElement('div');t.className='toast';t.textContent=m;document.body.appendChild(t);requestAnimationFrame(function(){t.classList.add('show');});setTimeout(function(){t.classList.remove('show');setTimeout(function(){t.remove();},300);},1700);}
6  function esc(s){return String(s==null?'':s).replace(/[&<>"']/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c];});}
7  function getParam(n){var m=location.search.match(new RegExp('[?&]'+n+'=([^&]*)'));return m?decodeURIComponent(m[1]):null;}
8  function gid(){return Date.now().toString(36)+Math.random().toString(36).slice(2,6);}
9  function todayStr(){var d=new Date();return d.getFullYear()+'-'+('0'+(d.getMonth()+1)).slice(-2)+'-'+('0'+d.getDate()).slice(-2);}
10  var CKEY='crm_customers',FKEY='crm_follows';
11  var SEED_C=[{"id": "c1", "name": "张伟", "phone": "13800138001", "company": "伟创科技有限公司", "level": "重要", "status": "已成交", "amount": 120000, "note": "企业采购负责人,关注批量折扣与售后响应。", "created": "2026-03-12"}, {"id": "c2", "name": "李娜", "phone": "13800138002", "company": "娜美服饰", "level": "普通", "status": "跟进中", "amount": 45000, "note": "电商卖家,需要节日营销方案。", "created": "2026-05-08"}, {"id": "c3", "name": "王强", "phone": "13800138003", "company": "强联物流", "level": "潜在", "status": "跟进中", "amount": 0, "note": "通过展会名片获取,待首次拜访。", "created": "2026-06-21"}, {"id": "c4", "name": "刘洋", "phone": "13800138004", "company": "洋帆文化传媒", "level": "重要", "status": "已成交", "amount": 88000, "note": "年度框架合同,季度复购。", "created": "2026-02-18"}, {"id": "c5", "name": "陈静", "phone": "13800138005", "company": "静思空间设计", "level": "普通", "status": "已流失", "amount": 20000, "note": "预算缩减,今年暂未续约。", "created": "2026-01-30"}, {"id": "c6", "name": "赵磊", "phone": "13800138006", "company": "磊鑫建材", "level": "潜在", "status": "跟进中", "amount": 0, "note": "工程行业,决策周期长。", "created": "2026-07-02"}],SEED_F=[{"id": "f1", "cid": "c1", "date": "2026-03-15", "type": "电话", "content": "初次沟通需求,确认采购清单 20 项。"}, {"id": "f2", "cid": "c1", "date": "2026-03-28", "type": "微信", "content": "发送正式报价单,客户确认无误。"}, {"id": "f3", "cid": "c1", "date": "2026-04-10", "type": "拜访", "content": "上门签约,合同金额 ¥120,000。"}, {"id": "f4", "cid": "c2", "date": "2026-05-12", "type": "电话", "content": "介绍夏季促销套餐,客户表示感兴趣。"}, {"id": "f5", "cid": "c2", "date": "2026-06-02", "type": "拜访", "content": "上门演示后台,收集修改意见 3 条。"}, {"id": "f6", "cid": "c3", "date": "2026-06-25", "type": "微信", "content": "发送公司画册,约定下周电话沟通。"}, {"id": "f7", "cid": "c4", "date": "2026-02-20", "type": "电话", "content": "续约洽谈,确认年度框架。"}, {"id": "f8", "cid": "c4", "date": "2026-04-18", "type": "邮件", "content": "发送 Q2 复购清单,已确认下单。"}, {"id": "f9", "cid": "c6", "date": "2026-07-05", "type": "电话", "content": "了解工程进度,预计 Q4 有采购需求。"}];
12  function lc(){try{return JSON.parse(localStorage.getItem(CKEY))||null}catch(e){return null}}
13  function sc(a){localStorage.setItem(CKEY,JSON.stringify(a));}
14  function lf(){try{return JSON.parse(localStorage.getItem(FKEY))||null}catch(e){return null}}
15  function sf(a){localStorage.setItem(FKEY,JSON.stringify(a));}
16  function ensureSeed(){if(lc()===null)sc(SEED_C);if(lf()===null)sf(SEED_F);}
17  function levelCls(l){return l==='重要'?'lv-key':(l==='潜在'?'lv-pot':'lv-nor');}
18  function statusCls(s){return s==='已成交'?'st-ok':(s==='已流失'?'st-lost':'st-ing');}
19  function fmtMoney(n){n=+n||0;return n?('¥'+n.toLocaleString()):'—';}
20  ensureSeed();
21
22  if(page==='index'){
23    var custs=lc(),fols=lf();
24    function stat(n,l){return '<div class="stat"><div class="n">'+n+'</div><div class="l">'+l+'</div></div>';}
25    function render(){
26      var kw=(document.getElementById('kw').value||'').trim().toLowerCase();
27      var f=document.querySelector('#seg .on').getAttribute('data-f');
28      var list=custs.filter(function(c){
29        if(f!=='all'&&c.level!==f)return false;
30        if(kw){var hay=(c.name+' '+(c.company||'')+' '+(c.phone||'')).toLowerCase();if(hay.indexOf(kw)<0)return false;}
31        return true;
32      });
33      var total=custs.length;
34      var monthNew=custs.filter(function(c){return c.created&&c.created.indexOf('2026-07')>=0;}).length;
35      var ing=custs.filter(function(c){return c.status==='跟进中';}).length;
36      var deal=custs.filter(function(c){return c.status==='已成交';}).reduce(function(s,c){return s+(+c.amount||0);},0);
37      document.getElementById('stats4').innerHTML=stat(total,'总客户')+stat(monthNew,'本月新增')+stat(ing,'跟进中')+'<div class="stat"><div class="n g">'+fmtMoney(deal)+'</div><div class="l">成交金额</div></div>';
38      var el=document.getElementById('custList');
39      if(!list.length){el.innerHTML='<div class="empty">没有匹配的客户</div>';return;}
40      el.innerHTML=list.map(function(c){
41        return '<a class="cust" href="detail.html?id='+c.id+'">'
42          +'<div class="av">'+esc((c.name||'?')[0])+'</div>'
43          +'<div class="ci"><div class="cn">'+esc(c.name)+' <span class="tag '+levelCls(c.level)+'">'+c.level+'</span> <span class="tag '+statusCls(c.status)+'">'+c.status+'</span></div>'
44          +'<div class="cm">'+esc(c.company||'—')+' · '+esc(c.phone||'')+'</div>'
45          +'<div class="cam">意向 '+fmtMoney(c.amount)+'</div></div>'
46          +'<div class="ca">›</div></a>';
47      }).join('');
48    }
49    document.getElementById('kw').addEventListener('input',render);
50    document.getElementById('seg').addEventListener('click',function(e){var s=e.target.closest('span');if(!s)return;document.querySelectorAll('#seg span').forEach(function(x){x.classList.remove('on');});s.classList.add('on');render();});
51    render();
52  }
53
54  if(page==='add'||page==='edit'){
55    var isEdit=(page==='edit');
56    var head=document.getElementById('formHead');
57    if(head)head.innerHTML=isEdit?'✏️ 修改客户':'➕ 添加客户';
58    var custs=lc();
59    var cur=null;
60    if(isEdit){
61      var id=getParam('id');
62      cur=custs.filter(function(x){return x.id===id;})[0]||custs[0];
63      if(!cur){location='index.html';return;}
64      document.getElementById('name').value=cur.name;
65      document.getElementById('phone').value=cur.phone;
66      document.getElementById('company').value=cur.company||'';
67      document.getElementById('level').value=cur.level;
68      document.getElementById('status').value=cur.status;
69      document.getElementById('amount').value=cur.amount||'';
70      document.getElementById('note').value=cur.note||'';
71    }
72    document.getElementById('f').addEventListener('submit',function(e){
73      e.preventDefault();
74      var name=document.getElementById('name').value.trim();
75      var phone=document.getElementById('phone').value.trim();
76      if(!name||!phone){toast('请填写姓名和电话');return;}
77      if(isEdit){
78        cur.name=name;cur.phone=phone;
79        cur.company=document.getElementById('company').value.trim();
80        cur.level=document.getElementById('level').value;
81        cur.status=document.getElementById('status').value;
82        cur.amount=document.getElementById('amount').value.replace(/[^\d]/g,'')*1||0;
83        cur.note=document.getElementById('note').value.trim();
84        sc(custs);toast('已保存修改 ✅');setTimeout(function(){location='detail.html?id='+cur.id;},900);
85      }else{
86        custs.push({id:gid(),name:name,phone:phone,company:document.getElementById('company').value.trim(),
87          level:document.getElementById('level').value,status:document.getElementById('status').value,
88          amount:document.getElementById('amount').value.replace(/[^\d]/g,'')*1||0,
89          note:document.getElementById('note').value.trim(),created:todayStr()});
90        sc(custs);toast('已添加客户 ✅');setTimeout(function(){location='index.html';},900);
91      }
92    });
93  }
94
95  if(page==='detail'){
96    var custs=lc(),fols=lf();
97    var id=getParam('id');
98    var c=custs.filter(function(x){return x.id===id;})[0]||custs[0];
99    if(!c){location='index.html';return;}
100    document.getElementById('dHead').innerHTML='<div class="d-head"><div class="av">'+esc((c.name||'?')[0])+'</div><div><div class="nm">'+esc(c.name)+' <span class="tag '+levelCls(c.level)+'">'+c.level+'</span> <span class="tag '+statusCls(c.status)+'">'+c.status+'</span></div><div class="cp">'+esc(c.company||'—')+'</div></div></div>';
101    document.getElementById('dInfo').innerHTML=
102      '<div class="kv"><span class="k">联系电话</span><span class="v"><a href="tel:'+esc(c.phone)+'" style="color:var(--pri)">'+esc(c.phone)+'</a></span></div>'
103      +'<div class="kv"><span class="k">客户等级</span><span class="v">'+c.level+'</span></div>'
104      +'<div class="kv"><span class="k">跟进状态</span><span class="v">'+c.status+'</span></div>'
105      +'<div class="kv"><span class="k">意向金额</span><span class="v">'+fmtMoney(c.amount)+'</span></div>'
106      +'<div class="kv"><span class="k">录入日期</span><span class="v">'+esc(c.created||'—')+'</span></div>'
107      +'<div class="kv"><span class="k">备注</span><span class="v">'+esc(c.note||'—')+'</span></div>';
108    function renderF(){
109      var fs=fols.filter(function(f){return f.cid===c.id;}).sort(function(a,b){return a.date<b.date?1:-1;});
110      document.getElementById('fCount').textContent='共 '+fs.length+' 条';
111      var el=document.getElementById('fList');
112      if(!fs.length){el.innerHTML='<div class="empty">暂无跟进记录,点击上方“添加跟进”</div>';return;}
113      el.innerHTML=fs.map(function(f){return '<div class="fol"><div class="fh"><span class="nm">'+esc(c.name)+'</span><span class="ty">'+esc(f.type)+'</span><span class="dt">'+esc(f.date)+'</span></div><div class="fb">'+esc(f.content)+'</div></div>';}).join('');
114    }
115    renderF();
116    document.getElementById('editBtn').setAttribute('href','edit.html?id='+c.id);
117    document.getElementById('followBtn').setAttribute('href','follow.html?cid='+c.id);
118    document.getElementById('delBtn').addEventListener('click',function(){
119      if(!confirm('确认删除客户「'+c.name+'」?此操作不可恢复。'))return;
120      sc(custs.filter(function(x){return x.id!==c.id;}));
121      sf(fols.filter(function(f){return f.cid!==c.id;}));
122      toast('已删除');setTimeout(function(){location='index.html';},800);
123    });
124  }
125
126  if(page==='follow'){
127    var custs=lc(),fols=lf();
128    var sel=document.getElementById('cid');
129    sel.innerHTML=custs.map(function(c){return '<option value="'+c.id+'">'+esc(c.name)+'('+esc(c.company||'—')+')</option>';}).join('');
130    var pre=getParam('cid');
131    if(pre)sel.value=pre;
132    document.getElementById('fdate').value=todayStr();
133    function render(){
134      var fs=fols.slice().sort(function(a,b){return a.date<b.date?1:-1;});
135      document.getElementById('fc').textContent='共 '+fs.length+' 条';
136      var el=document.getElementById('folList');
137      if(!fs.length){el.innerHTML='<div class="empty">暂无跟进记录</div>';return;}
138      var map={};custs.forEach(function(c){map[c.id]=c.name;});
139      el.innerHTML=fs.map(function(f){return '<div class="fol"><div class="fh"><span class="nm">'+esc(map[f.cid]||'客户')+'</span><span class="ty">'+esc(f.type)+'</span><span class="dt">'+esc(f.date)+'</span></div><div class="fb">'+esc(f.content)+'</div></div>';}).join('');
140    }
141    render();
142    document.getElementById('ff').addEventListener('submit',function(e){
143      e.preventDefault();
144      var cid=sel.value,content=document.getElementById('fcontent').value.trim();
145      if(!content){toast('请填写跟进内容');return;}
146      fols.unshift({id:gid(),cid:cid,type:document.getElementById('ftype').value,date:document.getElementById('fdate').value||todayStr(),content:content});
147      sf(fols);render();document.getElementById('ff').reset();sel.value=cid;document.getElementById('fdate').value=todayStr();toast('跟进已记录 ✅');
148    });
149  }
150
151  if(page==='stats'){
152    var custs=lc(),fols=lf();
153    var tot=custs.length||1;
154    var lv={};custs.forEach(function(c){lv[c.level]=(lv[c.level]||0)+1;});
155    var maxLv=Math.max(1,lv['重要']||0,lv['普通']||0,lv['潜在']||0);
156    function barRow(label,val,max,cls){return '<div class="bar"><div class="bl"><span>'+label+'</span><b>'+val+' 人</b></div><div class="track"><div class="fill '+(cls||'')+'" style="width:'+(val/max*100)+'%"></div></div></div>';}
157    document.getElementById('byLevel').innerHTML=barRow('重要客户',lv['重要']||0,maxLv)+barRow('普通客户',lv['普通']||0,maxLv)+barRow('潜在客户',lv['潜在']||0,maxLv);
158    var st={};custs.forEach(function(c){st[c.status]=(st[c.status]||0)+1;});
159    var okD=(st['已成交']||0)/tot*360,ingD=(st['跟进中']||0)/tot*360,lostD=360-okD-ingD;
160    document.getElementById('ring').style.background='conic-gradient(var(--ok) 0 '+okD+'deg, var(--warn) '+okD+'deg '+(okD+ingD)+'deg, var(--mute) '+(okD+ingD)+'deg 360deg)';
161    document.getElementById('donutLg').innerHTML='<div><i style="background:var(--ok)"></i>已成交 '+(st['已成交']||0)+' 人</div><div><i style="background:var(--warn)"></i>跟进中 '+(st['跟进中']||0)+' 人</div><div><i style="background:var(--mute)"></i>已流失 '+(st['已流失']||0)+' 人</div>';
162    var top=custs.filter(function(c){return +c.amount>0;}).sort(function(a,b){return b.amount-a.amount;}).slice(0,3);
163    var maxAmt=Math.max(1,top.length?top[0].amount:1);
164    document.getElementById('topAmt').innerHTML=top.map(function(c){return '<div class="bar"><div class="bl"><span>'+esc(c.name)+'</span><b>'+fmtMoney(c.amount)+'</b></div><div class="track"><div class="fill g" style="width:'+(c.amount/maxAmt*100)+'%"></div></div></div>';}).join('')||'<div class="empty">暂无成交客户</div>';
165    var fTot=fols.length,fMonth=fols.filter(function(f){return f.date&&f.date.indexOf('2026-07')>=0;}).length;
166    document.getElementById('fStat').innerHTML='<div class="stat"><div class="n">'+fTot+'</div><div class="l">跟进总数</div></div><div class="stat"><div class="n">'+fMonth+'</div><div class="l">本月跟进</div></div>';
167  }
168})();
169