record.blade.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. <!DOCTYPE html>
  2. <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1">
  6. <link rel="icon" href="{{asset('icon/faviconc.ico')}}" type="image/x-icon"/>
  7. <!-- CSRF Token -->
  8. <meta name="csrf-token" content="{{ csrf_token() }}">
  9. <title>快递记录</title>
  10. <link href="{{ mix('css/app.css') }}" rel="stylesheet">
  11. <style>
  12. html{
  13. width: 100%;
  14. height: 100%;
  15. }
  16. body{
  17. width: 100%;
  18. height: 100%;
  19. }
  20. .bg-suc{
  21. background: RGB(240,249,235);
  22. }
  23. .bg-fail{
  24. background: RGB(255,240,240);
  25. }
  26. </style>
  27. </head>
  28. <body allowfullscreen="true">
  29. <div class="container-fluid w-100 h-100" id="container">
  30. <div id="toast-container" style="position: absolute;top: 0;width: 100%;z-index: 999"></div>
  31. <div class="row w-100 h-100">
  32. <div class="col-4">
  33. <div class="dropdown">
  34. <button type="button" class="btn col-5 p-0" id="dropdownMenu"
  35. data-toggle="dropdown">
  36. <h4 class="text-muted mt-1">@{{ name }}&nbsp;&nbsp;<i class="fa fa-exchange text-info"></i></h4>
  37. </button>
  38. <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu">
  39. <li role="presentation" v-for="warehouse in warehouses" v-if="warehouse.id!==selected">
  40. <a role="menuitem" tabindex="-1" style="cursor: pointer" class="ml-3"
  41. @click="selectedWarehouse(warehouse)">@{{ warehouse.name }}</a>
  42. </li>
  43. </ul>
  44. </div>
  45. <p class="text-center h1 pt-3 font-weight-bold">@{{sum}}</p>
  46. <p class="text-center text-secondary">本次单量</p>
  47. <div class="form-group mt-4">
  48. <label for="logisticNumber">快递单号</label>
  49. <input class="form-control" disabled id="logisticNumber" placeholder="快递单号"
  50. v-model="logisticNumber">
  51. </div>
  52. <div class="form-group">
  53. <label for="recordAt">记录时间</label>
  54. <input class="form-control" disabled id="recordAt" placeholder="记录时间"
  55. v-model="recordAt">
  56. </div>
  57. <button class="btn btn-info col-12 text-white" @click="fullScreen()" style="position:absolute;bottom: 20px;cursor: pointer">
  58. <span v-if="!full">全屏</span>
  59. <span v-else>退出全屏</span>
  60. </button>
  61. </div>
  62. <div class="col-8 p-0">
  63. <p class="bg-white h2 text-center p-2 text-secondary" style="height: 5vh">记录信息</p>
  64. <div class="w-100" style="height: 93vh;overflow: auto">
  65. <table class="table w-100 text-center">
  66. <thead>
  67. <tr>
  68. <th>快递单号</th>
  69. <th>快递公司</th>
  70. <th>记录时间</th>
  71. <th>上传状态<button class="ml-1 btn btn-sm btn-outline-primary" @click="failRetry()">失败重传</button></th>
  72. </tr>
  73. </thead>
  74. <tbody>
  75. <tr v-for="item in data" :class="item.status ? 'bg-suc text-success' : 'bg-fail text-danger'">
  76. <td>@{{ item.logisticNumber }}</td>
  77. <td>@{{ item.logistic }}</td>
  78. <td>@{{ item.recordAt }}</td>
  79. <td>
  80. <span v-if="item.status">OK</span>
  81. <span v-else>FAIL</span>
  82. </td>
  83. </tr>
  84. </tbody>
  85. </table>
  86. </div>
  87. </div>
  88. </div>
  89. </div>
  90. </body>
  91. <script src="{{ mix('js/app.js') }}"></script>
  92. <script type="text/javascript">
  93. var vue = new Vue({
  94. el:"#container",
  95. data:{
  96. sum:0,
  97. data:[
  98. ],
  99. warehouses:[@foreach($warehouses as $warehouse)@json($warehouse),@endforeach],
  100. logisticNumber:"",
  101. recordAt:"",
  102. maxLen:20,
  103. full:false,
  104. selected:"",
  105. name:"",
  106. errorSum:0,
  107. jwtToken:"{{$jwtToken}}",
  108. voiceLoadSign:false,
  109. },
  110. mounted() {
  111. this.registerEvent();
  112. let record = localStorage.getItem("record:warehouse");
  113. if (record){
  114. let recordObj = JSON.parse(record);
  115. this.selected = recordObj.id;
  116. this.name = recordObj.name;
  117. }else{
  118. if (this.warehouses.length>0)this.selectedWarehouse(this.warehouses[0]);
  119. }
  120. window.onbeforeunload = e=>{
  121. if (this.errorSum===0)return;
  122. e = e || window.event;
  123. // 兼容IE8和Firefox 4之前的版本
  124. if (e) {
  125. e.returnValue = '当前存在失败数据未处理';
  126. }
  127. return '当前存在失败数据未处理';
  128. };
  129. setTimeout(()=>{
  130. this.refreshToken();
  131. },600800000);
  132. },
  133. methods:{
  134. playAudio(src){
  135. let audio = document.createElement('audio');
  136. audio.src = src;
  137. document.body.append(audio);
  138. setTimeout(()=>{
  139. audio.play();
  140. },10);
  141. setTimeout(()=>{
  142. audio.remove();
  143. },5000);
  144. },
  145. refreshToken(){
  146. window.axios.post('{{url('record/refreshToken')}}',{},{
  147. headers: {
  148. "jwtToken" : this.jwtToken
  149. }
  150. }).then(body=>{
  151. let res = body.data;
  152. if (!res.success){
  153. this.playAudio('storage/logonFailure.mp3');
  154. this.buildToast(false,"tokenFailure");
  155. return;
  156. }
  157. this.jwtToken = res.data;
  158. setTimeout(()=>{
  159. this.refreshToken();
  160. },600800000);
  161. }).catch(err=>{
  162. this.playAudio('storage/networkError.mp3');
  163. this.buildToast(false,"tokenFailure");
  164. });
  165. },
  166. selectedWarehouse(warehouse){
  167. this.selected = warehouse.id;
  168. this.name = warehouse.name;
  169. localStorage.setItem("record:warehouse", JSON.stringify(warehouse));
  170. },
  171. //全屏
  172. fullScreen() {
  173. if (this.full){
  174. this.exitFullscreen();
  175. return;
  176. }
  177. let element = document.documentElement;
  178. if (element.requestFullscreen) {
  179. element.requestFullscreen();
  180. } else if (element.msRequestFullscreen) {
  181. element.msRequestFullscreen();
  182. } else if (element.mozRequestFullScreen) {
  183. element.mozRequestFullScreen();
  184. } else if (element.webkitRequestFullscreen) {
  185. element.webkitRequestFullscreen();
  186. }
  187. this.full = true;
  188. },
  189. //退出全屏
  190. exitFullscreen() {
  191. if (document.exitFullscreen) {
  192. document.exitFullscreen();
  193. } else if (document.msExitFullscreen) {
  194. document.msExitFullscreen();
  195. } else if (document.mozCancelFullScreen) {
  196. document.mozCancelFullScreen();
  197. } else if (document.webkitExitFullscreen) {
  198. document.webkitExitFullscreen();
  199. }
  200. this.full = false;
  201. },
  202. registerEvent(){
  203. $(document).on('keypress', e=>{
  204. if (!this.voiceLoadSign){
  205. this.voiceLoadSign = true;
  206. let audio = document.createElement('audio');
  207. let audio1 = document.createElement('audio');
  208. audio.src = 'storage/logonFailure.mp3';
  209. audio1.src = 'storage/networkError.mp3';
  210. document.body.append(audio);
  211. document.body.append(audio1);
  212. setTimeout(()=>{
  213. audio.remove();
  214. audio1.remove();
  215. },10);
  216. }
  217. if (e.keyCode===13){
  218. if(this.logisticNumber==="")return;
  219. this.submitRecord(this.logisticNumber,this.recordAt);
  220. this.logisticNumber = "";
  221. this.recordAt = "";
  222. return;
  223. }
  224. if (this.logisticNumber.length===this.maxLen)return;
  225. if ((e.keyCode>=97 && e.keyCode<=122) || (e.keyCode>=65 && e.keyCode<=90)
  226. || (e.keyCode>=48 && e.keyCode<=57)){
  227. if (this.logisticNumber===""){
  228. let now = new Date();
  229. let yy = now.getFullYear();
  230. let mm = now.getMonth() + 1;
  231. let dd = now.getDate();
  232. let hh = now.getHours();
  233. let m = now.getMinutes();
  234. let ss = now.getSeconds();
  235. this.recordAt = yy+'-'+(mm<10 ? '0'+mm : mm)+'-'+(dd<10 ? '0'+dd : dd)+" "+(hh<10 ? '0'+hh : hh)+":"+(m<10 ? '0'+m : m)+":"+(ss<10 ? '0'+ss : ss);
  236. }
  237. this.logisticNumber += e.key;
  238. }
  239. });
  240. },
  241. _getToDay(){
  242. let now = new Date();
  243. let yy = now.getFullYear();
  244. let mm = now.getMonth() + 1;
  245. let dd = now.getDate();
  246. let hh = now.getHours();
  247. let m = now.getMinutes();
  248. let ss = now.getSeconds();
  249. return yy+'-'+(mm<10 ? '0'+mm : mm)+'-'+(dd<10 ? '0'+dd : dd)+" "+(hh<10 ? '0'+hh : hh)+":"+(m<10 ? '0'+m : m)+":"+(ss<10 ? '0'+ss : ss);
  250. },
  251. failRetry(){
  252. this.data.forEach(item=>{
  253. if (!item.status){
  254. window.axios.post('{{url('record')}}',{logisticNumber:item.logisticNumber,locationAt:item.locationAt,warehouse:this.selected},{
  255. headers: {
  256. "jwtToken" : this.jwtToken
  257. }
  258. }).then(body=>{
  259. let res = body.data;
  260. let status = true;
  261. if (!res.success && res.data!=="unique")status = false;
  262. this.buildToast(status,item.logisticNumber);
  263. if (status){
  264. if (res.data!=="unique"){
  265. item.recordAt = res.data.recordAt;
  266. item.logistic = res.data.logistic;
  267. }
  268. item.status = true;
  269. this.errorSum--;
  270. }
  271. }).catch(err=>{
  272. this.playAudio('storage/networkError.mp3');
  273. this.buildToast(false,item.logisticNumber);
  274. });
  275. }
  276. });
  277. },
  278. submitRecord(logisticNumber,recordAt){
  279. let dateTime = this._getToDay();
  280. window.axios.post('{{url('record')}}',{logisticNumber:logisticNumber,locationAt:dateTime,warehouse:this.selected},{
  281. headers: {
  282. "jwtToken" : this.jwtToken,
  283. }
  284. }).then(body=>{
  285. let res = body.data;
  286. let status = true;
  287. if (!res.success){
  288. status = false;
  289. if (res.data==="unique"){
  290. this.buildToast(false,logisticNumber);
  291. return;
  292. }
  293. }
  294. this.buildToast(status,logisticNumber);
  295. this.data.unshift({
  296. locationAt:dateTime,
  297. logisticNumber:logisticNumber,logistic:status ? res.data.logistic : "未知",recordAt:status ? res.data.recordAt : recordAt,status:status
  298. });
  299. if (!status)this.errorSum++;
  300. this.sum++;
  301. }).catch(err=>{
  302. this.playAudio('storage/networkError.mp3');
  303. this.buildToast(false,logisticNumber);
  304. this.data.unshift({
  305. locationAt:dateTime,
  306. logisticNumber:logisticNumber,logistic:"未知",recordAt:recordAt,status:false
  307. });
  308. this.errorSum++;
  309. this.sum++;
  310. });
  311. },
  312. buildToast(status,sign){
  313. let div = document.createElement("div");
  314. div.className = "d-flex justify-content-center align-items-center p-2";
  315. let toast = document.createElement("div");
  316. toast.className = "toast hide";
  317. toast.setAttribute("role","alert");
  318. toast.setAttribute("aria-live","assertive");
  319. toast.setAttribute("aria-atomic","true");
  320. toast.setAttribute("data-delay","2000");
  321. toast.id = sign;
  322. let info = document.createElement("div");
  323. info.style.width = "400px";
  324. let txt = document.createElement("div");
  325. txt.className = "ml-3";
  326. let i = document.createElement("i");
  327. if (status){
  328. info.style.background = "RGB(240,249,235)";
  329. info.className = "toast-body text-success";
  330. i.className = "fa fa-chevron-circle-down text-success";
  331. }else {
  332. info.style.background = "RGB(255,240,240)";
  333. info.className = "toast-body text-danger";
  334. i.className = "fa fa-times-circle text-danger";
  335. }
  336. txt.append(i);
  337. txt.append(document.createTextNode(" 退件记录"+(status ? '成功' : "失败")+"!"));
  338. info.append(txt);
  339. toast.append(info);
  340. div.append(toast);
  341. $("#toast-container").append(div);
  342. let jqEl = $("#"+sign);
  343. jqEl.toast("show");
  344. jqEl.on("hidden.bs.toast",function (){
  345. div.remove();
  346. });
  347. }
  348. },
  349. });
  350. </script>
  351. </html>