3 Commits 10f072273c ... 5145a45420

Auteur SHA1 Message Date
  lisongyi 5145a45420 Merge branch 'lsy-20251017-OCR识别' il y a 5 mois
  lisongyi e3b69c27f3 ocr图片识别-后端 il y a 5 mois
  lisongyi a624c84821 ocr识别 il y a 5 mois

+ 20 - 0
src/api/inbound/index.ts

@@ -0,0 +1,20 @@
+// @ts-ignore
+import request from '@/utils/request'
+
+/**
+ * 面单识别OCR - 上传图片进行识别
+ * @param file 图片文件
+ * @param warehouse 仓库编码
+ */
+export function uploadOCRImage(file: File, warehouse: string) {
+  const formData = new FormData()
+  formData.append('file', file)
+  formData.append('warehouse', warehouse)
+  
+  return request({
+    url: '/api/entryOrder/OCR/upload',
+    method: 'post',
+    headers: { 'Content-Type': 'multipart/form-data' },
+    data: formData,
+  })
+}

+ 5 - 0
src/hooks/basic/menu.js

@@ -29,6 +29,11 @@ export default function() {
               icon: 'newspaper-o',
               path: 'putaway',
             },
+            {
+              title: '面单拍照',
+              icon: 'newspaper-o',
+              path: 'photo-OCR',
+            },
           ],
         },
         {

+ 6 - 0
src/router/index.ts

@@ -151,6 +151,12 @@ const routes: RouteRecordRaw[] = [
     meta:{title:'加工拍摄任务'},
     component: () => import('@/views/processing/photoTask/index.vue')
   },
+  {
+    path: '/photo-OCR',
+    name: 'photoOCR',
+    meta:{title:'面单识别'},
+    component: () => import('@/views/inbound/photoOCR/index.vue')
+  },
 ];
 
 // 创建路由实例

+ 1 - 1
src/static/setting.txt

@@ -1 +1 @@
-66
+72

+ 299 - 0
src/views/inbound/photoOCR/index.vue

@@ -0,0 +1,299 @@
+<template>
+  <div class="container">
+    <div class="photo-ocr">
+      <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="warehouse-info">
+            <van-cell title="当前仓库" :value="warehouse" />
+          </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"
+              :max-size="2 * 1024 * 1024"
+              :before-read="beforeReadImage"
+              :after-read="afterReadImage"
+              preview-full-image
+              capture="camera"
+              accept="image/*"
+            >
+              <template #preview-cover="{ file }">
+                <div class="preview-cover">
+                  <van-icon name="photo" />
+                </div>
+              </template>
+            </van-uploader>
+
+            <div class="upload-footer">
+              <van-button
+                type="primary"
+                block
+                :loading="uploading"
+                @click="submitOCR"
+                :disabled="!uploadImages.length"
+              >
+                {{ uploading ? '上传中...' : '上传识别' }}
+              </van-button>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+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 { compressImage } from '@/utils/imageCompression'
+import { useStore } from '@/store/modules/user'
+
+try {
+  getHeader()
+} catch (error) {
+  console.log(error)
+}
+
+const store = useStore()
+const warehouse = store.warehouse
+
+const uploadImages = ref([])
+const uploading = 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
+    }
+
+    // 检查文件类型
+    const isImage = /^image\//.test(f.type)
+    if (!isImage) {
+      showFailToast('请上传图片文件')
+      return false
+    }
+
+    // 检查文件大小 (2MB)
+    if (f.size > 2 * 1024 * 1024) {
+      showFailToast('图片大小不能超过2MB')
+      return false
+    }
+  }
+  return true
+}
+
+// 图片读取完成后处理
+const afterReadImage = async (file) => {
+  try {
+    // 处理单个文件或多个文件
+    const files = Array.isArray(file) ? file : [file]
+    
+    for (let i = 0; i < files.length; i++) {
+      const fileItem = files[i]
+      const originalFile = fileItem.file
+      
+      if (originalFile) {
+        // 检查文件大小,如果大于1MB则进行压缩
+        if (originalFile.size > 1 * 1024 * 1024) {
+          showNotify({ 
+            type: 'primary', 
+            message: `正在压缩图片: ${originalFile.name} (${(originalFile.size / 1024 / 1024).toFixed(2)}MB)` 
+          })
+          
+          try {
+            // 压缩图片到1MB以内
+            const compressedFile = await compressImage(originalFile, 1 * 1024 * 1024)
+            
+            // 更新文件对象
+            fileItem.file = compressedFile
+            fileItem.content = URL.createObjectURL(compressedFile)
+            
+            // 显示压缩结果
+            const compressionRatio = ((originalFile.size - compressedFile.size) / originalFile.size * 100).toFixed(1)
+            showNotify({ 
+              type: 'success', 
+              message: `图片压缩完成: ${(compressedFile.size / 1024 / 1024).toFixed(2)}MB (压缩${compressionRatio}%)` 
+            })
+          } catch (error) {
+            console.error('图片压缩失败:', error)
+            showNotify({ 
+              type: 'warning', 
+              message: `图片压缩失败,将使用原图上传: ${error.message}` 
+            })
+            // 压缩失败时保持原文件
+          }
+        } else {
+          console.log(`图片大小符合要求,无需压缩: ${(originalFile.size / 1024).toFixed(2)}KB`)
+        }
+      }
+    }
+  } catch (error) {
+    console.error('处理图片时发生错误:', error)
+    showNotify({ 
+      type: 'danger', 
+      message: '处理图片时发生错误: ' + error.message 
+    })
+  }
+}
+
+// 提交OCR识别
+const submitOCR = async () => {
+  if (!uploadImages.value.length) {
+    showFailToast('请先选择图片')
+    return
+  }
+
+  if (!warehouse) {
+    showFailToast('未获取到仓库信息')
+    return
+  }
+
+  uploading.value = true
+  const toast = showLoadingToast('识别中...')
+
+  try {
+    const image = uploadImages.value[0]
+    const formData = new FormData()
+    formData.append('file', image.file)
+    formData.append('warehouse', warehouse)
+    
+    const response = await uploadOCRImage(image.file, warehouse)
+    
+    closeToast()
+    
+    if (response.code === 200) {
+      showNotify({ type: 'success', message: '面单上传成功' })
+      // 上传成功后重置表单
+      uploadImages.value = []
+    } else {
+      showNotify({ type: 'danger', message: response.message || '上传失败' })
+    }
+  } catch (err) {
+    closeToast()
+    showNotify({ type: 'danger', message: '识别过程发生异常: ' + (err.message || '未知错误') })
+  } finally {
+    uploading.value = false
+  }
+}
+
+
+
+onMounted(() => {
+  // 页面加载时的初始化操作
+  console.log('面单识别页面加载完成')
+})
+</script>
+
+<style scoped lang="sass">
+.container
+  height: 100vh
+  background: #f5f5f5
+
+.photo-ocr
+  height: 100%
+
+.top
+  height: 100%
+  display: flex
+  flex-direction: column
+
+.nav-bar
+  flex-shrink: 0
+
+.context
+  flex: 1
+  padding: 15px
+  overflow-y: auto
+
+.warehouse-info
+  margin-bottom: 20px
+  background: #fff
+  border-radius: 8px
+  overflow: hidden
+
+.upload-section
+  background: #fff
+  padding: 20px
+  border-radius: 8px
+  margin-bottom: 20px
+
+.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
+
+.result-section
+  background: #fff
+  padding: 20px
+  border-radius: 8px
+
+.result-actions
+  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>