فهرست منبع

计件宝兼容安卓7.1及以上

zh 10 ماه پیش
والد
کامیت
44b5f75e3b

BIN
src/assets/sounds/error.mp3


BIN
src/assets/sounds/relogin.mp3


BIN
src/assets/sounds/scanner_error.mp3


BIN
src/assets/sounds/scanner_repeat.mp3


BIN
src/assets/sounds/tip.mp3


+ 125 - 170
src/views/piece/dashboard/index.vue

@@ -96,7 +96,7 @@
               <div class="col operator">{{ item.operatorName }}</div>
               <div class="col content">{{ item.deliveryNo }}</div>
             </div>
-            <van-back-top />
+            <van-back-top @click="scrollTop"/>
           </van-list>
         </van-pull-refresh>
       </div>
@@ -141,23 +141,21 @@
       </van-notice-bar>
     </van-dialog>
   </van-sticky>
-  <audio ref="audioPlayer" controls hidden="true" />
 </template>
 
 <script setup>
 import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
 import { closeListener, openListener, scanInit } from '@/utils/keydownListener'
-import {
-  getVersionName, saveUserId, getUserId, checkUpdate, saveMacAddress, listErrRecords, readMacAddress,
-  pageDelivery, addDelivery, isDeliveryNoExists, markAsPushed, getErrRecordsCount, removeUserId
-} from '@/utils/androidPiece'
+import { scanSuccess, scanError } from '@/utils/android'
+import { getVersionName, checkUpdate, saveUserId, getUserId, scanRepeat, saveMacAddress, listErrRecords, reLoginTip,
+   readMacAddress, pageDelivery, addDelivery, isDeliveryNoExists, markAsPushed, getErrRecordsCount, removeUserId, scanErr } from '@/utils/androidPiece'
 import { showLoadingToast, showNotify } from 'vant'
 import { getUserIdByCert, getUserNameById } from '@/api/login/index'
 import { receive, getScanDriverInfo } from '@/api/scan/index'
 import { formatDateTime } from '@/utils/date'
 
 // 在输入对话框或界面中添加提示文本
-const hintMessage = ref(`请前往:设置 → 关于平板电脑 → 设备WLAN MAC 地址 长按复制并粘贴到此处。`)
+const hintMessage = ref(`请前往:设置 → 关于平板电脑 → 设备WLAN MAC 地址 长按复制并粘贴到此处。`);
 const mac = ref('')
 const userInfo = ref({
   userId: '',
@@ -178,8 +176,8 @@ const preDeliveryNo = ref('')
 // 小青蛙位置属性
 const frogPosition = ref({
   warehouse: '',
-  type: '',
-  code: ''
+  type:'',
+  code:''
 })
 
 
@@ -193,13 +191,13 @@ const updateNetworkStatus = () => {
 const logout = () => {
   if (!userInfo.value.userId) {
     scanError()
-    showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '请先登录!' })
+    showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '请先登录!' });
     return
   }
   userInfo.value = {}
   removeUserId()
   scanSuccess()
-  showNotify({ type: 'success', style: 'font-size: 30px !important;height:50px', message: '已退出登录!' })
+  showNotify({ type: 'success', style: 'font-size: 30px !important;height:50px', message: '已退出登录!' });
 }
 
 // 网络提示
@@ -274,7 +272,7 @@ const _openScan = () => {
   openListener()
   setTimeout(() => {
     scanInit(debounceScan)
-  }, 300)
+  },300)
 }
 
 const pushing = ref(false)
@@ -295,17 +293,17 @@ const debounceScan = (code) => {
 const _handlerScan = (code) => {
   code = fixDuplicateText(code)
   // 校验扫描的是否为登录二维码
-  const regex = /\{"sign":"([^"]*)",\s*"identity":(\d+)\}/
+  const regex = /\{"sign":"([^"]*)",\s*"identity":(\d+)\}/;
   if (code !== null && regex.test(code)) {
     const match = regex.exec(code)
     const sign = match[1]
     const identity = match[2]
-    getUserIdByCert({ sign: sign, identity: identity }).then(res => {
+    getUserIdByCert({sign: sign, identity: identity}).then(res => {
       if (res && res.data) {
         userInfo.value.userId = res.data
         getUserName(res.data)
       } else {
-        showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '登录失败!' })
+        showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '登录失败!' });
       }
     })
   } else {
@@ -313,57 +311,45 @@ const _handlerScan = (code) => {
     if (typeof code !== 'string' || code.length < 11 || code.length > 25) {
       console.log(code)
       scanErr()
-      showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '请扫描正确的快递单号!' })
+      showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '请扫描正确的快递单号!' });
       return
     }
     // 数字0-9,大写字母A-Z,部分特殊字符(空格、! " % & ' ( ) * + , - . / : ; < = > ? _)
-    const code128BPattern = /^[\x20-\x7F]+$/
+    const code128BPattern = /^[\x20-\x7F]+$/;
     // 检查是否只包含有效字符
     if (!code128BPattern.test(code)) {
       console.log(code)
       scanErr()
-      showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '请扫描正确的快递单号!' })
+      showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '请扫描正确的快递单号!' });
       return
     }
-    // 添加滚动到顶部的逻辑
-    nextTick(() => {
-      if (pullRefreshRef.value.$el) {
-        pullRefreshRef.value.$el.scrollTo({
-          top: 0,
-          behavior: 'smooth' // 平滑滚动效果
-        })
-      }
-    })
+    scrollTop()
     // 校验是否已登录
     if (!userInfo.value.userId) {
       scanError()
-      showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '请先登录!' })
+      showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '请先登录!' });
       return
     }
     // 校验是否为空
     if (!code) {
       scanErr()
-      showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '请先扫描快递单号!' })
+      showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '请先扫描快递单号!' });
       return
     }
 
     if (pushing.value) {
       scanErr()
-      showNotify({
-        type: 'danger',
-        style: 'font-size: 30px !important;height:50px',
-        message: '正在提交中,请稍后扫描!'
-      })
+      showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '正在提交中,请稍后扫描!' });
       return
     }
-    pushing.value = true  // 请求状态锁
+    pushing.value =  true  // 请求状态锁
 
     // 校验是否已扫描
     if (list.value.some(item => item.code === code)) {
       scanRepeat()
       pushing.value = false
       message.value = '该快递单已扫描!'
-      showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '该快递单已扫描!' })
+      showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '该快递单已扫描!' });
       return
     }
 
@@ -372,7 +358,7 @@ const _handlerScan = (code) => {
       scanRepeat()
       pushing.value = false
       message.value = '该快递单已扫描!'
-      showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '该快递单已扫描!' })
+      showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '该快递单已扫描!' });
       return
     }
 
@@ -380,7 +366,7 @@ const _handlerScan = (code) => {
       message: '上传中...',
       forbidClick: true,
       duration: 0 // 禁止自动关闭
-    })
+    });
     // 校验是否已存在
     const currentTime = formatDateTime(new Date())
     message.value = ''
@@ -392,8 +378,7 @@ const _handlerScan = (code) => {
       operatorName: userInfo.value.name,
       isPush: 1,
       preDeliveryNo: preDeliveryNo.value,
-      version: versionName.value
-    }
+      version: versionName.value }
 
     receive(dto).then(res => {
       if (res && res.code === 200) {
@@ -401,23 +386,19 @@ const _handlerScan = (code) => {
         if (result === -1) {
           scanRepeat()
           message.value = '该快递单已扫描!'
-          showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '该快递单已扫描!' })
+          showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '该快递单已扫描!' });
           return
         }
         if (result === -2) {
           scanErr()
-          showNotify({
-            type: 'danger',
-            style: 'font-size: 30px !important;height:50px',
-            message: '添加失败,请联系开发人员!'
-          })
+          showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '添加失败,请联系开发人员!' });
           return
         }
         preDeliveryNo.value = code
         scanSuccess()
         message.value = code + '扫描成功!'
         list.value.unshift(dto)
-        showNotify({ type: 'success', style: 'font-size: 30px !important;height:50px', message: '扫描成功!' })
+        showNotify({ type: 'success', style: 'font-size: 30px !important;height:50px', message: '扫描成功!' });
       }
     }).catch(err => {
       if (err.code === 700) {
@@ -426,105 +407,113 @@ const _handlerScan = (code) => {
       } else {
         scanErr()
       }
-      showNotify({
-        type: 'danger',
-        style: 'font-size: 30px !important;height:50px',
-        message: `扫描失败!${err.message}`
-      })
+      showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: `扫描失败!${err.message}` });
     }).finally(() => {
-      toast.close()
+      toast.close();
       pushing.value = false
     })
   }
 }
 
+const scrollTop = () => {
+  // 添加滚动到顶部的逻辑
+  nextTick(() => {
+    const el = pullRefreshRef.value?.$el;
+    if (el && el.scrollTo) {
+      el.scrollTo({ top: 0, behavior: 'smooth' });
+    } else if (el) {
+      // 回退方案
+      el.scrollTop = 0;
+    }
+  })
+}
 function fixDuplicateText(input) {
   // 改进版正则表达式(支持任意字符且精确匹配完全重复)
-  const regex = /^(.+?)\1$/ // 非贪婪模式防止误匹配
-  const match = input.match(regex)
+  const regex = /^(.+?)\1$/; // 非贪婪模式防止误匹配
+  const match = input.match(regex);
   // 返回处理逻辑
-  return match ? match[1] : input
+  return match ? match[1] : input;
 }
 
 
 // 在外部定义一个防抖函数映射表,用于跟踪每个单据的防抖状态
-const debounceMap = new Map()
+const debounceMap = new Map();
 
 // 防抖函数封装
 const debounce = (func, wait = 500) => {
-  let timer
+  let timer;
   return (...args) => {
-    clearTimeout(timer)
+    clearTimeout(timer);
     timer = setTimeout(() => {
-      func.apply(this, args)
-    }, wait)
-  }
-}
+      func.apply(this, args);
+    }, wait);
+  };
+};
 
 // 修改后的推送函数
 const _rePush = (item) => {
-  if (!item || item.isPush === 1) return
+  if (!item || item.isPush === 1) return;
   let status = false
   showLoadingToast({
     message: '上传中...',
     forbidClick: true,
     closeToast: status
-  })
+  });
   // 初始化当前单据的防抖状态
   if (!debounceMap.has(item.deliveryNo)) {
     debounceMap.set(item.deliveryNo, {
       isPushing: false,  // 请求状态锁
       debouncedFn: debounce(() => {
-        const entry = debounceMap.get(item.deliveryNo)
-        if (!entry || entry.isPushing) return
+        const entry = debounceMap.get(item.deliveryNo);
+        if (!entry || entry.isPushing) return;
 
-        entry.isPushing = true  // 加锁
+        entry.isPushing = true;  // 加锁
 
         const dto = {
           deliveryNo: item.deliveryNo,
           machine: item.machine,
           operator: item.operator,
           operationTime: item.operationTime
-        }
+        };
 
         receive(dto).then(res => {
-          const result = markAsPushed(item.deliveryNo)
-          entry.isPushing = false  // 请求完成解锁
-          item.isPush = 1
-          scanSuccess()
+          const result = markAsPushed(item.deliveryNo);
+          entry.isPushing = false;  // 请求完成解锁
+          item.isPush = 1;
+          scanSuccess();
           status = true
           errNum.value -= 1
           showNotify({
             type: 'success',
             style: 'font-size: 30px !important;height:50px',
             message: '推送成功!'
-          })
+          });
         }).catch(err => {
-          entry.isPushing = false  // 请求失败解锁
-          scanError()
+          entry.isPushing = false;  // 请求失败解锁
+          scanError();
           status = true
           showNotify({
             type: 'danger',
             style: 'font-size: 30px !important;height:50px',
             message: `推单失败!${err.message}`
-          })
-        })
+          });
+        });
       }, 500)  // 500ms防抖时间
-    })
+    });
   }
 
   // 执行防抖函数
-  const { debouncedFn } = debounceMap.get(item.deliveryNo)
-  debouncedFn()
-}
+  const { debouncedFn } = debounceMap.get(item.deliveryNo);
+  debouncedFn();
+};
 
 const getUserName = (userId) => {
   getUserNameById(userId, mac.value).then(res => {
     if (res && res.data) {
-      scanSuccess()
       saveUserId(userInfo.value.userId)
       userInfo.value.name = res.data
-      showNotify({ type: 'success', style: 'font-size: 30px !important;height:50px', message: '登录成功!' })
+      scanSuccess()
+      showNotify({ type: 'success', style: 'font-size: 30px !important;height:50px', message: '登录成功!' });
     }
   })
 }
@@ -552,30 +541,29 @@ const _closeSetting = () => {
 }
 
 const _saveMac = () => {
-  if (!isValidMacAddress(setting.value.mac)) {
-    showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '请输入正确的MAC地址!' })
+  if(!isValidMacAddress(setting.value.mac)) {
+    showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '请输入正确的MAC地址!' });
     return
   }
   if (!isValidPassword(setting.value.password)) {
-    showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '请输入正确的密码!' })
+    showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '请输入正确的密码!' });
     return
   }
-  if (!saveMacAddress(setting.value.mac)) {
-    showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: 'MAC保存失败!' })
+  if(!saveMacAddress(setting.value.mac)) {
+    showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: 'MAC保存失败!' });
     return
   }
-  showNotify({ type: 'success', style: 'font-size: 30px !important;height:50px', message: 'MAC保存成功!' })
+  showNotify({ type: 'success', style: 'font-size: 30px !important;height:50px', message: 'MAC保存成功!' });
   mac.value = setting.value.mac
   getFrogPosition()
 }
 
 function isValidMacAddress(mac) {
-  const regex = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/
-  return mac !== null && regex.test(mac)
+  const regex = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;
+  return mac !== null && regex.test(mac);
 }
-
 function isValidPassword(password) {
-  return password === '123456'
+  return password === '123456';
 }
 
 // 网络状态和ip
@@ -583,18 +571,18 @@ const ipAddress = ref(null)
 
 // 获取 IP 地址
 const fetchIP = async () => {
-  const RTCPeerConnection = window.RTCPeerConnection
+  const RTCPeerConnection = window.RTCPeerConnection;
   if (RTCPeerConnection) {
-    const pc = new RTCPeerConnection({ iceServers: [] })
-    pc.createDataChannel('')
+    const pc = new RTCPeerConnection({ iceServers: [] });
+    pc.createDataChannel('');
     pc.createOffer()
       .then(sdp => pc.setLocalDescription(sdp))
-      .catch(console.error)
+      .catch(console.error);
     pc.onicecandidate = e => {
-      if (!e.candidate) return
-      const ip = /([0-9]{1,3}(\.[0-9]{1,3}){3})/.exec(e.candidate.candidate)?.[1]
-      if (ip) ipAddress.value = ip
-    }
+      if (!e.candidate) return;
+      const ip = /([0-9]{1,3}(\.[0-9]{1,3}){3})/.exec(e.candidate.candidate)?.[1];
+      if (ip) ipAddress.value = ip;
+    };
   }
 }
 
@@ -607,36 +595,36 @@ const size = ref(10)
 // 模拟数据加载
 const onLoad = () => {
   if (refreshing.value) {
-    list.value = []
-    refreshing.value = false
+    list.value = [];
+    refreshing.value = false;
   }
   const result = pageDelivery(page.value, size.value)
   if (result.size < size.value) {
-    finished.value = true
+    finished.value = true;
   } else {
     page.value += 1
   }
   list.value.push(...result)
-  loading.value = false
-}
-const refreshing = ref(false)
+  loading.value = false;
+};
+const refreshing = ref(false);
 const onRefresh = () => {
   // 清空列表数据
-  finished.value = false
+  finished.value = false;
 
   // 重新加载数据
   // 将 loading 设置为 true,表示处于加载状态
-  loading.value = true
+  loading.value = true;
   page.value = 1
-  onLoad()
-}
+  onLoad();
+};
 const isOnePush = ref(false)
 const _one_click_push = () => {
   isOnePush.value = true
   const result = listErrRecords()
   if (result && result != '[]') {
-    const records = JSON.parse(result)
-    const promises = [] // 收集所有请求的Promise
+    const records = JSON.parse(result);
+    const promises = []; // 收集所有请求的Promise
 
     records.forEach(item => {
       // 初始化当前单据的防抖状态
@@ -644,93 +632,60 @@ const _one_click_push = () => {
         const debounceEntry = {
           isPushing: false,
           debouncedFn: debounce(() => {
-            const entry = debounceMap.get(item.deliveryNo)
-            if (!entry || entry.isPushing) return
+            const entry = debounceMap.get(item.deliveryNo);
+            if (!entry || entry.isPushing) return;
 
-            entry.isPushing = true  // 加锁
+            entry.isPushing = true;  // 加锁
 
             const dto = {
               deliveryNo: item.deliveryNo,
               machine: item.machine,
               operator: item.operator,
               operationTime: item.operationTime
-            }
+            };
 
             // 将请求包装成Promise并收集
             const promise = receive(dto)
               .then(res => {
-                const result = markAsPushed(item.deliveryNo)
+                const result = markAsPushed(item.deliveryNo);
                 list.value.filter(entry => entry.deliveryNo === item.deliveryNo).forEach(entry => {
-                  entry.isPush = 1
+                  entry.isPush = 1;
                 })
                 errNum.value -= 1
               })
               .finally(() => {
-                entry.isPushing = false // 无论成功失败都解锁
-              })
+                entry.isPushing = false; // 无论成功失败都解锁
+              });
 
-            promises.push(promise) // 添加到Promise数组
-            return promise
+            promises.push(promise); // 添加到Promise数组
+            return promise;
           }, 500)
-        }
-        debounceMap.set(item.deliveryNo, debounceEntry)
+        };
+        debounceMap.set(item.deliveryNo, debounceEntry);
       }
 
       // 立即执行防抖函数并收集Promise
-      const promise = debounceMap.get(item.deliveryNo).debouncedFn()
-      if (promise) promises.push(promise)
-    })
+      const promise = debounceMap.get(item.deliveryNo).debouncedFn();
+      if (promise) promises.push(promise);
+    });
 
     // 所有请求完成后重置状态
     Promise.allSettled(promises)
       .then(_ => {
-        scanSuccess()
-        showNotify({ type: 'success', style: 'font-size: 30px !important;height:50px', message: '数据已推送!' })
+        scanSuccess();
+        showNotify({ type: 'success', style: 'font-size: 30px !important;height:50px', message: '数据已推送!' });
       })
       .catch(_ => {
-        scanError()
-        showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '存在数据推送失败!' })
+        scanError();
+        showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '存在数据推送失败!' });
       }).finally(() => {
-      isOnePush.value = false
-    })
+      isOnePush.value = false;
+    });
   } else {
-    showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '暂无异常数据!' })
-    isOnePush.value = false // 没有数据时立即重置
+    showNotify({ type: 'danger', style: 'font-size: 30px !important;height:50px', message: '暂无异常数据!' });
+    isOnePush.value = false; // 没有数据时立即重置
   }
 }
-
-
-const soundPaths = {
-  error: new URL('@/assets/sounds/error.mp3', import.meta.url).href,
-  tip: new URL('@/assets/sounds/tip.mp3', import.meta.url).href,
-  repeat: new URL('@/assets/sounds/scanner_repeat.mp3', import.meta.url).href,
-  reLogin: new URL('@/assets/sounds/relogin.mp3', import.meta.url).href,
-  scanError: new URL('@/assets/sounds/scanner_error.mp3', import.meta.url).href
-}
-
-const audioPlayer = ref(null)
-
-// 统一播放函数
-const playSound = (type) => {
-  nextTick(() => {
-    if (!audioPlayer.value) return
-
-    // 避免音频重叠播放
-    audioPlayer.value.pause()
-    audioPlayer.value.currentTime = 0
-
-    // 设置对应音源并播放
-    audioPlayer.value.src = soundPaths[type]
-    audioPlayer.value.play()
-  })
-}
-
-// 语义化播放方法
-const scanSuccess = () => playSound('tip')
-const scanError = () => playSound('error')  // 原errorAudio
-const scanRepeat = () => playSound('repeat')
-const reLoginTip = () => playSound('reLogin')
-const scanErr = () => playSound('scanError')  // 原scanErrorAudio
 </script>
 
 <style scoped lang="sass">