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

1164 lines
32 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="page-container">
<!-- 设备搜索页面 -->
<view v-if="!showCamera">
<view class="table-list-item">
<view class="scan-btn" @click="openCamera">
<text style="color: #FFF;">开始识别</text>
</view>
</view>
<view class="table-list-item">
<uni-row :gutter="24" style="display: flex; align-items: center">
<uni-col :span="6">
<text>设备编码</text>
</uni-col>
<uni-col :span="10">
<view>
<uni-easyinput v-model="queryCodeParams.maCode" placeholder="请输入内容" />
</view>
</uni-col>
<uni-col :span="6">
<view class="coding-btn" @click="getCode()">
<text style="color: #fe9a09;text-align: center;">编码检索</text>
</view>
</uni-col>
</uni-row>
<div v-if="optionList.length > 1" class="select-container">
<uni-data-select
v-model="queryCodeParams.maId"
:localdata="optionList"
@change="changeTag"
placeholder="请选择相关联编号"
class="data-select"
:searchable="false"
></uni-data-select>
</div>
</view>
<view class="table-list-item">
<uni-forms :model="codeData" label-width="100" :border="true">
<uni-forms-item label="物资名称:" name="materialType">
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.materialType }}</text>
</uni-forms-item>
<uni-forms-item label="物资类型:" name="materialName">
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.materialName }}</text>
</uni-forms-item>
<uni-forms-item label="规格型号:" name="materialModel">
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.materialModel }}</text>
</uni-forms-item>
<uni-forms-item label="设备编码:" name="maCode">
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.maCode }}</text>
</uni-forms-item>
<uni-forms-item label="检验结论:" name="statusName">
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.statusName }}</text>
</uni-forms-item>
<uni-forms-item label="本次检修时间:" name="inspectionTime">
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.inspectionTime }}</text>
</uni-forms-item>
<uni-forms-item label="下次检修时间:" name="nextInspectionTime">
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.nextInspectionTime }}</text>
</uni-forms-item>
<uni-forms-item label="检修员:" name="inspectionPerson">
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.inspectionPerson }}</text>
</uni-forms-item>
<uni-forms-item label="检验员:" name="inspectionVerifier">
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.inspectionVerifier }}</text>
</uni-forms-item>
<uni-forms-item label="联系电话:" name="contactNumber">
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.contactNumber }}</text>
</uni-forms-item>
<uni-forms-item label="领料工程:" name="materialProject">
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.materialProject }}</text>
</uni-forms-item>
<uni-forms-item label="领料单位:" name="materialUnit">
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.materialUnit }}</text>
</uni-forms-item>
</uni-forms>
</view>
</view>
<!-- 相机预览页面 -->
<view v-if="showCamera" class="camera-container">
<!-- 底部控制区 -->
<view class="bottom-controls">
<view class="control-btn" @click="closeCamera">
<text class="control-icon">✕</text>
</view>
<view class="photo-btn" @click="takePicture" :class="{ 'taking': isTaking, 'disabled': isFocusing }">
<view class="photo-btn-inner"></view>
</view>
<view class="control-btn" @click="openGallery">
<text class="control-icon">📷</text>
</view>
</view>
<!-- 加载提示 -->
<view v-if="isProcessing" class="loading-overlay">
<view class="loading-content">
<view class="loading-spinner"></view>
<text class="loading-text">正在识别中...</text>
</view>
</view>
</view>
<!-- 隐藏的canvas用于图片处理 -->
<canvas
canvas-id="imageCanvas"
style="position: fixed; top: -9999px; left: -9999px; width: 1px; height: 1px;"
></canvas>
</view>
</template>
<script>
import ImageUtils from "../../services/utils/imageUtils";
export default {
data() {
return {
showCamera: false,
isTaking: false,
isProcessing: false,
cameraStarted: false,
queryParams: {},
queryCodeParams: {
maCode: "",
maId: ""
},
codeData: {},
optionList: [],
cameraReady: false,
deviceReadyTimeout: null,
pluginCheckRetries: 0,
maxRetries: 10,
// 聚焦相关状态
isFocusing: false,
focusSuccess: false,
showFocusIndicator: false,
focusIndicatorStyle: {},
autoFocusEnabled: true,
focusTimeout: null,
// 设备信息
systemInfo: null
}
},
onShow() {
this.initializeCordova();
// 获取设备信息用于相机配置
this.getDeviceInfo();
},
onHide() {
this.cleanup();
},
beforeDestroy() {
this.cleanup();
},
methods: {
// 获取设备信息
getDeviceInfo() {
try {
const systemInfo = uni.getSystemInfoSync();
console.log('设备信息:', systemInfo);
this.systemInfo = systemInfo;
} catch (error) {
console.error('获取设备信息失败:', error);
}
},
// 设置全屏模式仅Android
setFullscreenMode(enable) {
try {
// #ifdef APP-PLUS
if (this.systemInfo && this.systemInfo.platform === 'android') {
if (enable) {
// 隐藏状态栏和导航栏
plus.navigator.setFullscreen(true);
} else {
// 恢复状态栏和导航栏
plus.navigator.setFullscreen(false);
}
}
// #endif
} catch (error) {
console.error('设置全屏模式失败:', error);
}
},
// 初始化Cordova - 改进版本
initializeCordova() {
console.log('开始初始化Cordova...');
this.pluginCheckRetries = 0;
// 清除之前的超时
if (this.deviceReadyTimeout) {
clearTimeout(this.deviceReadyTimeout);
}
// 如果Cordova已经准备好直接检查插件
if (this.isCordovaReady()) {
this.onDeviceReady();
return;
}
// 监听deviceready事件
document.addEventListener('deviceready', this.onDeviceReady, false);
// 设置超时,防止无限等待
this.deviceReadyTimeout = setTimeout(() => {
console.warn('Cordova初始化超时尝试直接检查插件');
this.checkCameraPlugin();
}, 5000);
},
// 检查Cordova是否准备好
isCordovaReady() {
return !!(window.cordova && (
document.readyState === 'complete' ||
window.cordova.platformId
));
},
// 设备准备就绪
onDeviceReady() {
console.log('Cordova设备准备就绪');
// 清除超时
if (this.deviceReadyTimeout) {
clearTimeout(this.deviceReadyTimeout);
this.deviceReadyTimeout = null;
}
// 移除事件监听器,避免重复调用
document.removeEventListener('deviceready', this.onDeviceReady);
// 检查相机插件
this.checkCameraPlugin();
},
// 检查相机插件 - 改进版本
checkCameraPlugin() {
console.log(`检查相机插件... (尝试 ${this.pluginCheckRetries + 1}/${this.maxRetries})`);
// 按优先级检查插件可用性
const pluginAvailable = this.getCameraPlugin();
if (pluginAvailable) {
console.log('相机插件可用:', pluginAvailable);
this.cameraReady = true;
return;
}
this.pluginCheckRetries++;
if (this.pluginCheckRetries < this.maxRetries) {
// 递增延迟重试
const delay = Math.min(1000 * this.pluginCheckRetries, 5000);
console.log(`插件未就绪,${delay}ms后重试...`);
setTimeout(() => {
this.checkCameraPlugin();
}, delay);
} else {
console.error('相机插件检查失败,已达到最大重试次数');
this.cameraReady = false;
uni.showToast({
title: '相机插件初始化失败',
icon: 'none',
duration: 3000
});
}
},
// 获取相机插件引用 - 改进版本
getCameraPlugin() {
// 按照插件注册的实际路径检查
const possiblePaths = [
() => window.CameraPreview, // 全局注册的路径
() => window.cordova?.plugins?.CameraPreview,
() => window.plugins?.CameraPreview,
() => cordova?.plugins?.CameraPreview
];
for (let getPlugin of possiblePaths) {
try {
const plugin = getPlugin();
if (plugin && typeof plugin.startCamera === 'function') {
console.log('找到相机插件:', plugin);
return plugin;
}
} catch (e) {
// 忽略访问错误,继续尝试下一个路径
}
}
return null;
},
// 打开相机 - 改进版本
async openCamera() {
console.log('尝试打开相机...');
if (!this.cameraReady) {
// 再次尝试检查插件
this.checkCameraPlugin();
if (!this.cameraReady) {
uni.showModal({
title: '提示',
content: '相机插件未准备好,请确保应用已正确安装相机插件,或尝试重启应用',
showCancel: false
});
return;
}
}
try {
// 显示相机界面
this.showCamera = true;
// 等待UI更新
await this.$nextTick();
// 初始化相机
await this.initCamera();
} catch (error) {
console.error('打开相机失败:', error);
this.showCamera = false;
uni.showToast({
title: error.message || '打开相机失败',
icon: 'none',
duration: 2000
});
}
},
// 初始化相机 - 改进版本,添加聚焦支持
async initCamera() {
return new Promise((resolve, reject) => {
console.log('初始化相机预览...');
const CameraPreview = this.getCameraPlugin();
if (!CameraPreview) {
reject(new Error('相机插件不可用'));
return;
}
// 获取屏幕尺寸和状态栏高度
const systemInfo = this.systemInfo || uni.getSystemInfoSync();
const screenWidth = systemInfo.screenWidth;
const screenHeight = systemInfo.screenHeight;
const statusBarHeight = systemInfo.statusBarHeight || 0;
// 相机预览配置 - 始终使用全屏背景模式
let cameraY = 0;
let cameraHeight = screenHeight;
let toBack = true; // 设置为背景层让UI元素显示在上方
console.log('屏幕信息:', {
platform: systemInfo.platform,
screenWidth,
screenHeight,
statusBarHeight,
cameraY,
cameraHeight,
toBack
});
const options = {
x: 0,
y: cameraY,
width: screenWidth,
height: cameraHeight,
camera: CameraPreview.CAMERA_DIRECTION?.BACK || 'back',
tapPhoto: false,
previewDrag: false,
toBack: toBack, // 根据平台动态设置
alpha: 1,
tapFocus: true, // 启用点击聚焦
disableExifHeaderStripping: false
};
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}`));
}
);
});
},
// 执行自动聚焦
async performAutoFocus() {
if (!this.cameraStarted || !this.autoFocusEnabled) {
return;
}
const CameraPreview = this.getCameraPlugin();
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();
},
(error) => {
console.error('自动聚焦失败:', error);
if (this.focusTimeout) {
clearTimeout(this.focusTimeout);
this.focusTimeout = null;
}
this.isFocusing = false;
resolve(); // 即使聚焦失败也继续
}
);
});
} catch (error) {
console.error('聚焦过程出错:', error);
this.isFocusing = false;
}
},
// 关闭相机
async closeCamera() {
console.log('关闭相机...');
try {
await this.stopCamera();
} catch (error) {
console.error('关闭相机出错:', error);
} finally {
this.showCamera = 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;
}
},
// 停止相机
async stopCamera() {
if (!this.cameraStarted) {
return Promise.resolve();
}
const CameraPreview = this.getCameraPlugin();
if (!CameraPreview) {
return Promise.resolve();
}
return new Promise((resolve) => {
CameraPreview.stopCamera(
() => {
console.log('相机已停止');
this.cameraStarted = false;
resolve();
},
(error) => {
console.error('停止相机失败:', error);
this.cameraStarted = false;
resolve(); // 即使失败也继续
}
);
});
},
// 拍照 - 添加聚焦逻辑
async takePicture() {
if (!this.cameraStarted || this.isTaking || this.isFocusing) {
if (this.isFocusing) {
uni.showToast({
title: '正在聚焦,请稍候...',
icon: 'none',
duration: 1000
});
}
return;
}
console.log('开始拍照流程...');
this.isTaking = true;
try {
const CameraPreview = this.getCameraPlugin();
if (!CameraPreview) {
throw new Error('相机插件不可用');
}
// 拍照前先进行聚焦
console.log('拍照前聚焦...');
await this.performFocusBeforeCapture();
console.log('开始拍照...');
const imageData = await new Promise((resolve, reject)=>
{
const options = {
quality: 85,
targetHeight: 1920,
targetWidth: 1080
};
CameraPreview.takePicture(
options,
(imageData) => {
console.log('拍照成功');
resolve(imageData);
},
(error) => {
console.error('拍照失败:', error);
reject(new Error(`拍照失败: ${error}`));
}
);
}
)
;
await this.processImage(imageData);
} catch (error) {
console.error('拍照过程出错:', error);
uni.showToast({
title: error.message || '拍照失败',
icon: 'none'
});
} finally {
this.isTaking = false;
}
},
// 拍照前聚焦
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) {
this.isProcessing = true;
try {
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)=>
{
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
});
}
)
;
if (response.data?.data?.result) {
this.queryCodeParams.maCode = response.data.data.result;
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;
}
},
// 打开相册
openGallery() {
uni.chooseImage({
count: 1,
sourceType: ['album'],
success: (res) => {
if (res.tempFilePaths && res.tempFilePaths.length > 0) {
this.convertImageToBase64(res.tempFilePaths[0]);
}
},
fail: (error) => {
console.error('选择图片失败:', error);
uni.showToast({
title: '选择图片失败',
icon: 'none'
});
}
});
},
// 将图片转换为base64 - 使用ImageUtils处理
async convertImageToBase64(filePath) {
this.isProcessing = true;
try {
console.log('开始处理相册图片...');
// 使用ImageUtils处理相册图片
const processedBase64 = await ImageUtils.processImage(filePath, {
maxWidth: 1024,
maxHeight: 1024,
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
});
}
)
;
if (response.data?.data?.result) {
this.queryCodeParams.maCode = response.data.data.result;
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;
}
},
// 编码检索
async getCode() {
if (!this.queryCodeParams.maCode.trim()) {
uni.showToast({
title: '请输入设备编码',
icon: 'none'
});
return;
}
try {
const response = await new Promise((resolve, reject)=>
{
uni.request({
url: '/material/ma_machine/getHisByCode',
method: 'GET',
data: {maCode: this.queryCodeParams.maCode},
success: resolve,
fail: reject
});
}
)
;
if (response.data?.data && response.data.data.length > 0) {
this.optionList = response.data.data.map(option => ({
value: option.maId,
text: option.maCode
}));
if (response.data.data.length === 1) {
this.codeData = response.data.data[0];
}
} else {
uni.showToast({
title: '未查询到该编号信息',
icon: 'none'
});
}
} catch (error) {
console.error('查询失败:', error);
uni.showToast({
title: '查询失败,请重试',
icon: 'none'
});
}
},
// 标签改变
async changeTag() {
if (!this.queryCodeParams.maId) return;
try {
const response = await new Promise((resolve, reject)=>
{
uni.request({
url: '/material/ma_machine/getHisByCode',
method: 'GET',
data: {maId: this.queryCodeParams.maId},
success: resolve,
fail: reject
});
}
)
;
if (response.data?.data && response.data.data.length > 0) {
this.codeData = response.data.data[0];
} else {
uni.showToast({
title: '获取编号信息失败',
icon: 'none'
});
}
} catch (error) {
console.error("获取编号信息失败", error);
uni.showToast({
title: '获取信息失败',
icon: 'none'
});
}
},
// 清理资源
cleanup() {
console.log('清理资源...');
// 清除超时
if (this.deviceReadyTimeout) {
clearTimeout(this.deviceReadyTimeout);
this.deviceReadyTimeout = null;
}
if (this.focusTimeout) {
clearTimeout(this.focusTimeout);
this.focusTimeout = null;
}
// 移除事件监听
document.removeEventListener('deviceready', this.onDeviceReady);
// 停止相机
if (this.cameraStarted) {
this.stopCamera().catch(console.error);
}
// 重置聚焦状态
this.resetFocusState();
}
}
}
</script>
<style>
.page-container {
display: flex;
height: auto;
flex-direction: column;
background-color: #f7f8fa;
padding: 24rpx;
}
.table-list-item {
background: #fff;
padding: 32rpx;
border-radius: 20rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
margin-bottom: 24rpx;
}
.scan-btn {
height: 88rpx;
background: #4b8eff;
display: flex;
justify-content: center;
align-items: center;
border-radius: 12rpx;
font-size: 32rpx;
font-weight: 600;
box-shadow: 0 6rpx 20rpx rgba(55, 132, 251, 0.2);
}
.coding-btn {
padding: 10rpx 0;
color: #fe9a09;
background-color: #fff7eb;
border: 1px solid #fe9a09;
border-radius: 12rpx;
text-align: center;
font-size: 28rpx;
font-weight: 600;
}
/* 相机预览样式 */
.camera-container {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: #000000;
z-index: 1000; /* 降低层级让UI元素显示在上方 */
}
.top-tip {
position: absolute;
top: 120rpx;
left: 50%;
transform: translateX(-50%);
z-index: 10001; /* 提高层级,确保显示在相机之上 */
width: 100%;
text-align: center;
}
.tip-text {
background-color: rgba(0, 0, 0, 0.5);
color: #fff;
font-size: 32rpx;
font-weight: bold;
padding: 20rpx 30rpx;
border-radius: 10rpx;
}
/* 聚焦状态提示 */
.focus-tip {
position: absolute;
top: 200rpx;
left: 50%;
transform: translateX(-50%);
z-index: 10001; /* 提高层级 */
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 {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 10001; /* 提高取景框层级 */
}
.viewfinder {
position: relative;
width: 500rpx;
height: 600rpx;
background-color: transparent;
border: 2rpx solid rgba(255, 255, 255, 0.5);
border-radius: 20rpx;
box-shadow: 0 0 20rpx rgba(75, 142, 255, 0.5);
}
/* 扫描线动画 */
.scan-line {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 4rpx;
background: linear-gradient(90deg, transparent, rgba(75, 142, 255, 0.8), 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 {
position: absolute;
width: 60rpx;
height: 60rpx;
border: 6rpx solid rgba(75, 142, 255, 0.8);
background-color: transparent;
}
.corner-top-left {
top: -6rpx;
left: -6rpx;
border-right: none;
border-bottom: none;
border-top-left-radius: 20rpx;
}
.corner-top-right {
top: -6rpx;
right: -6rpx;
border-left: none;
border-bottom: none;
border-top-right-radius: 20rpx;
}
.corner-bottom-left {
bottom: -6rpx;
left: -6rpx;
border-right: none;
border-top: none;
border-bottom-left-radius: 20rpx;
}
.corner-bottom-right {
bottom: -6rpx;
right: -6rpx;
border-left: none;
border-top: none;
border-bottom-right-radius: 20rpx;
}
/* 聚焦指示器 */
.focus-indicator {
position: absolute;
width: 60rpx;
height: 60rpx;
z-index: 10002; /* 聚焦指示器最高层级 */
}
.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 {
position: absolute;
bottom: 100rpx;
left: 0;
right: 0;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 0 100rpx;
z-index: 10001; /* 提高底部控制按钮层级 */
}
.control-btn {
width: 100rpx;
height: 100rpx;
background-color: rgba(0, 0, 0, 0.3);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
border: 2rpx solid rgba(255, 255, 255, 0.3);
}
.control-icon {
color: #fff;
font-size: 40rpx;
font-weight: bold;
}
.photo-btn {
width: 140rpx;
height: 140rpx;
background-color: rgba(0, 0, 0, 0.3);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
border: 6rpx solid rgba(255, 255, 255, 0.5);
transition: all 0.2s ease;
}
.photo-btn.taking {
transform: scale(0.9);
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 {
width: 100rpx;
height: 100rpx;
background-color: rgba(255, 255, 255, 0.9);
border-radius: 50%;
transition: all 0.2s ease;
}
.photo-btn.taking .photo-btn-inner {
background-color: #4b8eff;
}
.photo-btn.disabled .photo-btn-inner {
background-color: #ccc;
}
.loading-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 10003; /* 加载层级最高 */
}
.loading-content {
display: flex;
flex-direction: column;
align-items: center;
background-color: rgba(255, 255, 255, 0.9);
padding: 60rpx;
border-radius: 20rpx;
}
.loading-spinner {
width: 60rpx;
height: 60rpx;
border: 6rpx solid #f3f3f3;
border-top: 6rpx solid #4b8eff;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 30rpx;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.loading-text {
color: #333;
font-size: 32rpx;
font-weight: bold;
}
</style>