2 Commits d9f75e17a0 ... 21626f00b5

Auteur SHA1 Bericht Datum
  lisongyi 21626f00b5 Merge remote-tracking branch 'origin/master' 2 maanden geleden
  lisongyi 905fc83f96 ocr增加条码识别 2 maanden geleden
3 gewijzigde bestanden met toevoegingen van 177 en 8 verwijderingen
  1. 1 0
      package.json
  2. 5 1
      src/api/inbound/index.ts
  3. 171 7
      src/views/inbound/photoOCR/index.vue

+ 1 - 0
package.json

@@ -13,6 +13,7 @@
     "preview": "vite preview"
   },
   "dependencies": {
+    "@ericblade/quagga2": "^1.12.1",
     "axios": "^1.7.9",
     "lib-flexible": "^0.3.2",
     "pinia": "^2.3.0",

+ 5 - 1
src/api/inbound/index.ts

@@ -5,11 +5,15 @@ import request from '@/utils/request'
  * 面单识别OCR - 上传图片进行识别
  * @param file 图片文件
  * @param warehouse 仓库编码
+ * @param barcode 识别到的条码(可选)
  */
-export function uploadOCRImage(file: File, warehouse: string) {
+export function uploadOCRImage(file: File, warehouse: string, barcode?: string) {
   const formData = new FormData()
   formData.append('file', file)
   formData.append('warehouse', warehouse)
+  if (barcode) {
+    formData.append('barcode', barcode)
+  }
   
   return request({
     url: '/api/entryOrder/OCR/upload',

+ 171 - 7
src/views/inbound/photoOCR/index.vue

@@ -30,6 +30,22 @@
             <van-cell title="当前仓库" :value="warehouse" />
           </div>
 
+          <!-- 快递单号输入框 -->
+          <div class="express-input-box">
+            <div class="express-input-text">
+              <div>快递单号</div>
+            </div>
+            <van-field 
+              class="express-input" 
+              ref="expressNoRef"
+              :style="expressNo!==''?'border: 2px solid #07c160':''" 
+              clearable 
+              v-model="expressNo"
+              placeholder="识别完成后自动填充"
+              readonly
+            />
+          </div>
+
           <!-- 拍照上传区域 -->
           <div class="upload-section">
             <div class="upload-tips">
@@ -75,9 +91,10 @@
 import { ref, onMounted } from 'vue'
 import { showNotify, showFailToast, showLoadingToast, closeToast } from 'vant'
 import { uploadOCRImage } from '@/api/inbound/index'
-import { getHeader, goBack } from '@/utils/android'
+import { getHeader, goBack, scanSuccess, scanError } from '@/utils/android'
 import { compressImage } from '@/utils/imageCompression'
 import { useStore } from '@/store/modules/user'
+import Quagga from '@ericblade/quagga2'
 
 try {
   getHeader()
@@ -90,9 +107,12 @@ const warehouse = store.warehouse
 
 const uploadImages = ref([])
 const uploading = ref(false)
+const expressNo = ref('') // 快递单号
+const expressNoRef = ref(null)
+const recognizing = ref(false) // 条码识别中
 
 // 自动上传图片
-const autoUploadImage = async (file) => {
+const autoUploadImage = async (file, barcode = null) => {
   if (!warehouse) {
     showFailToast('未获取到仓库信息')
     return
@@ -108,13 +128,13 @@ const autoUploadImage = async (file) => {
   const toast = showLoadingToast('自动上传识别中...')
 
   try {
-    const response = await uploadOCRImage(file, warehouse)
+    const response = await uploadOCRImage(file, warehouse, barcode)
     
     closeToast()
     
     if (response.code === 200) {
       showNotify({ type: 'success', message: '面单上传成功' })
-      // 上传成功后重置表单
+      // 上传成功后重置表单(但保留快递单号)
       uploadImages.value = []
     } else {
       showNotify({ type: 'danger', message: response.message || '上传失败' })
@@ -149,6 +169,112 @@ const beforeReadImage = (file) => {
   return true
 }
 
+// 使用 Quagga2 识别图片中的条码(Code128)
+// 注意:decodeSingle 只能识别一个条码,如果图片中有多个条码,会识别到第一个找到的
+const recognizeBarcode = async (file) => {
+  return new Promise((resolve) => {
+    try {
+      recognizing.value = true
+      const imageFile = file
+      
+      // 创建图片对象
+      const img = new Image()
+      const url = URL.createObjectURL(imageFile)
+      
+      img.onload = () => {
+        try {
+          // 创建canvas
+          const canvas = document.createElement('canvas')
+          const ctx = canvas.getContext('2d')
+          canvas.width = img.width
+          canvas.height = img.height
+          ctx.drawImage(img, 0, 0)
+          
+          // 使用 Quagga2 识别条码(优先识别 Code128 格式)
+          Quagga.decodeSingle(
+            {
+              decoder: {
+                readers: ['code_128_reader'] // 优先识别 Code128 格式(快递单号通常是 Code128)
+              },
+              locate: true,
+              src: canvas.toDataURL(),
+              numOfWorkers: 0 // 不使用 Web Workers,避免兼容性问题
+            },
+            (result) => {
+              if (result && result.codeResult && result.codeResult.code) {
+                const barcodeText = result.codeResult.code
+                URL.revokeObjectURL(url)
+                recognizing.value = false
+                console.log('识别到条码(Code128):', barcodeText)
+                scanSuccess()
+                resolve(barcodeText)
+              } else {
+                // 如果 Code128 失败,尝试所有格式
+                Quagga.decodeSingle(
+                  {
+                    decoder: {
+                      readers: [
+                        'code_128_reader',
+                        'ean_reader',
+                        'ean_8_reader',
+                        'code_39_reader',
+                        'code_39_vin_reader',
+                        'codabar_reader',
+                        'upc_reader',
+                        'upc_e_reader',
+                        'i2of5_reader'
+                      ]
+                    },
+                    locate: true,
+                    src: canvas.toDataURL(),
+                    numOfWorkers: 0
+                  },
+                  (result) => {
+                    URL.revokeObjectURL(url)
+                    recognizing.value = false
+                    
+                    if (result && result.codeResult && result.codeResult.code) {
+                      const barcodeText = result.codeResult.code
+                      console.log('识别到条码(其他格式):', barcodeText, '格式:', result.codeResult.format)
+                      scanSuccess()
+                      resolve(barcodeText)
+                    } else {
+                      console.log('条码识别失败')
+                      scanError()
+                      resolve(null)
+                    }
+                  }
+                )
+              }
+            }
+          )
+        } catch (error) {
+          URL.revokeObjectURL(url)
+          recognizing.value = false
+          console.error('条码识别过程出错:', error)
+          scanError()
+          resolve(null)
+        }
+      }
+      
+      img.onerror = () => {
+        URL.revokeObjectURL(url)
+        recognizing.value = false
+        console.error('图片加载失败')
+        scanError()
+        resolve(null)
+      }
+      
+      img.src = url
+    } catch (error) {
+      recognizing.value = false
+      console.error('条码识别初始化失败:', error)
+      scanError()
+      resolve(null)
+    }
+  })
+}
+
 // 图片读取完成后处理
 const afterReadImage = async (file) => {
   try {
@@ -162,6 +288,26 @@ const afterReadImage = async (file) => {
       if (originalFile) {
         let finalFile = originalFile
         
+        // 先尝试识别条码
+        showNotify({ 
+          type: 'primary', 
+          message: '正在识别条码...' 
+        })
+        const barcodeResult = await recognizeBarcode(originalFile)
+        if (barcodeResult) {
+          expressNo.value = barcodeResult
+          showNotify({ 
+            type: 'success', 
+            message: `识别到快递单号: ${barcodeResult}` 
+          })
+        } else {
+          expressNo.value = ''
+          showNotify({ 
+            type: 'warning', 
+            message: '未识别到条码,请确保图片清晰' 
+          })
+        }
+        
         // 检查文件大小,如果大于1MB则进行压缩
         if (originalFile.size > 1 * 1024 * 1024) {
           showNotify({ 
@@ -196,8 +342,8 @@ const afterReadImage = async (file) => {
           console.log(`图片大小符合要求,无需压缩: ${(originalFile.size / 1024).toFixed(2)}KB`)
         }
         
-        // 压缩完成后自动上传
-        await autoUploadImage(finalFile)
+        // 压缩完成后自动上传(带上识别到的条码)
+        await autoUploadImage(finalFile, expressNo.value || null)
       }
     }
   } catch (error) {
@@ -236,7 +382,7 @@ const submitOCR = async () => {
     formData.append('file', image.file)
     formData.append('warehouse', warehouse)
     
-    const response = await uploadOCRImage(image.file, warehouse)
+    const response = await uploadOCRImage(image.file, warehouse, expressNo.value || undefined)
     
     closeToast()
     
@@ -290,6 +436,24 @@ onMounted(() => {
   border-radius: 8px
   overflow: hidden
 
+.express-input-box
+  margin-bottom: 20px
+  padding: 10px 5px
+  .express-input-text
+    display: flex
+    justify-content: space-between
+    align-items: center
+    font-size: 18px
+    font-weight: bold
+    margin: 10px 0
+    padding: 5px 0
+  .express-input
+    background: #eff0f2
+    padding: 10px 20px
+    font-size: 20px
+    border: 2px solid #0077ff
+    font-weight: 500
+
 .upload-section
   background: #fff
   padding: 20px