This commit is contained in:
hayu 2025-07-31 17:57:04 +08:00
parent c19c31e62a
commit a19de9187a
1 changed files with 525 additions and 164 deletions

View File

@ -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;