bonus-material-app/src/pages/devicesSearch/OcrCamera.vue

364 lines
7.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="camera-container">
<!-- 相机预览层由插件控制 -->
<!-- 自定义OCR框 -->
<view class="preview-overlay">
<view class="ocr-frame">
<view class="frame-label">请将编码置于框内拍摄</view>
</view>
</view>
<!-- 预览结果 -->
<view class="preview-result" v-if="previewImage">
<image :src="previewImage" alt="预览" class="result-image"/>
</view>
<view class="status">OCR相机模式</view>
<!-- 控制按钮 -->
<view class="controls">
<view class="close-btn" @click="closeCamera">×</view>
<view class="capture-btn" @click="captureImage"></view>
</view>
<!-- 加载指示器 -->
<view class="loading" v-if="isLoading">
<view class="loading-spinner"></view>
<text class="loading-text">处理中...</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
previewImage: null,
frameRect: {
x: 0,
y: 0,
width: 0,
height: 0
},
isLoading: false
};
},
mounted() {
this.initCamera();
// 初始计算框位置
this.calculateFrameRect();
// 监听窗口变化
uni.onWindowResize(() => {
this.calculateFrameRect();
});
},
beforeUnmount() {
this.stopCamera();
},
methods: {
// 计算OCR框的位置和尺寸
calculateFrameRect() {
const query = uni.createSelectorQuery().in(this);
query.select('.ocr-frame').boundingClientRect(data => {
if (data) {
this.frameRect = {
x: data.left,
y: data.top,
width: data.width,
height: data.height
};
}
}).exec();
},
// 初始化相机
initCamera() {
if (typeof cordova === 'undefined') {
console.warn('Cordova环境未加载');
return;
}
document.addEventListener('deviceready', () => {
if (cordova.plugins && cordova.plugins.camerapreview) {
window.CameraPreview = cordova.plugins.camerapreview;
this.startCamera();
} else {
console.error('CameraPreview插件未加载');
}
}, false);
},
// 启动相机预览
startCamera() {
if (!window.CameraPreview) return;
const screenWidth = uni.getSystemInfoSync().screenWidth;
const screenHeight = uni.getSystemInfoSync().screenHeight;
window.CameraPreview.startCamera({
x: 0,
y: 0,
width: screenWidth,
height: screenHeight,
camera: 'back',
toBack: true,
tapPhoto: false,
previewDrag: false,
disableExifHeader: true
},
() => console.log('相机启动成功'),
(err) => console.error('相机启动失败', err));
},
// 停止相机
stopCamera() {
if (window.CameraPreview) {
window.CameraPreview.stopCamera();
}
},
// 拍照并裁剪
captureImage() {
if (!window.CameraPreview) return;
this.isLoading = true;
window.CameraPreview.takePicture({quality: 85}, (imageData) => {
const fullBase64 = 'data:image/jpeg;base64,' + imageData;
this.cropToOcrFrame(fullBase64);
}, (error) => {
console.error('拍照失败', error);
this.isLoading = false;
uni.showToast({
title: '拍照失败',
icon: 'none'
});
});
},
// 裁剪到OCR框区域
cropToOcrFrame(base64) {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 计算裁剪区域(考虑设备像素比)
const dpr = window.devicePixelRatio || 1;
const cropX = this.frameRect.x * dpr;
const cropY = this.frameRect.y * dpr;
const cropWidth = this.frameRect.width * dpr;
const cropHeight = this.frameRect.height * dpr;
// 设置画布尺寸
canvas.width = cropWidth;
canvas.height = cropHeight;
// 裁剪图像
ctx.drawImage(
img,
cropX, cropY, cropWidth, cropHeight,
0, 0, cropWidth, cropHeight
);
// 获取裁剪后的base64
const croppedBase64 = canvas.toDataURL('image/jpeg', 0.9);
this.previewImage = croppedBase64;
this.isLoading = false;
// 这里可以添加OCR识别逻辑
console.log('裁剪后的Base64:', croppedBase64.substring(0, 100) + '...');
// 在实际应用中这里可以触发OCR识别
// this.recognizeOcr(croppedBase64);
};
img.src = base64;
},
// OCR识别方法示例
recognizeOcr(base64Image) {
// 在实际应用中这里调用OCR识别API
console.log('调用OCR识别...');
// uni.request({
// url: 'https://api.ocr.example.com/recognize',
// method: 'POST',
// data: { image: base64Image.split(',')[1] },
// success: (res) => {
// console.log('OCR识别结果:', res.data);
// }
// });
},
// 关闭相机
closeCamera() {
this.stopCamera();
// 返回上一页或执行其他关闭逻辑
uni.navigateBack();
}
}
};
</script>
<style scoped>
.camera-container {
position: relative;
width: 100vw;
height: 100vh;
overflow: hidden;
background-color: transparent;
}
.preview-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 10;
pointer-events: none;
}
.ocr-frame {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80%;
height: 30%;
border: 4px solid #00ff00;
border-radius: 12px;
box-shadow: 0 0 0 10000px rgba(0, 0, 0, 0.6);
pointer-events: none;
z-index: 11;
}
.frame-label {
position: absolute;
bottom: -40px;
left: 50%;
transform: translateX(-50%);
color: white;
font-size: 16px;
text-align: center;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
width: 100%;
}
.controls {
position: absolute;
bottom: 40px;
left: 0;
width: 100%;
display: flex;
justify-content: center;
z-index: 20;
gap: 30px;
}
.capture-btn {
width: 70px;
height: 70px;
border-radius: 50%;
background: white;
border: 4px solid #f0f0f0;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.capture-btn::after {
content: '';
width: 50px;
height: 50px;
background: white;
border-radius: 50%;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2) inset;
}
.close-btn {
width: 50px;
height: 50px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
border: 2px solid white;
color: white;
font-size: 30px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.preview-result {
position: absolute;
top: 20px;
right: 20px;
width: 120px;
height: 160px;
border: 2px solid white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
z-index: 15;
}
.result-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.status {
position: absolute;
top: 20px;
left: 20px;
color: white;
font-size: 14px;
background: rgba(0, 0, 0, 0.5);
padding: 5px 12px;
border-radius: 20px;
z-index: 15;
}
.loading {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
}
.loading-spinner {
width: 50px;
height: 50px;
border: 4px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top: 4px solid #00ff00;
animation: spin 1s linear infinite;
}
.loading-text {
margin-top: 15px;
color: white;
font-size: 16px;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>