ocr
This commit is contained in:
parent
c19c31e62a
commit
a19de9187a
|
|
@ -75,36 +75,65 @@
|
||||||
</uni-forms>
|
</uni-forms>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 相机预览页面 -->
|
<!-- 相机预览页面 -->
|
||||||
<view v-if="showCamera" class="camera-container">
|
<view v-if="showCamera" class="camera-container">
|
||||||
<!-- 相机预览容器 -->
|
<!-- 遮罩层,中间透明 -->
|
||||||
<view class="camera-preview-container" id="cameraPreview"></view>
|
<view class="camera-overlay">
|
||||||
|
<!-- 顶部遮罩 -->
|
||||||
|
<view class="overlay-top"></view>
|
||||||
|
<!-- 中间行:左遮罩 + 透明区域 + 右遮罩 -->
|
||||||
|
<view class="overlay-middle">
|
||||||
|
<view class="overlay-left"></view>
|
||||||
|
<view class="overlay-transparent"></view>
|
||||||
|
<view class="overlay-right"></view>
|
||||||
|
</view>
|
||||||
|
<!-- 底部遮罩 -->
|
||||||
|
<view class="overlay-bottom"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 顶部提示 -->
|
<!-- 顶部提示 -->
|
||||||
<view class="top-tip">
|
<view class="top-tip">
|
||||||
<text class="tip-text">请将识别编码置于取景框内,完成扫描</text>
|
<text class="tip-text">请将识别编码置于取景框内,完成扫描</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 聚焦状态提示 -->
|
||||||
|
<view v-if="isFocusing" class="focus-tip">
|
||||||
|
<text class="focus-text">正在聚焦...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 取景框 -->
|
<!-- 取景框 -->
|
||||||
<view class="viewfinder-container">
|
<view class="viewfinder-container">
|
||||||
<view class="viewfinder">
|
<view class="viewfinder" @click="handleTapToFocus">
|
||||||
|
<!-- 四个角的装饰 -->
|
||||||
<view class="corner corner-top-left"></view>
|
<view class="corner corner-top-left"></view>
|
||||||
<view class="corner corner-top-right"></view>
|
<view class="corner corner-top-right"></view>
|
||||||
<view class="corner corner-bottom-left"></view>
|
<view class="corner corner-bottom-left"></view>
|
||||||
<view class="corner corner-bottom-right"></view>
|
<view class="corner corner-bottom-right"></view>
|
||||||
|
|
||||||
|
<!-- 扫描线动画 -->
|
||||||
|
<view class="scan-line"></view>
|
||||||
|
|
||||||
|
<!-- 聚焦指示器 -->
|
||||||
|
<view v-if="showFocusIndicator" class="focus-indicator" :style="focusIndicatorStyle">
|
||||||
|
<view class="focus-ring" :class="{ 'focusing': isFocusing, 'focused': focusSuccess }"></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 底部控制区 -->
|
<!-- 底部控制区 -->
|
||||||
<view class="bottom-controls">
|
<view class="bottom-controls">
|
||||||
<view class="control-btn" @click="closeCamera">
|
<view class="control-btn" @click="closeCamera">
|
||||||
<text class="control-icon">✕</text>
|
<text class="control-icon">✕</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="photo-btn" @click="takePicture" :class="{ 'taking': isTaking }">
|
<view class="photo-btn" @click="takePicture" :class="{ 'taking': isTaking, 'disabled': isFocusing }">
|
||||||
<view class="photo-btn-inner"></view>
|
<view class="photo-btn-inner"></view>
|
||||||
</view>
|
</view>
|
||||||
<view class="control-btn" @click="openGallery">
|
<view class="control-btn" @click="openGallery">
|
||||||
<text class="control-icon">📷</text>
|
<text class="control-icon">📷</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 加载提示 -->
|
<!-- 加载提示 -->
|
||||||
<view v-if="isProcessing" class="loading-overlay">
|
<view v-if="isProcessing" class="loading-overlay">
|
||||||
<view class="loading-content">
|
<view class="loading-content">
|
||||||
|
|
@ -113,10 +142,17 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 隐藏的canvas用于图片处理 -->
|
||||||
|
<canvas
|
||||||
|
canvas-id="imageCanvas"
|
||||||
|
style="position: fixed; top: -9999px; left: -9999px; width: 1px; height: 1px;"
|
||||||
|
></canvas>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import ImageUtils from "../../services/utils/imageUtils";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -135,7 +171,14 @@ export default {
|
||||||
cameraReady: false,
|
cameraReady: false,
|
||||||
deviceReadyTimeout: null,
|
deviceReadyTimeout: null,
|
||||||
pluginCheckRetries: 0,
|
pluginCheckRetries: 0,
|
||||||
maxRetries: 10
|
maxRetries: 10,
|
||||||
|
// 聚焦相关状态
|
||||||
|
isFocusing: false,
|
||||||
|
focusSuccess: false,
|
||||||
|
showFocusIndicator: false,
|
||||||
|
focusIndicatorStyle: {},
|
||||||
|
autoFocusEnabled: true,
|
||||||
|
focusTimeout: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onShow() {
|
onShow() {
|
||||||
|
|
@ -152,21 +195,17 @@ export default {
|
||||||
initializeCordova() {
|
initializeCordova() {
|
||||||
console.log('开始初始化Cordova...');
|
console.log('开始初始化Cordova...');
|
||||||
this.pluginCheckRetries = 0;
|
this.pluginCheckRetries = 0;
|
||||||
|
|
||||||
// 清除之前的超时
|
// 清除之前的超时
|
||||||
if (this.deviceReadyTimeout) {
|
if (this.deviceReadyTimeout) {
|
||||||
clearTimeout(this.deviceReadyTimeout);
|
clearTimeout(this.deviceReadyTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果Cordova已经准备好,直接检查插件
|
// 如果Cordova已经准备好,直接检查插件
|
||||||
if (this.isCordovaReady()) {
|
if (this.isCordovaReady()) {
|
||||||
this.onDeviceReady();
|
this.onDeviceReady();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听deviceready事件
|
// 监听deviceready事件
|
||||||
document.addEventListener('deviceready', this.onDeviceReady, false);
|
document.addEventListener('deviceready', this.onDeviceReady, false);
|
||||||
|
|
||||||
// 设置超时,防止无限等待
|
// 设置超时,防止无限等待
|
||||||
this.deviceReadyTimeout = setTimeout(() => {
|
this.deviceReadyTimeout = setTimeout(() => {
|
||||||
console.warn('Cordova初始化超时,尝试直接检查插件');
|
console.warn('Cordova初始化超时,尝试直接检查插件');
|
||||||
|
|
@ -185,16 +224,13 @@ export default {
|
||||||
// 设备准备就绪
|
// 设备准备就绪
|
||||||
onDeviceReady() {
|
onDeviceReady() {
|
||||||
console.log('Cordova设备准备就绪');
|
console.log('Cordova设备准备就绪');
|
||||||
|
|
||||||
// 清除超时
|
// 清除超时
|
||||||
if (this.deviceReadyTimeout) {
|
if (this.deviceReadyTimeout) {
|
||||||
clearTimeout(this.deviceReadyTimeout);
|
clearTimeout(this.deviceReadyTimeout);
|
||||||
this.deviceReadyTimeout = null;
|
this.deviceReadyTimeout = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移除事件监听器,避免重复调用
|
// 移除事件监听器,避免重复调用
|
||||||
document.removeEventListener('deviceready', this.onDeviceReady);
|
document.removeEventListener('deviceready', this.onDeviceReady);
|
||||||
|
|
||||||
// 检查相机插件
|
// 检查相机插件
|
||||||
this.checkCameraPlugin();
|
this.checkCameraPlugin();
|
||||||
},
|
},
|
||||||
|
|
@ -202,18 +238,14 @@ export default {
|
||||||
// 检查相机插件 - 改进版本
|
// 检查相机插件 - 改进版本
|
||||||
checkCameraPlugin() {
|
checkCameraPlugin() {
|
||||||
console.log(`检查相机插件... (尝试 ${this.pluginCheckRetries + 1}/${this.maxRetries})`);
|
console.log(`检查相机插件... (尝试 ${this.pluginCheckRetries + 1}/${this.maxRetries})`);
|
||||||
|
|
||||||
// 按优先级检查插件可用性
|
// 按优先级检查插件可用性
|
||||||
const pluginAvailable = this.getCameraPlugin();
|
const pluginAvailable = this.getCameraPlugin();
|
||||||
|
|
||||||
if (pluginAvailable) {
|
if (pluginAvailable) {
|
||||||
console.log('相机插件可用:', pluginAvailable);
|
console.log('相机插件可用:', pluginAvailable);
|
||||||
this.cameraReady = true;
|
this.cameraReady = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pluginCheckRetries++;
|
this.pluginCheckRetries++;
|
||||||
|
|
||||||
if (this.pluginCheckRetries < this.maxRetries) {
|
if (this.pluginCheckRetries < this.maxRetries) {
|
||||||
// 递增延迟重试
|
// 递增延迟重试
|
||||||
const delay = Math.min(1000 * this.pluginCheckRetries, 5000);
|
const delay = Math.min(1000 * this.pluginCheckRetries, 5000);
|
||||||
|
|
@ -241,7 +273,6 @@ export default {
|
||||||
() => window.plugins?.CameraPreview,
|
() => window.plugins?.CameraPreview,
|
||||||
() => cordova?.plugins?.CameraPreview
|
() => cordova?.plugins?.CameraPreview
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let getPlugin of possiblePaths) {
|
for (let getPlugin of possiblePaths) {
|
||||||
try {
|
try {
|
||||||
const plugin = getPlugin();
|
const plugin = getPlugin();
|
||||||
|
|
@ -253,18 +284,15 @@ export default {
|
||||||
// 忽略访问错误,继续尝试下一个路径
|
// 忽略访问错误,继续尝试下一个路径
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
// 打开相机 - 改进版本
|
// 打开相机 - 改进版本
|
||||||
async openCamera() {
|
async openCamera() {
|
||||||
console.log('尝试打开相机...');
|
console.log('尝试打开相机...');
|
||||||
|
|
||||||
if (!this.cameraReady) {
|
if (!this.cameraReady) {
|
||||||
// 再次尝试检查插件
|
// 再次尝试检查插件
|
||||||
this.checkCameraPlugin();
|
this.checkCameraPlugin();
|
||||||
|
|
||||||
if (!this.cameraReady) {
|
if (!this.cameraReady) {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
|
|
@ -274,17 +302,13 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 显示相机界面
|
// 显示相机界面
|
||||||
this.showCamera = true;
|
this.showCamera = true;
|
||||||
|
|
||||||
// 等待UI更新
|
// 等待UI更新
|
||||||
await this.$nextTick();
|
await this.$nextTick();
|
||||||
|
|
||||||
// 初始化相机
|
// 初始化相机
|
||||||
await this.initCamera();
|
await this.initCamera();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('打开相机失败:', error);
|
console.error('打开相机失败:', error);
|
||||||
this.showCamera = false;
|
this.showCamera = false;
|
||||||
|
|
@ -296,77 +320,198 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 初始化相机 - 改进版本
|
// 初始化相机 - 改进版本,添加聚焦支持
|
||||||
async initCamera() {
|
async initCamera() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
console.log('初始化相机预览...');
|
||||||
const CameraPreview = this.getCameraPlugin();
|
const CameraPreview = this.getCameraPlugin();
|
||||||
if (!CameraPreview) {
|
if (!CameraPreview) {
|
||||||
reject(new Error('相机插件不可用'));
|
reject(new Error('相机插件不可用'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用setTimeout确保DOM更新完成
|
// 获取屏幕尺寸
|
||||||
setTimeout(() => {
|
const screenWidth = uni.getSystemInfoSync().screenWidth;
|
||||||
const query = uni.createSelectorQuery().in(this);
|
const screenHeight = uni.getSystemInfoSync().screenHeight;
|
||||||
query.select('.camera-preview-container').boundingClientRect(data => {
|
|
||||||
if (!data) {
|
|
||||||
reject(new Error('无法获取相机容器尺寸'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算实际像素值(某些平台需要px而非rpx)
|
|
||||||
const pixelRatio = uni.getSystemInfoSync().pixelRatio || 1;
|
|
||||||
const width = data.width * pixelRatio;
|
|
||||||
const height = data.height * pixelRatio;
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
x: data.left,
|
x: 0,
|
||||||
y: data.top,
|
y: 0,
|
||||||
width: data.width,
|
width: window.innerWidth || 375,
|
||||||
height: data.height,
|
height: window.innerHeight || 667,
|
||||||
camera: CameraPreview.CAMERA_DIRECTION?.BACK || 'back',
|
camera: CameraPreview.CAMERA_DIRECTION?.BACK || 'back',
|
||||||
tapPhoto: false,
|
tapPhoto: false,
|
||||||
previewDrag: false,
|
previewDrag: false,
|
||||||
toBack: true,
|
toBack: true, // 设置为 true,让相机显示在背景
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
tapFocus: true
|
tapFocus: true, // 启用点击聚焦
|
||||||
|
disableExifHeaderStripping: false
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('相机配置:', options);
|
console.log('相机配置:', options);
|
||||||
|
CameraPreview.startCamera(
|
||||||
|
options,
|
||||||
|
(result) => {
|
||||||
|
console.log('相机启动成功:', result);
|
||||||
|
this.cameraStarted = true;
|
||||||
|
// 启动后进行一次自动聚焦
|
||||||
|
this.performAutoFocus();
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.error('相机启动失败:', error);
|
||||||
|
this.cameraStarted = false;
|
||||||
|
reject(new Error(`相机启动失败: ${error}`));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
// 先停止已有相机(如果有)
|
// 执行自动聚焦
|
||||||
CameraPreview.stopCamera(() => {
|
async performAutoFocus() {
|
||||||
// 启动新相机
|
if (!this.cameraStarted || !this.autoFocusEnabled) {
|
||||||
CameraPreview.startCamera(
|
return;
|
||||||
options,
|
}
|
||||||
() => {
|
|
||||||
console.log('相机启动成功');
|
const CameraPreview = this.getCameraPlugin();
|
||||||
this.cameraStarted = true;
|
if (!CameraPreview || typeof CameraPreview.setFocus !== 'function') {
|
||||||
|
console.log('相机插件不支持聚焦功能');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.isFocusing = true;
|
||||||
|
this.focusSuccess = false;
|
||||||
|
|
||||||
|
// 清除之前的聚焦超时
|
||||||
|
if (this.focusTimeout) {
|
||||||
|
clearTimeout(this.focusTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('开始自动聚焦...');
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
// 设置聚焦超时
|
||||||
|
this.focusTimeout = setTimeout(() => {
|
||||||
|
console.log('聚焦超时');
|
||||||
|
this.isFocusing = false;
|
||||||
|
resolve();
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
CameraPreview.setFocus(
|
||||||
|
(result) => {
|
||||||
|
console.log('自动聚焦成功:', result);
|
||||||
|
if (this.focusTimeout) {
|
||||||
|
clearTimeout(this.focusTimeout);
|
||||||
|
this.focusTimeout = null;
|
||||||
|
}
|
||||||
|
this.isFocusing = false;
|
||||||
|
this.focusSuccess = true;
|
||||||
|
// 显示聚焦成功状态一段时间
|
||||||
|
setTimeout(() => {
|
||||||
|
this.focusSuccess = false;
|
||||||
|
}, 1000);
|
||||||
resolve();
|
resolve();
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
console.error('相机启动失败:', error);
|
console.error('自动聚焦失败:', error);
|
||||||
reject(new Error('相机启动失败'));
|
if (this.focusTimeout) {
|
||||||
|
clearTimeout(this.focusTimeout);
|
||||||
|
this.focusTimeout = null;
|
||||||
|
}
|
||||||
|
this.isFocusing = false;
|
||||||
|
resolve(); // 即使聚焦失败也继续
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, () => {
|
});
|
||||||
// 即使停止失败也继续启动
|
} catch (error) {
|
||||||
CameraPreview.startCamera(
|
console.error('聚焦过程出错:', error);
|
||||||
options,
|
this.isFocusing = false;
|
||||||
() => {
|
}
|
||||||
console.log('相机启动成功');
|
},
|
||||||
this.cameraStarted = true;
|
|
||||||
|
// 处理点击聚焦
|
||||||
|
async handleTapToFocus(event) {
|
||||||
|
if (!this.cameraStarted || this.isFocusing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CameraPreview = this.getCameraPlugin();
|
||||||
|
if (!CameraPreview || typeof CameraPreview.tapToFocus !== 'function') {
|
||||||
|
console.log('相机插件不支持点击聚焦功能');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取点击位置
|
||||||
|
const rect = event.currentTarget.getBoundingClientRect();
|
||||||
|
const x = event.detail.x - rect.left;
|
||||||
|
const y = event.detail.y - rect.top;
|
||||||
|
|
||||||
|
// 转换为相对坐标 (0-1)
|
||||||
|
const relativeX = x / rect.width;
|
||||||
|
const relativeY = y / rect.height;
|
||||||
|
|
||||||
|
console.log('点击聚焦位置:', { x: relativeX, y: relativeY });
|
||||||
|
|
||||||
|
// 显示聚焦指示器
|
||||||
|
this.showFocusIndicator = true;
|
||||||
|
this.focusIndicatorStyle = {
|
||||||
|
left: `${x - 30}rpx`,
|
||||||
|
top: `${y - 30}rpx`
|
||||||
|
};
|
||||||
|
|
||||||
|
this.isFocusing = true;
|
||||||
|
this.focusSuccess = false;
|
||||||
|
|
||||||
|
// 清除之前的聚焦超时
|
||||||
|
if (this.focusTimeout) {
|
||||||
|
clearTimeout(this.focusTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
// 设置聚焦超时
|
||||||
|
this.focusTimeout = setTimeout(() => {
|
||||||
|
console.log('点击聚焦超时');
|
||||||
|
this.isFocusing = false;
|
||||||
|
this.showFocusIndicator = false;
|
||||||
|
resolve();
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
CameraPreview.tapToFocus(
|
||||||
|
relativeX,
|
||||||
|
relativeY,
|
||||||
|
(result) => {
|
||||||
|
console.log('点击聚焦成功:', result);
|
||||||
|
if (this.focusTimeout) {
|
||||||
|
clearTimeout(this.focusTimeout);
|
||||||
|
this.focusTimeout = null;
|
||||||
|
}
|
||||||
|
this.isFocusing = false;
|
||||||
|
this.focusSuccess = true;
|
||||||
|
// 显示聚焦成功状态
|
||||||
|
setTimeout(() => {
|
||||||
|
this.focusSuccess = false;
|
||||||
|
this.showFocusIndicator = false;
|
||||||
|
}, 1000);
|
||||||
resolve();
|
resolve();
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
console.error('相机启动失败:', error);
|
console.error('点击聚焦失败:', error);
|
||||||
reject(new Error('相机启动失败'));
|
if (this.focusTimeout) {
|
||||||
|
clearTimeout(this.focusTimeout);
|
||||||
|
this.focusTimeout = null;
|
||||||
|
}
|
||||||
|
this.isFocusing = false;
|
||||||
|
this.showFocusIndicator = false;
|
||||||
|
resolve(); // 即使聚焦失败也继续
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}).exec();
|
} catch (error) {
|
||||||
}, 300); // 适当延迟确保DOM更新
|
console.error('点击聚焦过程出错:', error);
|
||||||
});
|
this.isFocusing = false;
|
||||||
|
this.showFocusIndicator = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 关闭相机
|
// 关闭相机
|
||||||
|
|
@ -379,6 +524,18 @@ export default {
|
||||||
} finally {
|
} finally {
|
||||||
this.showCamera = false;
|
this.showCamera = false;
|
||||||
this.cameraStarted = false;
|
this.cameraStarted = false;
|
||||||
|
this.resetFocusState();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 重置聚焦状态
|
||||||
|
resetFocusState() {
|
||||||
|
this.isFocusing = false;
|
||||||
|
this.focusSuccess = false;
|
||||||
|
this.showFocusIndicator = false;
|
||||||
|
if (this.focusTimeout) {
|
||||||
|
clearTimeout(this.focusTimeout);
|
||||||
|
this.focusTimeout = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -387,17 +544,11 @@ export default {
|
||||||
if (!this.cameraStarted) {
|
if (!this.cameraStarted) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
const CameraPreview = this.getCameraPlugin();
|
const CameraPreview = this.getCameraPlugin();
|
||||||
if (!CameraPreview) {
|
if (!CameraPreview) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
// 先隐藏相机预览
|
|
||||||
CameraPreview.hideCamera(() => {
|
|
||||||
console.log('相机已隐藏');
|
|
||||||
// 然后停止相机
|
|
||||||
CameraPreview.stopCamera(
|
CameraPreview.stopCamera(
|
||||||
() => {
|
() => {
|
||||||
console.log('相机已停止');
|
console.log('相机已停止');
|
||||||
|
|
@ -407,35 +558,26 @@ export default {
|
||||||
(error) => {
|
(error) => {
|
||||||
console.error('停止相机失败:', error);
|
console.error('停止相机失败:', error);
|
||||||
this.cameraStarted = false;
|
this.cameraStarted = false;
|
||||||
resolve();
|
resolve(); // 即使失败也继续
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, (error) => {
|
|
||||||
console.error('隐藏相机失败:', error);
|
|
||||||
// 即使隐藏失败也继续停止
|
|
||||||
CameraPreview.stopCamera(
|
|
||||||
() => {
|
|
||||||
console.log('相机已停止');
|
|
||||||
this.cameraStarted = false;
|
|
||||||
resolve();
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
console.error('停止相机失败:', error);
|
|
||||||
this.cameraStarted = false;
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// 拍照
|
// 拍照 - 添加聚焦逻辑
|
||||||
async takePicture() {
|
async takePicture() {
|
||||||
if (!this.cameraStarted || this.isTaking) {
|
if (!this.cameraStarted || this.isTaking || this.isFocusing) {
|
||||||
|
if (this.isFocusing) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '正在聚焦,请稍候...',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 1000
|
||||||
|
});
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('开始拍照...');
|
console.log('开始拍照流程...');
|
||||||
this.isTaking = true;
|
this.isTaking = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -444,6 +586,11 @@ export default {
|
||||||
throw new Error('相机插件不可用');
|
throw new Error('相机插件不可用');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 拍照前先进行聚焦
|
||||||
|
console.log('拍照前聚焦...');
|
||||||
|
await this.performFocusBeforeCapture();
|
||||||
|
|
||||||
|
console.log('开始拍照...');
|
||||||
const imageData = await new Promise((resolve, reject) => {
|
const imageData = await new Promise((resolve, reject) => {
|
||||||
const options = {
|
const options = {
|
||||||
quality: 85,
|
quality: 85,
|
||||||
|
|
@ -465,7 +612,6 @@ export default {
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.processImage(imageData);
|
await this.processImage(imageData);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('拍照过程出错:', error);
|
console.error('拍照过程出错:', error);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
|
|
@ -477,17 +623,69 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 处理图片
|
// 拍照前聚焦
|
||||||
|
async performFocusBeforeCapture() {
|
||||||
|
const CameraPreview = this.getCameraPlugin();
|
||||||
|
if (!CameraPreview || typeof CameraPreview.setFocus !== 'function') {
|
||||||
|
console.log('相机插件不支持聚焦,直接拍照');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.isFocusing = true;
|
||||||
|
|
||||||
|
console.log('拍照前执行聚焦...');
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
// 设置聚焦超时
|
||||||
|
const focusTimeout = setTimeout(() => {
|
||||||
|
console.log('拍照前聚焦超时,继续拍照');
|
||||||
|
this.isFocusing = false;
|
||||||
|
resolve();
|
||||||
|
}, 2000); // 拍照前聚焦时间稍短
|
||||||
|
|
||||||
|
CameraPreview.setFocus(
|
||||||
|
(result) => {
|
||||||
|
console.log('拍照前聚焦成功:', result);
|
||||||
|
clearTimeout(focusTimeout);
|
||||||
|
this.isFocusing = false;
|
||||||
|
// 聚焦成功后稍等一下再拍照,确保聚焦稳定
|
||||||
|
setTimeout(resolve, 200);
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.error('拍照前聚焦失败:', error);
|
||||||
|
clearTimeout(focusTimeout);
|
||||||
|
this.isFocusing = false;
|
||||||
|
resolve(); // 即使聚焦失败也继续拍照
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('拍照前聚焦过程出错:', error);
|
||||||
|
this.isFocusing = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理图片 - 使用ImageUtils
|
||||||
async processImage(imageData) {
|
async processImage(imageData) {
|
||||||
this.isProcessing = true;
|
this.isProcessing = true;
|
||||||
try {
|
try {
|
||||||
console.log('开始OCR识别...');
|
console.log('开始处理图片...');
|
||||||
|
// 使用ImageUtils处理图片
|
||||||
|
const processedBase64 = await ImageUtils.processImage(imageData, {
|
||||||
|
maxWidth: 1024,
|
||||||
|
maxHeight: 1024,
|
||||||
|
quality: 50,
|
||||||
|
outputFormat: 'base64'
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('图片处理完成,开始OCR识别...');
|
||||||
const response = await new Promise((resolve, reject) => {
|
const response = await new Promise((resolve, reject) => {
|
||||||
uni.request({
|
uni.request({
|
||||||
url: '/material/app/ocr/getOcrCode',
|
url: '/material/app/ocr/getOcrCode',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
image: imageData,
|
image: processedBase64,
|
||||||
jiju_type: '',
|
jiju_type: '',
|
||||||
auth_lic: 'xIWDlaDVdijcBB4mjhGCPYk5Kvk8tHZJbUn+vW+ih15+MYx98e/PXyBmKL5gFcWMPznLgDA15QuSAnZQSLddwdy9HkZgtuQDEEZZ351Eyb1eiDUccUnyoSGIrNimbx5TooBNNPYqU4qJeFrPJXAqjBHzRrxoBxuR2CEGKQPgHC4='
|
auth_lic: 'xIWDlaDVdijcBB4mjhGCPYk5Kvk8tHZJbUn+vW+ih15+MYx98e/PXyBmKL5gFcWMPznLgDA15QuSAnZQSLddwdy9HkZgtuQDEEZZ351Eyb1eiDUccUnyoSGIrNimbx5TooBNNPYqU4qJeFrPJXAqjBHzRrxoBxuR2CEGKQPgHC4='
|
||||||
},
|
},
|
||||||
|
|
@ -512,7 +710,7 @@ export default {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('OCR识别失败:', error);
|
console.error('图片处理或OCR识别失败:', error);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '识别失败,请重试',
|
title: '识别失败,请重试',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
|
|
@ -542,22 +740,58 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// 将图片转换为base64
|
// 将图片转换为base64 - 使用ImageUtils处理
|
||||||
convertImageToBase64(filePath) {
|
async convertImageToBase64(filePath) {
|
||||||
// #ifdef APP-PLUS
|
this.isProcessing = true;
|
||||||
plus.io.resolveLocalFileSystemURL(filePath, (entry) => {
|
try {
|
||||||
entry.file((file) => {
|
console.log('开始处理相册图片...');
|
||||||
const reader = new plus.io.FileReader();
|
// 使用ImageUtils处理相册图片
|
||||||
reader.onloadend = (e) => {
|
const processedBase64 = await ImageUtils.processImage(filePath, {
|
||||||
this.processImage(e.target.result);
|
maxWidth: 1024,
|
||||||
};
|
maxHeight: 1024,
|
||||||
reader.readAsDataURL(file);
|
quality: 50,
|
||||||
|
outputFormat: 'base64'
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('相册图片处理完成,开始OCR识别...');
|
||||||
|
const response = await new Promise((resolve, reject) => {
|
||||||
|
uni.request({
|
||||||
|
url: '/material/app/ocr/getOcrCode',
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
image: processedBase64,
|
||||||
|
jiju_type: '',
|
||||||
|
auth_lic: 'xIWDlaDVdijcBB4mjhGCPYk5Kvk8tHZJbUn+vW+ih15+MYx98e/PXyBmKL5gFcWMPznLgDA15QuSAnZQSLddwdy9HkZgtuQDEEZZ351Eyb1eiDUccUnyoSGIrNimbx5TooBNNPYqU4qJeFrPJXAqjBHzRrxoBxuR2CEGKQPgHC4='
|
||||||
|
},
|
||||||
|
timeout: 30000,
|
||||||
|
success: resolve,
|
||||||
|
fail: reject
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// #endif
|
|
||||||
// #ifndef APP-PLUS
|
if (response.data?.data?.result) {
|
||||||
this.processImage(filePath);
|
this.queryCodeParams.maCode = response.data.data.result;
|
||||||
// #endif
|
await this.closeCamera();
|
||||||
|
this.getCode();
|
||||||
|
uni.showToast({
|
||||||
|
title: '识别成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '未识别到有效编码',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('相册图片处理或OCR识别失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '识别失败,请重试',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
this.isProcessing = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 编码检索
|
// 编码检索
|
||||||
|
|
@ -569,7 +803,6 @@ export default {
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await new Promise((resolve, reject) => {
|
const response = await new Promise((resolve, reject) => {
|
||||||
uni.request({
|
uni.request({
|
||||||
|
|
@ -586,7 +819,6 @@ export default {
|
||||||
value: option.maId,
|
value: option.maId,
|
||||||
text: option.maCode
|
text: option.maCode
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (response.data.data.length === 1) {
|
if (response.data.data.length === 1) {
|
||||||
this.codeData = response.data.data[0];
|
this.codeData = response.data.data[0];
|
||||||
}
|
}
|
||||||
|
|
@ -608,7 +840,6 @@ export default {
|
||||||
// 标签改变
|
// 标签改变
|
||||||
async changeTag() {
|
async changeTag() {
|
||||||
if (!this.queryCodeParams.maId) return;
|
if (!this.queryCodeParams.maId) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await new Promise((resolve, reject) => {
|
const response = await new Promise((resolve, reject) => {
|
||||||
uni.request({
|
uni.request({
|
||||||
|
|
@ -640,39 +871,29 @@ export default {
|
||||||
// 清理资源
|
// 清理资源
|
||||||
cleanup() {
|
cleanup() {
|
||||||
console.log('清理资源...');
|
console.log('清理资源...');
|
||||||
|
|
||||||
// 清除超时
|
// 清除超时
|
||||||
if (this.deviceReadyTimeout) {
|
if (this.deviceReadyTimeout) {
|
||||||
clearTimeout(this.deviceReadyTimeout);
|
clearTimeout(this.deviceReadyTimeout);
|
||||||
this.deviceReadyTimeout = null;
|
this.deviceReadyTimeout = null;
|
||||||
}
|
}
|
||||||
|
if (this.focusTimeout) {
|
||||||
|
clearTimeout(this.focusTimeout);
|
||||||
|
this.focusTimeout = null;
|
||||||
|
}
|
||||||
// 移除事件监听
|
// 移除事件监听
|
||||||
document.removeEventListener('deviceready', this.onDeviceReady);
|
document.removeEventListener('deviceready', this.onDeviceReady);
|
||||||
|
|
||||||
// 停止相机
|
// 停止相机
|
||||||
if (this.cameraStarted) {
|
if (this.cameraStarted) {
|
||||||
this.stopCamera().catch(console.error);
|
this.stopCamera().catch(console.error);
|
||||||
}
|
}
|
||||||
|
// 重置聚焦状态
|
||||||
|
this.resetFocusState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.camera-preview-container {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 1; /* 低于其他UI元素 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 确保其他UI元素的z-index高于相机预览 */
|
|
||||||
.top-tip, .viewfinder-container, .bottom-controls {
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
.page-container {
|
.page-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
|
@ -719,8 +940,54 @@ export default {
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background-color: rgba(0, 0, 0, 0.8); /* 添加半透明背景 */
|
background-color: transparent; /* 改为透明,让相机预览显示 */
|
||||||
z-index: 99999; /* 提高 z-index */
|
z-index: 99999;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 遮罩层样式 */
|
||||||
|
.camera-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-top {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(50% - 300rpx); /* 取景框上方的遮罩 */
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-middle {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 600rpx; /* 取景框的高度 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-left {
|
||||||
|
width: calc(50% - 250rpx); /* 取景框左侧的遮罩 */
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-transparent {
|
||||||
|
width: 500rpx; /* 取景框的宽度 */
|
||||||
|
height: 100%;
|
||||||
|
background-color: transparent; /* 透明区域,显示相机预览 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-right {
|
||||||
|
width: calc(50% - 250rpx); /* 取景框右侧的遮罩 */
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-bottom {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(50% - 300rpx); /* 取景框下方的遮罩 */
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-tip {
|
.top-tip {
|
||||||
|
|
@ -742,6 +1009,25 @@ export default {
|
||||||
border-radius: 10rpx;
|
border-radius: 10rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 聚焦状态提示 */
|
||||||
|
.focus-tip {
|
||||||
|
position: absolute;
|
||||||
|
top: 160rpx;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 10;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.focus-text {
|
||||||
|
background-color: rgba(75, 142, 255, 0.9);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 15rpx 25rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.viewfinder-container {
|
.viewfinder-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
|
|
@ -754,15 +1040,43 @@ export default {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 500rpx;
|
width: 500rpx;
|
||||||
height: 600rpx;
|
height: 600rpx;
|
||||||
border: 4rpx solid rgba(255, 255, 255, 0.5);
|
background-color: transparent; /* 透明背景 */
|
||||||
|
border: none; /* 移除边框 */
|
||||||
border-radius: 20rpx;
|
border-radius: 20rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 扫描线动画 */
|
||||||
|
.scan-line {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 4rpx;
|
||||||
|
background: linear-gradient(90deg, transparent, #4b8eff, transparent);
|
||||||
|
animation: scan 2s linear infinite;
|
||||||
|
border-radius: 2rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes scan {
|
||||||
|
0% {
|
||||||
|
top: 0;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
top: calc(100% - 4rpx);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.corner {
|
.corner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 60rpx;
|
width: 60rpx;
|
||||||
height: 60rpx;
|
height: 60rpx;
|
||||||
border: 6rpx solid #4b8eff;
|
border: 6rpx solid #4b8eff;
|
||||||
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.corner-top-left {
|
.corner-top-left {
|
||||||
|
|
@ -797,6 +1111,44 @@ export default {
|
||||||
border-bottom-right-radius: 20rpx;
|
border-bottom-right-radius: 20rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 聚焦指示器 */
|
||||||
|
.focus-indicator {
|
||||||
|
position: absolute;
|
||||||
|
width: 60rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
z-index: 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.focus-ring {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 3rpx solid #4b8eff;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: transparent;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.focus-ring.focusing {
|
||||||
|
border-color: #ff9500;
|
||||||
|
animation: focusPulse 1s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.focus-ring.focused {
|
||||||
|
border-color: #00ff00;
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes focusPulse {
|
||||||
|
0%, 100% {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.3);
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.bottom-controls {
|
.bottom-controls {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 100rpx;
|
bottom: 100rpx;
|
||||||
|
|
@ -844,6 +1196,11 @@ export default {
|
||||||
background-color: rgba(75, 142, 255, 0.8);
|
background-color: rgba(75, 142, 255, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.photo-btn.disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
.photo-btn-inner {
|
.photo-btn-inner {
|
||||||
width: 100rpx;
|
width: 100rpx;
|
||||||
height: 100rpx;
|
height: 100rpx;
|
||||||
|
|
@ -856,6 +1213,10 @@ export default {
|
||||||
background-color: #4b8eff;
|
background-color: #4b8eff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.photo-btn.disabled .photo-btn-inner {
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
.loading-overlay {
|
.loading-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue