Sfoglia il codice sorgente

Merge branch 'lsy/2026/0522/加工组扫描' into testing

lisongyi 2 settimane fa
parent
commit
aa68bd64e4

+ 12 - 0
src/api/processing/index.ts

@@ -107,3 +107,15 @@ export function status(){
   })
   })
 }
 }
 
 
+/**
+ * 加工组扫描 - 仅提交快递单号
+ * @param deliveryNo 快递单号
+ */
+export function submitProcessingGroupScan(deliveryNo: string) {
+  return request({
+    url: '/api/wms/processing/group-scan',
+    method: 'post',
+    data: { deliveryNo },
+  })
+}
+

+ 6 - 0
src/router/index.ts

@@ -170,6 +170,12 @@ const routes: RouteRecordRaw[] = [
     meta:{title:'加工拍摄任务'},
     meta:{title:'加工拍摄任务'},
     component: () => import('@/views/processing/photoTask/index.vue')
     component: () => import('@/views/processing/photoTask/index.vue')
   },
   },
+  {
+    path: '/processing-group-scan',
+    name: 'ProcessingGroupScan',
+    meta:{title:'加工组扫描'},
+    component: () => import('@/views/processing/groupScan/index.vue')
+  },
   {
   {
     path: '/photo-OCR',
     path: '/photo-OCR',
     name: 'photoOCR',
     name: 'photoOCR',

+ 344 - 0
src/views/processing/groupScan/index.vue

@@ -0,0 +1,344 @@
+<template>
+  <div class="container">
+    <div class="group-scan">
+      <div class="top">
+        <div class="nav-bar">
+          <van-nav-bar
+            title="加工组扫描"
+            left-arrow
+            @click-left="goBack"
+          >
+            <template #left>
+              <van-icon name="arrow-left" size="25" />
+              <div
+                style="
+                  color: #fff;
+                  height: 46px;
+                  padding-right: 20px;
+                  line-height: 46px;
+                "
+              >
+                返回
+              </div>
+            </template>
+          </van-nav-bar>
+        </div>
+
+        <div class="context">
+          <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="扫描面单条码后自动填充,也可手动输入"
+            />
+          </div>
+
+          <div class="upload-section">
+            <div class="upload-tips">
+              <van-icon name="photo" size="20" />
+              <span>请拍摄或选择面单图片,识别一维码</span>
+            </div>
+
+            <van-uploader
+              v-model="uploadImages"
+              :max-count="1"
+              :before-read="beforeReadImage"
+              :after-read="afterReadImage"
+              preview-full-image
+              capture="camera"
+              accept="image/*"
+            >
+              <template #preview-cover>
+                <div class="preview-cover">
+                  <van-icon name="photo" />
+                </div>
+              </template>
+            </van-uploader>
+
+            <div class="upload-footer">
+              <van-button
+                type="primary"
+                block
+                :loading="submitting"
+                :disabled="!expressNo.trim()"
+                @click="handleSubmit"
+              >
+                {{ submitting ? '提交中...' : '提交' }}
+              </van-button>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue'
+import { showNotify, showFailToast, showLoadingToast, closeToast } from 'vant'
+import { submitProcessingGroupScan } from '@/api/processing/index'
+import { getHeader, goBack, scanSuccess, scanError } from '@/utils/android'
+import Quagga from '@ericblade/quagga2'
+
+try {
+  getHeader()
+} catch (error) {
+  console.log(error)
+}
+
+const uploadImages = ref([])
+const submitting = ref(false)
+const expressNo = ref('')
+const expressNoRef = ref(null)
+const recognizing = ref(false)
+
+const beforeReadImage = (file) => {
+  const files = Array.isArray(file) ? file : [file]
+  for (const f of files) {
+    if (f.name.toLowerCase().endsWith('.heic') || f.name.toLowerCase().endsWith('.heif')) {
+      showFailToast('不支持HEIC/HEIF格式的图片')
+      return false
+    }
+    if (!/^image\//.test(f.type)) {
+      showFailToast('请上传图片文件')
+      return false
+    }
+  }
+  return true
+}
+
+const recognizeBarcode = async (file) => {
+  return new Promise((resolve) => {
+    try {
+      recognizing.value = true
+      const img = new Image()
+      const url = URL.createObjectURL(file)
+
+      img.onload = () => {
+        try {
+          const canvas = document.createElement('canvas')
+          const ctx = canvas.getContext('2d')
+          canvas.width = img.width
+          canvas.height = img.height
+          ctx.drawImage(img, 0, 0)
+
+          Quagga.decodeSingle(
+            {
+              decoder: {
+                readers: ['code_128_reader'],
+              },
+              locate: true,
+              src: canvas.toDataURL(),
+              numOfWorkers: 0,
+            },
+            (result) => {
+              if (result && result.codeResult && result.codeResult.code) {
+                const barcodeText = result.codeResult.code
+                URL.revokeObjectURL(url)
+                recognizing.value = false
+                scanSuccess()
+                resolve(barcodeText)
+              } else {
+                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,
+                  },
+                  (fallbackResult) => {
+                    URL.revokeObjectURL(url)
+                    recognizing.value = false
+                    if (fallbackResult && fallbackResult.codeResult && fallbackResult.codeResult.code) {
+                      scanSuccess()
+                      resolve(fallbackResult.codeResult.code)
+                    } else {
+                      scanError()
+                      resolve(null)
+                    }
+                  }
+                )
+              }
+            }
+          )
+        } catch (error) {
+          URL.revokeObjectURL(url)
+          recognizing.value = false
+          scanError()
+          resolve(null)
+        }
+      }
+
+      img.onerror = () => {
+        URL.revokeObjectURL(url)
+        recognizing.value = false
+        scanError()
+        resolve(null)
+      }
+
+      img.src = url
+    } catch (error) {
+      recognizing.value = false
+      scanError()
+      resolve(null)
+    }
+  })
+}
+
+const afterReadImage = async (file) => {
+  const files = Array.isArray(file) ? file : [file]
+  for (const fileItem of files) {
+    const originalFile = fileItem.file
+    if (!originalFile) continue
+
+    showNotify({ type: 'primary', message: '正在识别条码...' })
+    const barcodeResult = await recognizeBarcode(originalFile)
+    if (barcodeResult) {
+      expressNo.value = barcodeResult
+      showNotify({
+        type: 'success',
+        message: `识别到快递单号: ${barcodeResult}`,
+      })
+    } else {
+      showNotify({
+        type: 'warning',
+        message: '未识别到条码,请确保图片清晰或手动输入',
+      })
+    }
+  }
+}
+
+const resetForm = () => {
+  expressNo.value = ''
+  uploadImages.value = []
+}
+
+const handleSubmit = async () => {
+  const deliveryNo = expressNo.value.trim()
+  if (!deliveryNo) {
+    showFailToast('请先扫描或输入快递单号')
+    return
+  }
+
+  submitting.value = true
+  showLoadingToast({ message: '提交中...', forbidClick: true })
+
+  try {
+    const response = await submitProcessingGroupScan(deliveryNo)
+    closeToast()
+    if (response.code === 200) {
+      showNotify({ type: 'success', message: '提交成功' })
+      resetForm()
+    } else {
+      showNotify({ type: 'danger', message: response.message || '提交失败' })
+    }
+  } catch (err) {
+    closeToast()
+    showNotify({
+      type: 'danger',
+      message: '提交异常: ' + (err.message || '未知错误'),
+    })
+  } finally {
+    submitting.value = false
+  }
+}
+</script>
+
+<style scoped lang="sass">
+.container
+  height: 100vh
+  background: #f5f5f5
+
+.group-scan
+  height: 100%
+
+.top
+  height: 100%
+  display: flex
+  flex-direction: column
+
+.nav-bar
+  flex-shrink: 0
+
+.context
+  flex: 1
+  padding: 15px
+  overflow-y: auto
+
+.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
+  border-radius: 8px
+
+.upload-tips
+  display: flex
+  align-items: center
+  justify-content: center
+  margin-bottom: 20px
+  color: #666
+  font-size: 16px
+
+  span
+    margin-left: 8px
+
+.upload-footer
+  margin-top: 20px
+
+.preview-cover
+  position: absolute
+  bottom: 0
+  box-sizing: border-box
+  width: 100%
+  padding: 4px
+  color: #fff
+  font-size: 12px
+  text-align: center
+  background: rgba(0, 0, 0, 0.3)
+
+:deep(.van-uploader__upload)
+  width: 100px
+  height: 100px
+  margin: 0 auto
+  display: block
+
+:deep(.van-uploader__preview-image)
+  width: 100px
+  height: 100px
+  margin: 0 auto
+</style>

+ 4 - 3
src/views/returned/register/index.vue

@@ -298,7 +298,8 @@ const beforeReadImage = async (
     if (isHeicFile) {
     if (isHeicFile) {
       try {
       try {
         const convertedFile = await convertHeicHeifToWebp(f)
         const convertedFile = await convertHeicHeifToWebp(f)
-        normalizedFiles.push(convertedFile)
+        const image = await smartCompressImage(convertedFile)
+        normalizedFiles.push(image)
       } catch (error) {
       } catch (error) {
         console.error('HEIC/HEIF 转换失败:', error)
         console.error('HEIC/HEIF 转换失败:', error)
         showFailToast('HEIC/HEIF 转换失败,请使用 JPG/PNG/WEBP 格式')
         showFailToast('HEIC/HEIF 转换失败,请使用 JPG/PNG/WEBP 格式')
@@ -306,8 +307,8 @@ const beforeReadImage = async (
       }
       }
       continue
       continue
     }
     }
-
-    normalizedFiles.push(f)
+    const image = await smartCompressImage(f)
+    normalizedFiles.push(image)
   }
   }
 
 
   // 返回原始 File 数组 给 van-uploader 进行默认处理,
   // 返回原始 File 数组 给 van-uploader 进行默认处理,