index.blade.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. @extends('layouts.app')
  2. @section('title')点收一体@endsection
  3. @section('content')
  4. <div class="d-none" id="container">
  5. <audio src="{{asset('sound/warning_otherBarcode.mp3')}}" controls="controls" preload id="soundWarning" hidden>
  6. </audio>
  7. <audio src="{{asset('sound/ding.mp3')}}" controls="controls" preload id="soundDing" hidden>
  8. </audio>
  9. <div class="row">
  10. <div class="col-4 p-0 m-0" id="countGoods">
  11. <div class="col-12 text-center bg-white mt-2">
  12. <h4 class="font-weight-bold text-dark">清点模式</h4>
  13. </div>
  14. <div class="card-body">
  15. <div class="row pl-5">
  16. <div class="col-6">
  17. <label class="text-left font-weight-bold text-muted">条码</label>
  18. <input type="text" id="barcode" class="form-control" :disabled="status.barcodeDisable" placeholder="扫入条码"
  19. @focusin="focusOutDocument" @focusout="focusDocument" v-model="inputting.barcode">
  20. </div>
  21. <div v-if="inputMode==='sweepModel'" class="col-6">
  22. <label class="text-left font-weight-bold text-muted">已扫数量</label>
  23. <input type="number" id="amount" class="form-control mt-0" placeholder="" :disabled="status.amountDisable"
  24. v-model="inputting.amount" style='height: 40px;font-size: 1.6em;color:blue;font-weight: bolder;padding: 3px;text-align: center'>
  25. </div>
  26. </div>
  27. <p class="card-text text-muted mt-3 mb-n3 text-center">已完成:</p>
  28. <hr>
  29. <table class="table table-sm table-striped table-bordered" v-if="goodses.length>0">
  30. <tr>
  31. <th class="text-center">条码</th>
  32. <th class="text-center">数量</th>
  33. <th class="text-center">箱规</th>
  34. <th class="text-center">操作</th>
  35. </tr>
  36. <tr v-for="(goods,i) in goodses" :id="'tr_'+i">
  37. <td class="text-center">@{{ goods.barcode }}</td>
  38. <td class="text-center">@{{ goods.amount }}</td>
  39. <td class="text-center">
  40. <span v-if="goods.cast_number">第@{{ goods.cast_number }}箱</span>
  41. </td>
  42. <td class="text-center">
  43. <span v-if="goods.countGoodStatus">
  44. <button class="btn btn-outline-info btn-sm" @click="countGoods(i,goods.barcode)">完成清点</button>
  45. </span>
  46. <button class="btn btn-outline-danger btn-sm" @click="removeGoods($event,goods.barcode)">删</button>
  47. </td>
  48. </tr>
  49. </table>
  50. <hr>
  51. <span class="btn btn-outline-dark btn form-control" style="cursor: pointer" @click="submitExcelData">结束清点</span>
  52. </div>
  53. </div>
  54. <div class="col-8 p-0 m-0" id="receive">
  55. <div class="col-12 text-center bg-white mt-2">
  56. <h4 class="font-weight-bold text-dark">收货模式</h4>
  57. </div>
  58. <div class="card-body m-0">
  59. <div class="col-8 offset-2 row">
  60. <label for="asnno" class="font-weight-bold text-muted ">输入ASN</label>
  61. <input type="text" id="asnno" class="form-control" @focusin="focusOutDocument" autocomplete="off"
  62. @change="getReceiveTaskByAsnNoAndBarcodes()" disabled="true" placeholder="ASN单号" v-model="asnno">
  63. </div>
  64. <table class="table table-sm table-striped" v-if="regroupGoods.length>0">
  65. <hr>
  66. <tr>
  67. <th class="text-center">货主</th>
  68. <th class="text-center">条码</th>
  69. <th class="text-center">清点数量</th>
  70. <th class="text-center">ASN数量</th>
  71. <th class="text-center">差值</th>
  72. <th class="text-center">容器号</th>
  73. <th class="text-center">生产日期</th> <!--lotatt01-->
  74. <th class="text-center">失效日期</th> <!--lotatt02-->
  75. <th class="text-center">批号</th> <!--lotatt04-->
  76. <th class="text-center">属性仓</th> <!--lotatt05-->
  77. <th class="text-center">质量状态</th> <!--lotatt08-->
  78. <th class="text-center">入库日期</th> <!--lotatt03-->
  79. <th class="text-center">操作</th>
  80. </tr>
  81. <tr v-for="(goods,i) in regroupGoods">
  82. <td class="text-center">@{{ goods.customerid }}</td>
  83. <td class="text-center">@{{ goods.sku }}</td>
  84. <td class="text-center">@{{ goods.amount }}</td>
  85. <td class="text-center">@{{ goods.asn_amount }}</td>
  86. <td class="text-center">@{{ goods.diff_val }}</td>
  87. <td class="text-center">
  88. <span v-if="goods.receiveStatus">
  89. <input type="text" autocomplete="off" class="form-control form-control-sm" :class="errors.trackNumber ? 'is-invalid' : ''"
  90. id="trackNumber" v-model="goods.trackNumber">
  91. <span class="invalid-feedback offset-3" role="alert" v-if="errors.trackNumber">
  92. <strong>@{{ errors.trackNumber[0] }}</strong>
  93. </span>
  94. </span>
  95. </td>
  96. <td class="text-center">
  97. <span v-if="goods.basSku.lot_id">
  98. <input type="date" autocomplete="off" class="form-control form-control-sm input-sm" :class="errors.lotatt01 ? 'is-invalid' : ''"
  99. :disabled="goods.basSku.lot_id.lotkey01==='N'" id="lotatt01" v-model="goods.lotatt01">
  100. <span class="invalid-feedback offset-3" role="alert" v-if="errors.lotatt01">
  101. <strong>@{{ errors.lotatt01[0] }}</strong>
  102. </span>
  103. </span>
  104. </td>
  105. <td class="text-center">
  106. <span v-if="goods.basSku.lot_id">
  107. <input type="date" autocomplete="off" class="form-control form-control-sm input-sm" :class="errors.lotatt02 ? 'is-invalid' : ''"
  108. :disabled="goods.basSku.lot_id.lotkey02==='N'" id="lotatt02" v-model="goods.lotatt02">
  109. <span class="invalid-feedback offset-3" role="alert" v-if="errors.lotatt02">
  110. <strong>@{{ errors.lotatt02[0] }}</strong>
  111. </span>
  112. </span>
  113. </td>
  114. <td class="text-center">
  115. <span v-if="goods.basSku.lot_id">
  116. <input type="text" autocomplete="off" class="form-control form-control-sm input-sm" :class="errors.lotatt04 ? 'is-invalid' : ''"
  117. :disabled="goods.basSku.lot_id.lotkey04==='N'" id="lotatt04" v-model="goods.lotatt04">
  118. <span class="invalid-feedback offset-3" role="alert" v-if="errors.lotatt04">
  119. <strong>@{{ errors.lotatt04[0] }}</strong>
  120. </span>
  121. </span>
  122. </td>
  123. <td class="text-center">
  124. <span v-if="goods.basSku.lot_id">
  125. <select class="form-control form-control-sm" :disabled="goods.basSku.lot_id.lotkey05==='N'"
  126. id="lotatt05" v-model="goods.lotatt05" :class="errors.lotatt05 ? 'is-invalid' : ''">
  127. <option v-for="(attributeLocation,i) in attributeLocations"
  128. :value="attributeLocation.code">@{{ attributeLocation.codename_c }}</option>
  129. </select>
  130. <span class="invalid-feedback offset-3" role="alert" v-if="errors.lotatt05">
  131. <strong>@{{ errors.lotatt05[0] }}</strong>
  132. </span>
  133. </span>
  134. </td>
  135. <td class="text-center">
  136. <span v-if="goods.basSku.lot_id">
  137. <select class="form-control form-control-sm" :class="errors.lotatt08 ? 'is-invalid' : ''"
  138. :disabled="goods.basSku.lot_id.lotkey08==='N'" id="lotatt08" v-model="goods.lotatt08">
  139. <option v-for="(quality,i) in qualityStatus" :value="quality.code">@{{ quality.codename_c }}</option>
  140. </select>
  141. <span class="invalid-feedback offset-3" role="alert" v-if="errors.lotatt08">
  142. <strong>@{{ errors.lotatt08[0] }}</strong>
  143. </span>
  144. </span>
  145. </td>
  146. <td class="text-center">
  147. <span v-if="goods.basSku.lot_id">
  148. <input type="date" autocomplete="off" class="form-control form-control-sm input-sm" :class="errors.lotatt03 ? 'is-invalid' : ''"
  149. :disabled="goods.basSku.lot_id.lotkey03==='N'" id="lotatt03" v-model="goods.lotatt03">
  150. <span class="invalid-feedback offset-3" role="alert" v-if="errors.lotatt03">
  151. <strong>@{{ errors.lotatt03[0] }}</strong>
  152. </span>
  153. </span>
  154. </td>
  155. <td class="text-center">
  156. <span v-if="goods.receiveStatus&&goods.diff_val>=0">
  157. <button class="btn btn-outline-info btn-sm" @click="receiveGoods(goods)">收</button>
  158. </span>
  159. </td>
  160. </tr>
  161. </table>
  162. <span class="btn btn-outline-dark btn form-control mt-5" v-if="status.finishReceiveButton"
  163. style="cursor: pointer" @click="finishReceive()">结束收货</span>
  164. </div>
  165. </div>
  166. </div>
  167. </div>
  168. @endsection
  169. @section('lastScript')
  170. <script>
  171. new Vue({
  172. el: '#app',
  173. data:{
  174. qualityStatus:{!! $qualityStatus !!},
  175. attributeLocations:{!! $attributeLocations !!},
  176. focusing:'document',
  177. lastScannedBarcode:'',
  178. inputMode:'sweepModel',
  179. inputting:{
  180. barcode:'',amount:'',fromIncreasing:true,cast_number:'',countGoodStatus:true
  181. },
  182. status:{
  183. scanEndInputted:false,barcodeDisable:true,amountDisable:true,finishReceiveButton:false,
  184. },
  185. asnno:'',
  186. basSku:{
  187. lot_id:'',
  188. },
  189. goodses:[],
  190. initGoods:[],
  191. regroupGoods:[],
  192. errors:{},
  193. },
  194. mounted() {
  195. if (navigator.userAgent.indexOf("Android")!==-1)this.isAndroid = true;
  196. this.pageInit();
  197. $("#container").removeClass("d-none");
  198. this.scanListening();
  199. },
  200. methods: {
  201. //页面初始化
  202. pageInit(){
  203. if (!this.isAndroid)return;
  204. let element = document.getElementById("navbarSupportedContent").parentElement;
  205. element.className = "row";
  206. element.children[0].className += " col-5";
  207. element.innerHTML = element.children[0].outerHTML;
  208. let e1 = document.getElementById("menu");
  209. let e2 = document.getElementById("demand-div");
  210. if (e1)e1.remove();
  211. if (e2)e2.remove();
  212. },
  213. focusDocument: function () {
  214. this.focusing = 'document';
  215. },
  216. focusOutDocument: function () {
  217. this.focusing = '';
  218. },
  219. scanListening: function () {
  220. let data = this;
  221. $(document).on('keypress', function (e) {
  222. if(data.focusing!=='document'){return}
  223. if (e.keyCode !== 13) {
  224. if(data.status.scanEndInputted){
  225. data.lastScannedBarcode=data.inputting.barcode;
  226. data.inputting.barcode='';
  227. data.status.scanEndInputted=false;
  228. }
  229. data.inputting.barcode += String.fromCharCode(e.keyCode);
  230. } else {
  231. if(data.inputting.barcode.length<=1){
  232. window.tempTip.setDuration(4500);
  233. window.tempTip.show('未扫入条码,请检查扫码枪设置,尝试调至“直接键盘输出”模式');
  234. return;
  235. }
  236. data.status.scanEndInputted = true;
  237. switch(data.inputMode){
  238. case 'sweepModel': data.commitGoodsOnSweepModel();break;
  239. }
  240. }
  241. });
  242. },
  243. commitGoodsOnSweepModel(){
  244. let repeatedBarcode=this.repeatedIncreasingBarcodeFromSaved();
  245. if(!repeatedBarcode){
  246. this.focusOutDocument();
  247. this.alertVibrate();
  248. this.inputting.amount=1;
  249. this.goodses.unshift(JSON.parse(JSON.stringify(this.inputting)));
  250. window.tempTip.setDuration(500);
  251. window.tempTip.showSuccess('保存成功');
  252. this.focusDocument();
  253. this.audioDing();
  254. }else{
  255. repeatedBarcode.amount++;
  256. this.inputting.amount=repeatedBarcode.amount;
  257. window.tempTip.setDuration(500);
  258. window.tempTip.showSuccess(repeatedBarcode.amount);
  259. this.focusDocument();
  260. this.audioDing();
  261. }
  262. },
  263. audioDing: function () {
  264. let audio = document.getElementById('soundDing');
  265. audio.currentTime = 0;//重新播放
  266. audio.play();// 播放
  267. function startVibrate(duration) {
  268. if (navigator.vibrate) {
  269. navigator.vibrate(duration);
  270. } else if (navigator.webkitVibrate) {
  271. navigator.webkitVibrate(duration);
  272. }
  273. }
  274. startVibrate(500);
  275. },
  276. alertVibrate: function () {
  277. function startVibrate(duration) {
  278. if (navigator.vibrate) {
  279. navigator.vibrate(duration);
  280. } else if (navigator.webkitVibrate) {
  281. navigator.webkitVibrate(duration);
  282. }
  283. }
  284. let vibrateInterval = setInterval(function() {
  285. startVibrate(150);
  286. }, 50);
  287. setTimeout(function() {
  288. clearInterval(vibrateInterval)
  289. }, 2000);
  290. },
  291. repeatedIncreasingBarcodeFromSaved: function () {
  292. let data = this;
  293. let repeatedGoods=null;
  294. data.goodses.every(function(goods){
  295. if(goods.barcode===data.inputting.barcode && goods.fromIncreasing && goods.countGoodStatus){
  296. repeatedGoods=goods;
  297. return false;
  298. }
  299. return true;
  300. });
  301. return repeatedGoods;
  302. },
  303. cleanInputs: function () {
  304. this.inputting.barcode='';
  305. this.inputting.amount='';
  306. this.inputting.cast_number='';
  307. this.lastScannedBarcode='';
  308. },
  309. countGoods: function (i,barcode) {
  310. if(!confirm('确定要结束清点'+barcode+'的记录吗'))return;
  311. let data = this;
  312. let cast_number=0;
  313. data.goodses.forEach(function (goods) {
  314. if (goods.barcode===barcode)cast_number++
  315. });
  316. data.goodses.every(function(goods,i){
  317. if(goods.barcode===barcode &&goods.countGoodStatus){
  318. goods.countGoodStatus=false;
  319. goods.cast_number=cast_number;
  320. return false;
  321. }
  322. return true;
  323. })
  324. $('#tr_'+i).attr("class",'alert alert-dark');
  325. },
  326. removeGoods: function ($e,barcode) {
  327. if(!confirm('确定要删除条码为'+barcode+'的记录吗'))return;
  328. let data = this;
  329. data.goodses.every(function(goods,i){
  330. if(goods.barcode===barcode){
  331. data.goodses.splice(i,1)
  332. return false;
  333. }
  334. return true;
  335. })
  336. },
  337. regroupArr(arr){
  338. let a =JSON.parse(JSON.stringify(arr));
  339. return a.reduce((total, cur, index) => {
  340. let hasValue = total.findIndex(current => {
  341. return current.barcode === cur.barcode
  342. })
  343. hasValue === -1 && total.push(cur)
  344. hasValue !== -1 && (total[hasValue].amount = total[hasValue].amount + cur.amount)
  345. return total
  346. }, []);
  347. },
  348. assignRegroupGoods(){
  349. let data=this;
  350. data.initGoods=data.regroupArr(data.goodses);
  351. data.initGoods.forEach(function (obj){
  352. delete obj.fromIncreasing;
  353. delete obj.cast_number;
  354. delete obj.countGoodStatus;
  355. obj.asnno='';obj.customerid='';
  356. obj.location='';obj.asn_amount='';
  357. obj.diff_val='';obj.receiveStatus=false;
  358. obj.basSku=data.basSku;obj.asnlineno='';
  359. obj.lotatt01='';obj.lotatt02='';
  360. obj.lotatt03='';obj.lotatt04='';
  361. obj.lotatt05='';obj.lotatt08='';
  362. })
  363. data.initGoods=JSON.parse(JSON.stringify(data.initGoods).replace(/barcode/g,"sku"));
  364. data.regroupGoods=data.initGoods;
  365. },
  366. receiveGoods(goods){
  367. let data=this;
  368. this.errors={};
  369. this.checkGood(goods);
  370. if (JSON.stringify(this.errors)!=='{}')return;
  371. goods.asnno=data.asnno;
  372. tempTip.setDuration(99999);
  373. tempTip.waitingTip('提交中');
  374. let url='{{url("store/countGoodsAndReceive/fluxReceive")}}';
  375. axios.post(url,{good:goods})
  376. .then(function(res){
  377. if(res.data.success){
  378. data.regroupGoods.forEach(function (good,i){
  379. if (goods.sku==good.sku){
  380. data.regroupGoods.splice(i,1)
  381. }
  382. })
  383. tempTip.setDuration(2000);
  384. tempTip.cancelWaitingTip();
  385. tempTip.showSuccess(res.data.data);
  386. }else{
  387. tempTip.setDuration(3000);
  388. tempTip.cancelWaitingTip();
  389. tempTip.show(res.data.data);
  390. data.alertVibrate()
  391. }
  392. })
  393. .catch(function (err) {
  394. tempTip.setDuration(2000);
  395. tempTip.cancelWaitingTip();
  396. tempTip.show("网络错误:"+err);
  397. data.alertVibrate()
  398. });
  399. },
  400. checkGood(good){
  401. let error = {};
  402. if (!good.trackNumber)error.trackNumber = ["容器号必填"];
  403. if (good.basSku.lot_id && good.basSku.lot_id.lotkey01==='Y' && !good.lotatt01) error.lotatt01=["生产日期为选"];
  404. if (good.basSku.lot_id && good.basSku.lot_id.lotkey02==='Y' && !good.lotatt02) error.lotatt02=["失效日期为选"];
  405. if (good.basSku.lot_id && good.basSku.lot_id.lotkey03==='Y' && !good.lotatt03) error.lotatt03=["入库日期为选"];
  406. if (good.basSku.lot_id && good.basSku.lot_id.lotkey04==='Y' && !good.lotatt04) error.lotatt04=["批号未填"];
  407. if (good.basSku.lot_id && good.basSku.lot_id.lotkey05==='Y' && !good.lotatt05) error.lotatt05=["属性仓未选"];
  408. if (good.basSku.lot_id && good.basSku.lot_id.lotkey08==='Y' && !good.lotatt08) error.lotatt08=["质量状态未选"];
  409. if (JSON.stringify(error)!=='{}'){this.errors = error;}
  410. },
  411. getReceiveTaskByAsnNoAndBarcodes(){
  412. let data=this;
  413. data.regroupGoods=data.initGoods;//验证asn下商品时 初始商品数据
  414. if(data.asnno===''||data.asnno===null||data.asnno===undefined) window.tempTip.show('请先输入ASN单号!');
  415. if (data.asnno && data.asnno.indexOf('ASN')===-1)window.tempTip.show('无效ASN号!');
  416. let url='{{url("store/countGoodsAndReceive/getReceiveTaskByAsnNoAndBarcodes")}}';
  417. window.axios.post(url,{asnno:data.asnno,goods:data.initGoods})
  418. .then(res=>{
  419. if (res.data.success){
  420. data.regroupGoods=res.data.data;
  421. this.$forceUpdate()
  422. }else {
  423. tempTip.setDuration(3000);
  424. tempTip.show(res.data.data);
  425. }
  426. }).catch(err=>{
  427. window.tempTip.setDuration(2000);
  428. window.tempTip.show("网络错误:"+err);
  429. })
  430. },
  431. removeDisable(){
  432. let asnNo=$('#asnno');
  433. asnNo.removeAttr('disabled');
  434. this.status.finishReceiveButton=true;
  435. asnNo.focus();
  436. },
  437. submitExcelData: function () {
  438. let data = this;
  439. data.focusOutDocument();
  440. if(data.goodses.length===0){
  441. window.tempTip.show('请先录入数据再提交收货');return;
  442. }
  443. window.tempTip.confirm('请检查表格,确定全部完成。',
  444. function () {
  445. window.tempTip.setInputType('input');
  446. window.tempTip.setIndex(999999);
  447. window.tempTip.inputVal('收货前将上传excel文件,请输入文件名:',function(val){
  448. if(val===''){
  449. window.tempTip.setDuration(2500);
  450. window.tempTip.show('失败!文件名不能为空,请重新点击生成');
  451. return;
  452. }
  453. window.tempTip.setDuration(15500);
  454. window.tempTip.waitingTip('提交Excel中...');
  455. let expireHandler = setTimeout(function () {
  456. window.tempTip.cancelWaitingTip();
  457. window.tempTip.setDuration(1500);
  458. window.tempTip.show('响应超时! 请检查网络,确保可以连接后再重试。');
  459. },9000);
  460. let url='{{url("store/countGoodsAndReceive/createExcel")}}';
  461. axios.post(url,{'goodses':data.goodses,'filename':val})
  462. .then(function(response){
  463. if(response.data.result==='success'){
  464. data.removeDisable();//收货解锁asnno input标签
  465. data.assignRegroupGoods();//结束清点,重组收货所需数据
  466. data.goodses=[];
  467. data.cleanInputs();
  468. window.tempTip.cancelWaitingTip();
  469. window.tempTip.setDuration(1500);
  470. window.tempTip.showSuccess('成功生成EXCEL,可在列表页查看');
  471. }else{
  472. window.tempTip.setDuration(1500);
  473. window.tempTip.show('生成EXCEL失败');
  474. data.focusDocument();
  475. data.alertVibrate()
  476. }
  477. clearInterval(expireHandler);
  478. })
  479. .catch(function (err) {
  480. window.tempTip.setDuration(3500);
  481. window.tempTip.show('网络或系统错误,请将以下信息提交给开发者:'+err);
  482. clearInterval(expireHandler);
  483. data.alertVibrate();
  484. });
  485. });
  486. },function () {
  487. data.focusDocument();
  488. })
  489. },
  490. finishReceive(){
  491. if(!confirm('确定要结束收货吗?'))return;
  492. setTimeout(function (){window.location.reload()},10)
  493. }
  494. },
  495. });
  496. </script>
  497. @endsection