首页/ mobileweb/ 简库 · 库存清单 H5/ 代码预览

💻 代码预览 · 简库 · 库存清单 H5

mw_admin · 上传者 驰旭教育
← 返回详情
js/main.js · 约 13791 字符
1
2var KEY='wms_'+window.SYS_KEY;
3var SEED=window.SEED||[];
4var MEM=null;
5function load(){try{var s=localStorage.getItem(KEY);if(s)return JSON.parse(s);}catch(e){}return MEM?MEM.slice():(SEED.slice());}
6function save(a){MEM=a;try{localStorage.setItem(KEY,JSON.stringify(a));}catch(e){}}
7function money(n){n=Number(n)||0;return '\u00A5'+n.toLocaleString('zh-CN',{maximumFractionDigits:2});}
8function esc(s){return String(s==null?'':s).replace(/[&<>"]/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c];});}
9function now(){var d=new Date();return d.getFullYear()+'-'+('0'+(d.getMonth()+1)).slice(-2)+'-'+('0'+d.getDate()).slice(-2);}
10function toast(m){var t=document.createElement('div');t.textContent=m;t.style.cssText='position:fixed;left:50%;top:18%;transform:translateX(-50%);background:rgba(0,0,0,.82);color:#fff;padding:10px 16px;border-radius:10px;font-size:13px;z-index:99';document.body.appendChild(t);setTimeout(function(){t.remove();},1400);}
11function q(s){return document.querySelector(s);}
12function qa(s){return document.querySelectorAll(s);}
13var items=load();
14function lowCount(){return items.filter(function(i){return (i.qty||0) < (i.min||0);}).length;}
15function totalQty(){return items.reduce(function(a,i){return a+(i.qty||0);},0);}
16function totalVal(){return items.reduce(function(a,i){return a+(i.qty||0)*(i.price||0);},0);}
17function row(k,val){return '<div class="row"><span class="sub">'+k+'</span><b>'+val+'</b></div>';}
18function field(id,label,type,ph){return '<div class="field"><label>'+label+'</label><input id="f-'+id+'" type="'+type+'" placeholder="'+ph+'"></div>';}
19
20function renderDash(){
21 var v=q('#view');
22 var low=lowCount(), tq=totalQty(), tv=totalVal(), sku=items.length;
23 var lowList=items.filter(function(i){return (i.qty||0)<(i.min||0);}).slice(0,4);
24 var recent=items.slice().sort(function(a,b){return (b.upd||'').localeCompare(a.upd||'');}).slice(0,4);
25 var h='<div class="kpi">'
26   +'<div class="k"><div class="n">'+sku+'</div><div class="t">商品种类</div></div>'
27   +'<div class="k"><div class="n">'+tq+'</div><div class="t">库存总量</div></div>'
28   +'<div class="k"><div class="n">'+money(tv)+'</div><div class="t">库存总值</div></div>'
29   +'<div class="k"><div class="n" style="color:#e11d48">'+low+'</div><div class="t">预警商品</div></div></div>';
30 h+='<div class="card"><div class="sec">\u26A0 库存预警</div>'+(lowList.length?lowList.map(function(i){return '<div class="row"><div><b>'+esc(i.name)+'</b><br><span class="sub">'+esc(i.sku)+'</span></div><span class="badge warn">'+i.qty+'/'+i.min+'</span></div>';}).join(''):'<div class="sub" style="padding:10px 0">暂无预警 \u2705</div>')+'</div>';
31 h+='<div class="card"><div class="sec">\uD83D\uDD5E 最近更新</div>'+(recent.length?recent.map(function(i){return '<div class="row"><div><b>'+esc(i.name)+'</b></div><span class="sub">'+esc(i.upd||'')+'</span></div>';}).join(''):'')+'</div>';
32 v.innerHTML=h;
33}
34
35function renderStock(){
36 var v=q('#view');
37 v.innerHTML='<input id="kw" class="search" placeholder="\uD83D\uDD0D 搜索名称 / 编码 / 供应商">'
38  +'<div id="chips" class="chips"></div><div id="list"></div>';
39 var cats=[];items.forEach(function(i){if(i.cat&&cats.indexOf(i.cat)<0)cats.push(i.cat);});
40 q('#chips').innerHTML='<span class="chip on" data-c="">全部</span>'+cats.map(function(c){return '<span class="chip" data-c="'+esc(c)+'">'+esc(c)+'</span>';}).join('');
41 var cur='';
42 function draw(){
43   var kw=q('#kw').value.trim().toLowerCase();
44   var fl=items.filter(function(i){
45     if(cur&&i.cat!==cur)return false;
46     if(!kw)return true;
47     return (i.name+' '+i.sku+' '+(i.sup||'')).toLowerCase().indexOf(kw)>=0;
48   });
49   q('#list').innerHTML=fl.length?fl.map(function(i){
50     var w=(i.qty||0)<(i.min||0);
51     return '<a class="card item" href="detail.html?id='+i.id+'">'
52       +'<div class="item-top"><b>'+esc(i.name)+'</b><span class="badge '+(w?'warn':'ok')+'">'+(w?'预警':'正常')+'</span></div>'
53       +'<div class="sub">'+esc(i.sku)+' \u00B7 '+esc(i.cat||'')+' \u00B7 \uD83D\uDCCD'+esc(i.loc||'')+'</div>'
54       +'<div class="item-bot"><span class="qty">'+i.qty+' '+esc(i.unit||'件')+'</span><span class="sub">'+money(i.price)+'</span></div></a>';
55   }).join(''):'<div class="sub" style="padding:20px 0;text-align:center">没有匹配商品</div>';
56 }
57 q('#kw').addEventListener('input',draw);
58 qa('#chips .chip').forEach(function(c){c.addEventListener('click',function(){qa('#chips .chip').forEach(function(x){x.classList.remove('on');});c.classList.add('on');cur=c.getAttribute('data-c');draw();});});
59 draw();
60}
61
62function renderInbound(){
63 var v=q('#view');
64 v.innerHTML='<div class="card"><div class="sec">\u2795 新增库存商品</div>'
65  +field('name','商品名称 *','text','如:A4 打印纸')
66  +field('sku','商品编码','text','如:SKU-0001')
67  +'<div class="grid2">'+field('cat','分类','text','如:办公')+field('unit','单位','text','件/箱/个')+'</div>'
68  +'<div class="grid2">'+field('qty','数量 *','number','0')+field('price','单价','number','0')+'</div>'
69  +field('loc','存放位置','text','如:A 区 3 排')
70  +field('sup','供应商','text','')
71  +'<div class="grid2">'+field('min','预警下限','number','0')+field('note','备注','text','')+'</div>'
72  +'<button class="btn" id="save">保存入库</button></div>';
73 q('#save').addEventListener('click',function(){
74   var name=q('#f-name').value.trim();
75   if(!name){toast('请填写商品名称');return;}
76   var it={id:'I'+Date.now(),name:name,sku:q('#f-sku').value.trim(),cat:q('#f-cat').value.trim(),unit:q('#f-unit').value.trim()||'件',qty:parseInt(q('#f-qty').value)||0,price:parseFloat(q('#f-price').value)||0,loc:q('#f-loc').value.trim(),sup:q('#f-sup').value.trim(),min:parseInt(q('#f-min').value)||0,note:q('#f-note').value.trim(),upd:now()};
77   items.unshift(it);save(items);toast('已入库:'+name);setTimeout(function(){location.href='stock.html';},700);
78 });
79}
80
81function renderDetail(){
82 var v=q('#view');
83 var id=new URLSearchParams(location.search).get('id');
84 var it=items.filter(function(x){return x.id===id;})[0]||items[0];
85 if(!it){v.innerHTML='<div class="sub" style="padding:30px;text-align:center">暂无商品</div>';return;}
86 var w=(it.qty||0)<(it.min||0);
87 v.innerHTML='<div class="card">'
88   +'<div class="item-top"><b style="font-size:18px">'+esc(it.name)+'</b><span class="badge '+(w?'warn':'ok')+'">'+(w?'预警':'正常')+'</span></div>'
89   +'<div class="sub" style="margin:4px 0 10px">'+esc(it.sku)+'</div>'
90   +row('分类',esc(it.cat))+row('数量',it.qty+' '+(it.unit||'件'))+row('单价',money(it.price))
91   +row('位置',esc(it.loc))+row('供应商',esc(it.sup))+row('预警下限',it.min)+row('备注',esc(it.note))
92   +'</div>'
93   +'<div class="card"><div class="sec">\u26A1 快捷操作</div>'
94   +'<div class="grid2"><button class="btn ghost" id="dec">\uFF0D 出库</button><button class="btn ghost" id="inc">\uFF0B 入库</button></div>'
95   +'<button class="btn ghost" id="edit" style="margin-top:8px">\u270F\uFE0F 编辑资料</button>'
96   +'<button class="btn danger" id="del" style="margin-top:8px">\uD83D\uDDD1 删除</button></div>';
97 q('#inc').onclick=function(){it.qty=(it.qty||0)+1;it.upd=now();save(items);toast('+1');renderDetail();};
98 q('#dec').onclick=function(){if(it.qty>0)it.qty-=1;it.upd=now();save(items);toast('-1');renderDetail();};
99 q('#edit').onclick=function(){
100   var n=prompt('商品名称',it.name);if(n===null)return;it.name=n;
101   var l=prompt('存放位置',it.loc||'');it.loc=l;var m=prompt('预警下限',it.min||0);it.min=parseInt(m)||0;
102   it.upd=now();save(items);toast('已更新');renderDetail();
103 };
104 q('#del').onclick=function(){if(confirm('确认删除「'+it.name+'」?')){items=items.filter(function(x){return x.id!==it.id;});save(items);toast('已删除');setTimeout(function(){location.href='stock.html';},600);}};
105}
106
107function renderAdjust(){
108 var v=q('#view');
109 v.innerHTML='<div class="card"><div class="sec">\u2696\uFE0F 盘点调整</div>'
110  +'<div class="field"><label>选择商品</label><select id="ai"></select></div>'
111  +'<div class="field"><label>实际盘点数量</label><input id="aq" type="number" placeholder="0"></div>'
112  +'<div id="diff" class="sub" style="margin-bottom:10px"></div>'
113  +'<button class="btn" id="as">保存盘点结果</button></div>'
114  +'<div class="card"><div class="sec">\uD83D\uDCCB 当前库存</div><div id="al"></div></div>';
115 var sel=q('#ai');sel.innerHTML=items.map(function(i){return '<option value="'+i.id+'">'+esc(i.name)+'(现有 '+i.qty+')</option>';}).join('');
116 function updDiff(){var i=items.filter(function(x){return x.id===sel.value;})[0];var a=parseInt(q('#aq').value)||0;if(i){var d=a-(i.qty||0);q('#diff').textContent='差异:'+(d>0?'+':'')+d+'('+i.qty+' \u2192 '+a+')';}}
117 sel.onchange=updDiff;q('#aq').oninput=updDiff;
118 function drawList(){q('#al').innerHTML=items.map(function(i){return row(esc(i.name),i.qty+' '+(i.unit||'件'));}).join('');}
119 q('#as').onclick=function(){var i=items.filter(function(x){return x.id===sel.value;})[0];if(!i)return;var a=parseInt(q('#aq').value)||0;i.qty=a;i.upd=now();i.note=(i.note?i.note+'\uFF1B':'')+'盘点'+now();save(items);toast('已调整为 '+a);q('#aq').value='';updDiff();drawList();};
120 drawList();updDiff();
121}
122
123function renderTransfer(){
124 var v=q('#view');
125 v.innerHTML='<div class="card"><div class="sec">\uD83D\uDD00 调拨 / 领用出库</div>'
126  +'<div class="field"><label>选择物资</label><select id="ti"></select></div>'
127  +'<div class="field"><label>领用 / 调出数量</label><input id="tq" type="number" placeholder="0"></div>'
128  +'<div class="field"><label>去向 / 用途</label><input id="td" placeholder="如:B 车间 / 维修"></div>'
129  +'<button class="btn" id="ts">确认出库</button></div>';
130 var sel=q('#ti');sel.innerHTML=items.map(function(i){return '<option value="'+i.id+'">'+esc(i.name)+'('+i.qty+' '+esc(i.unit||'件')+' @ '+esc(i.loc||'')+')</option>';}).join('');
131 q('#ts').onclick=function(){var i=items.filter(function(x){return x.id===sel.value;})[0];if(!i)return;var n=parseInt(q('#tq').value)||0;if(n<=0){toast('数量无效');return;}if(n>i.qty){toast('超出库存');return;}var to=q('#td').value.trim()||'领用';i.qty-=n;i.upd=now();i.note=(i.note?i.note+'\uFF1B':'')+'调出'+n+'\u2192'+to;save(items);toast('已出库 '+n);q('#tq').value='';};
132}
133
134function renderRestock(){
135 var v=q('#view');
136 var low=items.filter(function(i){return (i.qty||0)<(i.min||0);});
137 v.innerHTML='<div class="card"><div class="sec">\uD83D\uDD14 库存预警与补货</div>'
138  +(low.length?'<div id="rl"></div>':'<div class="sub" style="padding:14px 0;text-align:center">\uD83C\uDF89 所有商品库存充足</div>')+'</div>';
139 if(low.length){
140   q('#rl').innerHTML=low.map(function(i){return '<div class="card" style="margin-bottom:10px;padding:12px"><div class="item-top"><b>'+esc(i.name)+'</b><span class="badge warn">'+i.qty+'/'+i.min+'</span></div>'
141     +'<div class="sub" style="margin:4px 0 8px">建议补货至 '+i.min+' 以上</div>'
142     +'<div class="grid2"><input id="rq-'+i.id+'" type="number" placeholder="补货量" style="padding:10px;border-radius:12px;border:1px solid var(--line);background:rgba(255,255,255,.15);color:#fff;font-size:14px"><button class="btn" data-id="'+i.id+'">补货</button></div></div>';}).join('');
143   qa('#rl .btn').forEach(function(b){b.onclick=function(){var id=b.getAttribute('data-id');var i=items.filter(function(x){return x.id===id;})[0];var n=parseInt(q('#rq-'+id).value)||0;if(n<=0){toast('请输入数量');return;}i.qty+=n;i.upd=now();i.note=(i.note?i.note+'\uFF1B':'')+'补货'+n;save(items);toast('已补货 '+n);renderRestock();};});
144 }
145}
146
147function renderStats(){
148 var v=q('#view');
149 var byCat={};items.forEach(function(i){byCat[i.cat||'未分类']=(byCat[i.cat||'未分类']||0)+(i.qty||0);});
150 var arr=Object.keys(byCat).map(function(c){return [c,byCat[c]];});
151 var max=Math.max(1,arr.length?Math.max.apply(null,arr.map(function(x){return x[1];})):1);
152 var catBars=arr.map(function(x){return '<div class="row" style="display:block"><div style="display:flex;justify-content:space-between"><span>'+esc(x[0])+'</span><b>'+x[1]+'</b></div><div class="bar"><i style="width:'+(x[1]/max*100)+'%"></i></div></div>';}).join('');
153 var top=items.slice().sort(function(a,b){return (b.qty*b.price)-(a.qty*a.price);}).slice(0,3);
154 var topH=top.map(function(i,k){return '<div class="row"><span>'+(k+1)+'. '+esc(i.name)+'</span><b>'+money(i.qty*i.price)+'</b></div>';}).join('');
155 v.innerHTML='<div class="kpi">'
156   +'<div class="k"><div class="n">'+items.length+'</div><div class="t">商品种类</div></div>'
157   +'<div class="k"><div class="n">'+totalQty()+'</div><div class="t">库存总量</div></div>'
158   +'<div class="k"><div class="n">'+money(totalVal())+'</div><div class="t">库存总值</div></div>'
159   +'<div class="k"><div class="n" style="color:#e11d48">'+lowCount()+'</div><div class="t">预警商品</div></div></div>'
160   +'<div class="card"><div class="sec">\uD83D\uDCE6 分类库存分布</div>'+(catBars||'<div class="sub">暂无数据</div>')+'</div>'
161   +'<div class="card"><div class="sec">\uD83D\uDCBC 库存价值 TOP3</div>'+(topH||'<div class="sub">暂无数据</div>')+'</div>';
162}
163
164var page=q('#view').getAttribute('data-page');
165if(page==='dash')renderDash();
166else if(page==='stock')renderStock();
167else if(page==='inbound')renderInbound();
168else if(page==='detail')renderDetail();
169else if(page==='adjust')renderAdjust();
170else if(page==='transfer')renderTransfer();
171else if(page==='restock')renderRestock();
172else if(page==='stats')renderStats();
173