blindReceive.blade.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. @extends('layouts.app')
  2. @section('content')
  3. <div class="container">
  4. <div class="mt-3">
  5. <span>
  6. <button class="btn btn-sm btn-outline-dark" @click="正常盘点(inventory.id)" >正常盘点</button>
  7. </span>
  8. <span class="form-group mb-5">
  9. <label class="text-muted">货主:</label><span class="font-weight-bold">@{{ inventory.owner.name }}</span>
  10. </span>
  11. <span class="form-group p-2 mb-5">
  12. <label >盘点单号:</label><span>@{{ inventory.id }}</span>
  13. </span>
  14. <span class="form-group p-2 mb-5">
  15. <label class="text-muted">时间范围:</label><span>@{{ inventory.start_at }} 至 @{{ inventory.end_at }}</span>
  16. </span>
  17. </div>
  18. <div class="mt-3">
  19. <span class="h5">
  20. <span class="form-group mb-5" v-if="inventory.status==='盘点中' || inventory.status==='待盘点'">
  21. <label class=" font-weight-bold">已盘点:</label><span>@{{ inventory.processed }}/总数:@{{ inventory.total }}</span>
  22. </span>
  23. <span class="form-group mb-5" v-if="inventory.status==='盘点中' || inventory.status==='待盘点'">
  24. <label class=" font-weight-bold">剩余数:</label><span>@{{ inventory.surplus }}</span>
  25. </span>
  26. <span class="form-group mb-5" v-if="inventory.status==='复盘中'&&未复盘有差异列">
  27. <label class=" font-weight-bold">有差异未复盘数:</label><span>@{{ 未复盘有差异列.length }}</span>
  28. </span>
  29. <span class="form-group mb-5" v-if="inventory.status==='复盘中'&&未盘列">
  30. <label class=" font-weight-bold">未盘点数:</label><span>@{{ 未盘列.length }}</span>
  31. </span>
  32. <span class="form-group mb-5" v-if="inventory.status==='复盘中'">
  33. <label class=" font-weight-bold">总数:</label><span>@{{ inventory.total }}</span>
  34. </span>
  35. </span>
  36. </div>
  37. {{-- <div class="mt-3" >--}}
  38. {{-- <span class="btn col-md-2 font-weight-bold" style="cursor: default;max-width: 160px" :class="inventory.status=='盘点中' ||inventory.status=='待盘点'?'bg-info':'btn-outline-info disabled'">--}}
  39. {{-- @{{ inventory.type }}--}}
  40. {{-- </span>--}}
  41. {{-- <span >--}}
  42. {{-- @can('库存管理-盘点-结束初盘')--}}
  43. {{-- <span v-if="inventory.status==='盘点中' ||inventory.status=='待盘点'" class="btn col-md-2 font-weight-bold btn-outline-secondary" style="max-width: 160px" @click="stockInventoryEnd(inventory.id)">结束</span>--}}
  44. {{-- @endcan--}}
  45. {{-- <span v-if="inventory.status==='复盘中'" class="btn col-md-2 font-weight-bold" style="cursor: default;max-width: 160px" :class="inventory.status=='盘点中' ||inventory.status=='待盘点'?'btn-outline-info disabled':'bg-info'">复盘</span>--}}
  46. {{-- </span>--}}
  47. {{-- </div>--}}
  48. <audio src="{{asset('sound/warning_otherBarcode.mp3')}}" controls="controls" preload id="soundWarning" hidden>
  49. </audio>
  50. <audio src="{{asset('sound/ding.mp3')}}" controls="controls" preload id="soundDing" hidden>
  51. </audio>
  52. <div class="row mt-2">
  53. <div class="col-12" id="scanColumn">
  54. <div class="card" :class="borderByMode">
  55. <div class="card-body">
  56. <div class="row">
  57. <div class="col-12">
  58. <p class="text-muted" style="font-size: 0.5em" v-if="inputMode=='regular'">常规:可输入效期,相同条码记录不会合并</p>
  59. <p class="text-muted" style="font-size: 0.5em" v-if="inputMode=='increasing'">逐一扫描:处理单一重复商品,每扫一次对应隔口总数量自动递增,扫到不同条码会提示</p>
  60. <p class="text-muted" style="font-size: 0.5em" v-if="inputMode=='multiIncreasing'">边扫边分:处理多种商品,自动将扫到的不同条码数量递增到各自隔口号</p>
  61. <ul class="nav nav-tabs mb-4 mt-n3">
  62. <li class="nav-item"><a href="#" class="nav-link" :class="inputMode=='regular'?'active':''"
  63. @click="inputMode='regular';changeToManualInputAmount();cleanInputs();inputting.fromIncreasing=false">常规</a></li>
  64. <li class="nav-item"><a href="#" class="nav-link" :class="inputMode=='increasing'?'active':''"
  65. @click="inputMode='increasing';changeToScanInputAmount();cleanInputs();inputting.fromIncreasing=true;">逐一扫描</a></li>
  66. <li class="nav-item"><a href="#" class="nav-link" :class="inputMode=='multiIncreasing'?'active':''"
  67. @click="inputMode='multiIncreasing';changeToScanInputAmount();cleanInputs();inputting.fromIncreasing=true">边扫边分</a></li>
  68. </ul>
  69. </div>
  70. <div class="col-6">
  71. <div v-if="inputMode=='regular'">
  72. <div class="btn btn-sm btn-outline-primary"
  73. v-if="!isManualInputtingBarcode" @click="changeToManualInputBarcode">手动输入</div>
  74. <div class="btn btn-sm btn-outline-danger"
  75. @click="changeToScanInputBarcode" v-if="isManualInputtingBarcode">扫描输入</div>
  76. </div>
  77. <input type="text" id="barcode" class="form-control" :disabled="status.barcodeDisable" placeholder="扫入条码" @focusin="focusOutDocument" @focusout="focusDocument" v-model="inputting.barcode">
  78. <div >
  79. <div >
  80. 库位:
  81. </div>
  82. <input type="text" class="form-control mb-2" v-model="location" @focusin="focusOutDocument" @focusout="focusDocument" @keyup="oninputEnter">
  83. </div>
  84. <div v-if="inputMode=='regular'">
  85. <div class="card-title">
  86. 生产日期:
  87. </div>
  88. <input type="date" class="form-control mb-2" v-model="inputting.produce_date" @focusin="focusOutDocument" @focusout="focusDocument" @keyup="oninputEnter">
  89. </div>
  90. <div v-if="inputMode=='regular'">
  91. <div class="card-title">
  92. 失效日期:
  93. </div>
  94. <input type="date" class="form-control mb-2" v-model="inputting.valid_date" @focusin="focusOutDocument" @focusout="focusDocument" @keyup="oninputEnter">
  95. </div>
  96. </div>
  97. <div class="col-6">
  98. <div class="card-title" id="amountLabel">
  99. 手动输入数量:
  100. </div>
  101. <div class="input-group mt-n2 mb-2">
  102. <input type="number" id="amount" class="form-control" placeholder="" :disabled="status.amountDisable"
  103. v-model="inputting.amount" @focusin="focusOutDocument" @focusout="focusDocument" @keyup="oninputEnter"
  104. style='height: 40px;font-size: 1.6em;color:blue;font-weight: bolder;padding: 3px;text-align: center'>
  105. </div>
  106. <div class="card-title">
  107. 格口号:
  108. </div>
  109. <input type="number" id="bin" class="form-control mt-n2 mb-2"
  110. v-model="inputting.bin"
  111. :disabled="status.binDisable" @focusin="focusOutDocument" @focusout="focusDocument" @keyup="oninputEnter"
  112. style='height: 80px;font-size: 5em;color:red;font-weight: bolder;padding: 3px;text-align: center'>
  113. <div v-if="inputMode=='regular'">
  114. <div class="card-title">
  115. 批次号:
  116. </div>
  117. <input type="text" class="form-control mb-2" v-model="inputting.batch_number" @focusin="focusOutDocument" @focusout="focusDocument" @keyup="oninputEnter">
  118. </div>
  119. </div>
  120. <div class="col-12" v-if="commitButtonVisible && inputMode=='regular'" >
  121. <button class="btn btn-success btn form-control" @click="commitGoods">确定</button>
  122. </div>
  123. </div>
  124. <p class="card-text text-muted mt-3 mb-n3 text-center">
  125. 已完成:
  126. </p>
  127. <hr>
  128. <table class="table table-sm table-striped" v-if="goodses.length>0">
  129. <tr>
  130. <th>隔口号</th>
  131. <th>数量</th>
  132. <th>条码</th>
  133. <th>生产日期</th>
  134. <th>失效日期</th>
  135. <th>批次号</th>
  136. <th>操作</th>
  137. </tr>
  138. <tr v-for="goods in goodses">
  139. <td>@{{ goods.bin }}</td>
  140. <td>@{{ goods.amount }}</td>
  141. <td>@{{ goods.barcode }}</td>
  142. <td>@{{ goods.produce_date }}</td>
  143. <td>@{{ goods.valid_date }}</td>
  144. <td>@{{ goods.batch_number }}</td>
  145. <td>
  146. <button class="btn btn-outline-danger btn-sm" @click="removeGoods($event,goods.barcode)">删</button>
  147. </td>
  148. </tr>
  149. </table>
  150. <hr>
  151. <span class="btn btn-outline-dark btn form-control" style="cursor: pointer" @click="submitStockInventory">结束并生成盘点</span>
  152. </div>
  153. </div>
  154. </div>
  155. </div>
  156. </div>
  157. @endsection
  158. @section('lastScript')
  159. <script>
  160. new Vue({
  161. el: '#app',
  162. data:{
  163. inventory:{!! $inventoryAccount!!},
  164. focusing:'document',
  165. commitButtonVisible:false,
  166. isManualInputtingBarcode:false,
  167. isManualInputtingAmount:true,
  168. lastScannedBarcode:'',
  169. inputMode:'regular',//regular,increasing,multiIncreasing
  170. location:'',
  171. inputting:{
  172. barcode:'',amount:'',bin:'',produce_date:'',valid_date:'',batch_number:'',fromIncreasing:false
  173. },
  174. status:{
  175. scanEndInputted:false,binDisable:false,barcodeDisable:true,amountDisable:false,
  176. },
  177. goodses:[],//{barcode,amount,bin,produce_date,valid_date,batch_number}
  178. },
  179. mounted() {
  180. this.scanListening();
  181. },
  182. methods: {
  183. 正常盘点(id){
  184. window.location.href='{{url('inventory/stockInventory/enterStockInventory')}}/'+id;
  185. },
  186. changeToManualInputAmount:function(){
  187. this.status.amountDisable=false;
  188. this.isManualInputtingAmount=true;
  189. $('#amountLabel').text('输入数量:');
  190. },
  191. changeToScanInputAmount:function(){
  192. this.status.amountDisable=true;
  193. this.isManualInputtingAmount=false;
  194. $('#amountLabel').text('自动扫入数量:');
  195. },
  196. changeToManualInputBarcode:function(){
  197. this.status.barcodeDisable=false;
  198. this.isManualInputtingBarcode=true;
  199. $('#barcode').attr('placeholder','手工输入条码');
  200. },
  201. changeToScanInputBarcode:function(){
  202. let data = this;
  203. this.isManualInputtingBarcode=false;
  204. this.inputting.barcode='';
  205. let $barcode = $('#barcode');
  206. this.status.barcodeDisable=false;
  207. setTimeout(function () {
  208. $barcode.focus();
  209. data.status.barcodeDisable=true;
  210. },20);
  211. $barcode.attr('placeholder','扫入条码');
  212. },
  213. oninputEnter:function(e){
  214. if (e.key === 'Enter') {
  215. this.focusDocument();
  216. }
  217. },
  218. focusDocument: function () {
  219. this.focusing = 'document';
  220. this.showCommitButton();
  221. },
  222. focusOutDocument: function () {
  223. this.focusing = '';
  224. this.autoFillBin();
  225. },
  226. scanListening: function () {
  227. let data = this;
  228. $(document).on('keypress', function (e) {
  229. if(data.focusing!=='document'){return}
  230. if(data.isManualInputtingBarcode){return}
  231. if (e.keyCode !== 13) {
  232. if(data.status.scanEndInputted){
  233. data.lastScannedBarcode=data.inputting.barcode;
  234. data.inputting.barcode='';
  235. data.status.scanEndInputted=false;
  236. }
  237. data.inputting.barcode += String.fromCharCode(e.keyCode);
  238. } else {
  239. if(data.inputting.barcode.length<=1){
  240. window.tempTip.setDuration(4500);
  241. window.tempTip.show('未扫入条码,请检查扫码枪设置,尝试调至“直接键盘输出”模式');
  242. return;
  243. }
  244. data.status.scanEndInputted = true;
  245. data.showCommitButton();
  246. data.autoFillBin();
  247. switch(data.inputMode){
  248. case 'increasing': data.commitGoodsOnIncreasingMode();break;
  249. case 'multiIncreasing': data.commitGoodsOnMultiIncreasingMode();break;
  250. }
  251. }
  252. });
  253. },
  254. commitGoodsOnIncreasingMode: function () {
  255. let data = this;
  256. function doIt(){
  257. let repeatedBarcode=data.repeatedIncreasingBarcodeFromSaved();
  258. function increase(){
  259. data.inputting.bin=repeatedBarcode.bin;
  260. repeatedBarcode.amount++;
  261. data.inputting.amount=repeatedBarcode.amount;
  262. window.tempTip.setDuration(500);
  263. window.tempTip.showSuccess(repeatedBarcode.amount);
  264. data.focusDocument();
  265. data.audioDing();
  266. }
  267. if(!repeatedBarcode){
  268. data.focusOutDocument();
  269. data.alertVibrate();
  270. window.tempTip.setInputType('number');
  271. window.tempTip.inputVal('该商品第一件递增请输入隔口号:',function(bin){
  272. if(bin===''){
  273. window.tempTip.setDuration(1500);
  274. window.tempTip.show('未输入隔口号,请重新扫描');
  275. data.alertVibrate();
  276. data.focusDocument();return}
  277. data.inputting.bin=bin;
  278. data.inputting.amount=1;
  279. data.goodses.unshift(JSON.parse(JSON.stringify(data.inputting)));
  280. data.status.binDisable=true;
  281. window.tempTip.setDuration(500);
  282. window.tempTip.showSuccess('保存成功');
  283. data.focusDocument();
  284. data.audioDing();
  285. })
  286. }else{
  287. increase();
  288. }
  289. }
  290. if(data.lastScannedBarcode!==data.inputting.barcode && data.lastScannedBarcode){
  291. data.audioWarning_otherBarcode();
  292. data.focusOutDocument();
  293. window.tempTip.confirm('扫到其它条码,是否切换至新条码并记录?',doIt,function () {
  294. data.inputting.barcode=data.lastScannedBarcode;
  295. data.focusDocument();
  296. })
  297. }else{
  298. doIt()
  299. }
  300. },
  301. commitGoodsOnMultiIncreasingMode: function () {
  302. let data = this;
  303. let repeatedBarcode=this.repeatedIncreasingBarcodeFromSaved();
  304. function increase(){
  305. data.inputting.bin=repeatedBarcode.bin;
  306. repeatedBarcode.amount++;
  307. data.inputting.amount=repeatedBarcode.amount;
  308. window.tempTip.setDuration(500);
  309. window.tempTip.showSuccess(repeatedBarcode.amount);
  310. data.focusDocument();
  311. data.audioDing();
  312. }
  313. if(!repeatedBarcode){
  314. data.focusOutDocument();
  315. data.alertVibrate();
  316. window.tempTip.setInputType('number');
  317. window.tempTip.inputVal('该商品第一件递增请输入隔口号:',function(bin){
  318. if(bin===''){
  319. window.tempTip.setDuration(1500);
  320. window.tempTip.show('未输入隔口号,请重新扫描');
  321. data.alertVibrate();
  322. data.focusDocument();return}
  323. data.inputting.bin=bin;
  324. data.inputting.amount=1;
  325. data.goodses.unshift(JSON.parse(JSON.stringify(data.inputting)));
  326. data.status.binDisable=true;
  327. window.tempTip.setDuration(500);
  328. window.tempTip.showSuccess('保存成功');
  329. data.focusDocument();
  330. data.audioDing();
  331. })
  332. }else{
  333. increase();
  334. }
  335. },
  336. audioWarning_otherBarcode: function () {
  337. let audio = document.getElementById('soundWarning');
  338. audio.currentTime = 0;//重新播放
  339. if(audio.paused){
  340. audio.play();// 播放
  341. }
  342. this.alertVibrate();
  343. },
  344. audioDing: function () {
  345. let audio = document.getElementById('soundDing');
  346. audio.currentTime = 0;//重新播放
  347. audio.play();// 播放
  348. function startVibrate(duration) {
  349. if (navigator.vibrate) {
  350. navigator.vibrate(duration);
  351. } else if (navigator.webkitVibrate) {
  352. navigator.webkitVibrate(duration);
  353. }
  354. }
  355. startVibrate(500);
  356. },
  357. alertVibrate: function () {
  358. function startVibrate(duration) {
  359. if (navigator.vibrate) {
  360. navigator.vibrate(duration);
  361. } else if (navigator.webkitVibrate) {
  362. navigator.webkitVibrate(duration);
  363. }
  364. }
  365. let vibrateInterval = setInterval(function() {
  366. startVibrate(150);
  367. }, 50);
  368. setTimeout(function() {
  369. clearInterval(vibrateInterval)
  370. }, 2000);
  371. },
  372. autoFillBin: function () {
  373. let data = this;
  374. let isNotRepeatingBarcode=this.goodses.every(function(goods){
  375. if(goods.barcode===data.inputting.barcode){
  376. data.inputting.bin=goods.bin;
  377. data.status.binDisable=true;
  378. return false;
  379. }
  380. return true;
  381. });
  382. if(isNotRepeatingBarcode){
  383. data.status.binDisable=false;
  384. }
  385. },
  386. repeatedIncreasingBarcodeFromSaved: function () {
  387. let data = this;
  388. let repeatedGoods=null;
  389. data.goodses.every(function(goods){
  390. if(goods.barcode===data.inputting.barcode && goods.fromIncreasing){
  391. repeatedGoods=goods;
  392. return false;
  393. }
  394. return true;
  395. });
  396. return repeatedGoods;
  397. },
  398. commitGoods: function () {
  399. let data = this;
  400. window.tempTip.setDuration(3500);
  401. if(!data.inputting.barcode){window.tempTip.show('请扫入条码');return;}
  402. else if(!data.inputting.amount){window.tempTip.show('请输入数量');return;}
  403. else if(!data.inputting.bin){window.tempTip.show('请输入隔口号');return;}
  404. data.recordOrPlusGoods();
  405. window.tempTip.setDuration(1500);
  406. window.tempTip.showSuccess('成功提交:' + data.inputting.barcode);
  407. data.cleanInputs();
  408. data.audioDing();
  409. },
  410. cleanInputs: function () {
  411. this.changeToScanInputBarcode();
  412. this.inputting.barcode='';
  413. this.inputting.amount='';
  414. this.inputting.bin='';
  415. this.inputting.produce_date='';
  416. this.inputting.valid_date='';
  417. this.inputting.batch_number='';
  418. this.commitButtonVisible=false;
  419. this.status.binDisable=false;
  420. this.lastScannedBarcode='';
  421. },
  422. recordOrPlusGoods: function () {
  423. let data = this;
  424. if(this.inputMode==='regular'){
  425. data.goodses.unshift(JSON.parse(JSON.stringify(data.inputting)));
  426. return;
  427. }
  428. let isNotRepeating=data.goodses.every(function(goods){
  429. if(goods.barcode===data.inputting.barcode){
  430. goods.amount=parseInt(goods.amount)+parseInt(data.inputting.amount);
  431. return false;
  432. }
  433. return true;
  434. });
  435. if(isNotRepeating){
  436. data.goodses.unshift(JSON.parse(JSON.stringify(data.inputting)));
  437. }
  438. },
  439. removeGoods: function ($e,barcode) {
  440. if(!confirm('确定要删除条码为'+barcode+'的记录吗'))return;
  441. let data = this;
  442. data.goodses.every(function(goods,i){
  443. if(goods.barcode===barcode){
  444. data.goodses.splice(i,1)
  445. return false;
  446. }
  447. return true;
  448. })
  449. },
  450. showCommitButton: function () {
  451. let data = this;
  452. if(data.inputting.barcode && data.inputting.amount && data.inputting.bin){
  453. data.commitButtonVisible=true;
  454. }
  455. },
  456. submitStockInventory: function () {
  457. let data = this;
  458. data.focusOutDocument();
  459. if(data.goodses.length===0){
  460. window.tempTip.show('请先录入数据再提交盘点');return;
  461. }
  462. if(!data.location){
  463. window.tempTip.setDuration(1500);
  464. window.tempTip.show('库位为必填项');
  465. return;
  466. }
  467. window.tempTip.confirm('请检查表格,确定全部完成。提交后数据将全部清空,不能后退',
  468. function () {
  469. let url='{{url("inventory/stockInventory/baseOnBlindReceive")}}';
  470. axios.post(url,{'location':data.location,'goodses':data.goodses,'inventoryId':data.inventory.id,'owner_code':data.inventory.owner.code})
  471. .then(function(response){
  472. if(response.data.success){
  473. data.goodses=[];
  474. data.cleanInputs();
  475. window.tempTip.cancelWaitingTip();
  476. window.tempTip.setDuration(1500);
  477. window.tempTip.showSuccess('盲收盘点成功,可返回盘点页面查看');
  478. data.location='';
  479. data.focusDocument();
  480. }else{
  481. window.tempTip.setDuration(1500);
  482. window.tempTip.show('盲收盘点失败:'+response.data.fail_info);
  483. console.log(response);
  484. data.focusDocument();
  485. data.alertVibrate()
  486. }
  487. })
  488. .catch(function (err) {
  489. window.tempTip.setDuration(3500);
  490. window.tempTip.show('网络或系统错误,请将以下信息提交给开发者:'+err);
  491. data.alertVibrate();
  492. });
  493. },function () {
  494. data.focusDocument();
  495. })
  496. }
  497. },
  498. computed: {
  499. borderByMode: function () {
  500. return {
  501. // 'border-info':this.inputMode==='regular',
  502. 'border-success':this.inputMode==='increasing',
  503. 'border-danger':this.inputMode==='multiIncreasing'
  504. }
  505. }
  506. }
  507. });
  508. </script>
  509. @endsection