|
|
@@ -37,47 +37,31 @@
|
|
|
<span>请拍摄或选择面单图片进行识别</span>
|
|
|
</div>
|
|
|
|
|
|
- <div class="camera-section">
|
|
|
- <div class="camera-button" @click="openCamera">
|
|
|
- <van-icon name="photo" size="40" />
|
|
|
- <div class="camera-text">点击拍照</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 隐藏的上传组件,用于处理从相册选择 -->
|
|
|
- <van-uploader
|
|
|
- v-model="uploadImages"
|
|
|
- :max-count="1"
|
|
|
- :before-read="beforeReadImage"
|
|
|
- :after-read="afterReadImage"
|
|
|
- preview-full-image
|
|
|
- accept="image/*"
|
|
|
- :show-upload="false"
|
|
|
- style="display: none;"
|
|
|
- ref="uploaderRef"
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 图片预览 -->
|
|
|
- <div v-if="uploadImages.length > 0" class="image-preview">
|
|
|
- <img :src="uploadImages[0].content" class="preview-image" />
|
|
|
- <van-button
|
|
|
- type="danger"
|
|
|
- size="small"
|
|
|
- @click="removeImage"
|
|
|
- class="remove-btn"
|
|
|
- >
|
|
|
- 重新拍摄
|
|
|
- </van-button>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="upload-footer" v-if="uploading">
|
|
|
+ <van-uploader
|
|
|
+ v-model="uploadImages"
|
|
|
+ :max-count="1"
|
|
|
+ :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"
|
|
|
- disabled
|
|
|
+ @click="submitOCR"
|
|
|
+ :disabled="!uploadImages.length"
|
|
|
>
|
|
|
- 自动上传中...
|
|
|
+ {{ uploading ? '上传中...' : '上传识别' }}
|
|
|
</van-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -106,138 +90,6 @@ const warehouse = store.warehouse
|
|
|
|
|
|
const uploadImages = ref([])
|
|
|
const uploading = ref(false)
|
|
|
-const uploaderRef = ref(null)
|
|
|
-
|
|
|
-// 打开相机
|
|
|
-const openCamera = () => {
|
|
|
- // 检查是否在App环境中
|
|
|
- if (window.android && window.android.openCamera) {
|
|
|
- // 调用App原生相机
|
|
|
- window.android.openCamera()
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- // 检查是否在iOS环境中
|
|
|
- if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.openCamera) {
|
|
|
- // 调用iOS原生相机
|
|
|
- window.webkit.messageHandlers.openCamera.postMessage({})
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- // 移动端浏览器:使用兼容性最好的方法
|
|
|
- if (/Android|iPhone|iPad|iPod/i.test(navigator.userAgent)) {
|
|
|
- // 移动设备:使用input file的capture属性
|
|
|
- const input = document.createElement('input')
|
|
|
- input.type = 'file'
|
|
|
- input.accept = 'image/*'
|
|
|
- input.capture = 'camera' // 强制使用相机
|
|
|
-
|
|
|
- input.onchange = (e) => {
|
|
|
- const file = e.target.files[0]
|
|
|
- if (file) {
|
|
|
- handleCameraPhoto(file)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 触发点击
|
|
|
- input.click()
|
|
|
- } else {
|
|
|
- // PC端:使用getUserMedia
|
|
|
- openCameraForPC()
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// PC端打开相机
|
|
|
-const openCameraForPC = async () => {
|
|
|
- try {
|
|
|
- if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
|
|
- showFailToast('您的浏览器不支持相机功能')
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- const stream = await navigator.mediaDevices.getUserMedia({
|
|
|
- video: { facingMode: 'environment' }
|
|
|
- })
|
|
|
- showCameraPreview(stream)
|
|
|
- } catch (error) {
|
|
|
- console.error('打开相机失败:', error)
|
|
|
- showFailToast('打开相机失败,请检查权限设置')
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// 显示相机预览界面(仅PC端使用)
|
|
|
-const showCameraPreview = (stream) => {
|
|
|
- const previewDiv = document.createElement('div')
|
|
|
- previewDiv.style.cssText = `
|
|
|
- position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
|
|
- background: black; z-index: 9999; display: flex; flex-direction: column;
|
|
|
- align-items: center; justify-content: center;
|
|
|
- `
|
|
|
-
|
|
|
- const video = document.createElement('video')
|
|
|
- video.style.cssText = `width: 100%; height: 80%; object-fit: cover;`
|
|
|
- video.srcObject = stream
|
|
|
- video.autoplay = true
|
|
|
- video.playsInline = true
|
|
|
-
|
|
|
- const captureBtn = document.createElement('button')
|
|
|
- captureBtn.style.cssText = `
|
|
|
- width: 80px; height: 80px; border-radius: 50%; background: #fff;
|
|
|
- border: 4px solid #1989fa; margin-top: 20px; cursor: pointer;
|
|
|
- font-size: 16px; color: #1989fa;
|
|
|
- `
|
|
|
- captureBtn.textContent = '拍照'
|
|
|
-
|
|
|
- const closeBtn = document.createElement('button')
|
|
|
- closeBtn.style.cssText = `
|
|
|
- position: absolute; top: 20px; right: 20px; background: rgba(255,255,255,0.3);
|
|
|
- border: none; color: white; font-size: 24px; width: 40px; height: 40px;
|
|
|
- border-radius: 50%; cursor: pointer;
|
|
|
- `
|
|
|
- closeBtn.textContent = '×'
|
|
|
-
|
|
|
- captureBtn.onclick = () => {
|
|
|
- const canvas = document.createElement('canvas')
|
|
|
- const context = canvas.getContext('2d')
|
|
|
- canvas.width = video.videoWidth
|
|
|
- canvas.height = video.videoHeight
|
|
|
- context.drawImage(video, 0, 0)
|
|
|
-
|
|
|
- canvas.toBlob(async (blob) => {
|
|
|
- stream.getTracks().forEach(track => track.stop())
|
|
|
- document.body.removeChild(previewDiv)
|
|
|
- const file = new File([blob], 'camera-photo.jpg', { type: 'image/jpeg' })
|
|
|
- handleCameraPhoto(file)
|
|
|
- }, 'image/jpeg', 0.9)
|
|
|
- }
|
|
|
-
|
|
|
- closeBtn.onclick = () => {
|
|
|
- stream.getTracks().forEach(track => track.stop())
|
|
|
- document.body.removeChild(previewDiv)
|
|
|
- }
|
|
|
-
|
|
|
- previewDiv.appendChild(video)
|
|
|
- previewDiv.appendChild(captureBtn)
|
|
|
- previewDiv.appendChild(closeBtn)
|
|
|
- document.body.appendChild(previewDiv)
|
|
|
- video.play()
|
|
|
-}
|
|
|
-
|
|
|
-// 处理相机拍摄的照片
|
|
|
-const handleCameraPhoto = async (file) => {
|
|
|
- const fileItem = {
|
|
|
- file: file,
|
|
|
- content: URL.createObjectURL(file),
|
|
|
- status: 'uploading'
|
|
|
- }
|
|
|
- uploadImages.value = [fileItem]
|
|
|
- await afterReadImage(fileItem)
|
|
|
-}
|
|
|
-
|
|
|
-// 移除图片
|
|
|
-const removeImage = () => {
|
|
|
- uploadImages.value = []
|
|
|
-}
|
|
|
|
|
|
// 自动上传图片
|
|
|
const autoUploadImage = async (file) => {
|
|
|
@@ -357,7 +209,51 @@ const afterReadImage = async (file) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// 提交OCR识别
|
|
|
+const submitOCR = async () => {
|
|
|
+ if (!uploadImages.value.length) {
|
|
|
+ showFailToast('请先选择图片')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!warehouse) {
|
|
|
+ showFailToast('未获取到仓库信息')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查图片大小是否超过2MB
|
|
|
+ const image = uploadImages.value[0]
|
|
|
+ if (image.file.size > 2 * 1024 * 1024) {
|
|
|
+ showFailToast('图片大小不能超过2MB')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ uploading.value = true
|
|
|
+ const toast = showLoadingToast('识别中...')
|
|
|
|
|
|
+ try {
|
|
|
+ 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
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -433,53 +329,6 @@ onMounted(() => {
|
|
|
text-align: center
|
|
|
background: rgba(0, 0, 0, 0.3)
|
|
|
|
|
|
-.camera-section
|
|
|
- display: flex
|
|
|
- flex-direction: column
|
|
|
- align-items: center
|
|
|
- margin-bottom: 20px
|
|
|
-
|
|
|
-.camera-button
|
|
|
- width: 120px
|
|
|
- height: 120px
|
|
|
- border: 2px dashed #ccc
|
|
|
- border-radius: 8px
|
|
|
- display: flex
|
|
|
- flex-direction: column
|
|
|
- align-items: center
|
|
|
- justify-content: center
|
|
|
- cursor: pointer
|
|
|
- transition: all 0.3s
|
|
|
- background: #f8f8f8
|
|
|
-
|
|
|
-.camera-button:hover
|
|
|
- border-color: #1989fa
|
|
|
- background: #f0f8ff
|
|
|
-
|
|
|
-.camera-button:active
|
|
|
- transform: scale(0.95)
|
|
|
-
|
|
|
-.camera-text
|
|
|
- margin-top: 8px
|
|
|
- font-size: 14px
|
|
|
- color: #666
|
|
|
-
|
|
|
-.image-preview
|
|
|
- display: flex
|
|
|
- flex-direction: column
|
|
|
- align-items: center
|
|
|
- margin-bottom: 20px
|
|
|
-
|
|
|
-.preview-image
|
|
|
- width: 200px
|
|
|
- height: 200px
|
|
|
- object-fit: cover
|
|
|
- border-radius: 8px
|
|
|
- margin-bottom: 10px
|
|
|
-
|
|
|
-.remove-btn
|
|
|
- margin-top: 10px
|
|
|
-
|
|
|
:deep(.van-uploader__upload)
|
|
|
width: 100px
|
|
|
height: 100px
|