This commit is contained in:
parent
08db9fe437
commit
529f04c591
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<view class="accept page-common">
|
||||
<uni-row :gutter="24" class="search-form">
|
||||
<uni-col :span="16">
|
||||
<uni-col :span="14">
|
||||
<view><uni-easyinput placeholder="请输入内容" v-model="searchValue"/></view>
|
||||
</uni-col>
|
||||
<uni-col :span="4">
|
||||
|
|
@ -9,7 +9,10 @@
|
|||
</uni-col>
|
||||
<uni-col :span="4">
|
||||
<view class="addBtn" @click="scanStart">扫码</view>
|
||||
</uni-col>
|
||||
</uni-col>
|
||||
<uni-col :span="4">
|
||||
<view class="addBtn" @click="openCamera">OCR</view>
|
||||
</uni-col>
|
||||
</uni-row>
|
||||
<div class="card" style="margin-top: 10px;">
|
||||
<uni-forms :model="boxInfo" label-width="200rpx" :border="true">
|
||||
|
|
@ -98,15 +101,54 @@
|
|||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
|
||||
<!-- 相机预览页面 -->
|
||||
<view v-if="showCamera" class="camera-container">
|
||||
<!-- 顶部提示 -->
|
||||
<view class="top-tip" style="color: red; text-align: center;margin-top: 30px;">
|
||||
<text class="tip-text">请将识别编码置于取景框内,完成扫描</text>
|
||||
</view>
|
||||
<!-- 取景框 -->
|
||||
<view class="viewfinder-container">
|
||||
</view>
|
||||
|
||||
<!-- 底部控制区 -->
|
||||
<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>
|
||||
<view class="custom-toast"
|
||||
:class="{'show': isShow, 'top': position === 'top', 'bottom': position === 'bottom'}"
|
||||
ref="toast">
|
||||
{{ message }}
|
||||
</view>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { onLoad,onShow } from '@dcloudio/uni-app'
|
||||
import {onHide, onLoad, onShow, onBackPress} from '@dcloudio/uni-app'
|
||||
import { getAppBoxDetailListApi,appBindMaCodeApi,appUnbindMaCodeApi,getTransferKeepsListApi,appTransferApi } from '../../services/standard.js';
|
||||
import { getMaCodeByQrCodeApi } from '@/services/index.js';
|
||||
import { baseURL } from '@/utils/http'
|
||||
import ScanQrCode from '@/pages/devicesSearch/ScanQrCode.vue'
|
||||
import {decryptWithSM4} from "../../utils/sm";
|
||||
const scanQrCodeRef = ref(null)
|
||||
const keeperPopup = ref(null);
|
||||
const searchValue = ref('')
|
||||
|
|
@ -115,7 +157,24 @@ const codeList = ref([])
|
|||
const keeperList = ref([])
|
||||
const transferUser = ref("")
|
||||
const imgBeseUrl = ref("")
|
||||
const bmFileInfos = ref([])//请求图片参数
|
||||
const bmFileInfos = ref([])//请求图片参数
|
||||
|
||||
// 相机相关状态
|
||||
const showCamera = ref(false)
|
||||
const isTaking = ref(false)
|
||||
const isProcessing = ref(false)
|
||||
const cameraStarted = ref(false)
|
||||
const isFocusing = ref(false)
|
||||
const cameraReady = ref(false)
|
||||
const isShow = ref(false)
|
||||
const message = ref('')
|
||||
const position = ref('bottom')
|
||||
const deviceReadyTimeout = ref(null)
|
||||
const pluginCheckRetries = ref(0)
|
||||
const maxRetries = ref(5)
|
||||
const focusTimeout = ref(null)
|
||||
const systemInfo = ref(null)
|
||||
|
||||
const getCodeList = () => {
|
||||
console.log(boxInfo.value)
|
||||
let param = {
|
||||
|
|
@ -181,7 +240,7 @@ const handleScanSuccess = (result) => {
|
|||
} else {
|
||||
// 原代码中 split 处理逻辑
|
||||
const actualQrCode = qrCode.split("qrcode=")[1] || qrCode;
|
||||
bindMa(actualQrCode)
|
||||
bindMa(actualQrCode,1)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -191,13 +250,486 @@ const handleScanError = (error) => {
|
|||
uni.showToast({ title: error.message, icon: 'none' })
|
||||
}
|
||||
|
||||
//绑定接口
|
||||
const bindMa = (qrCode) => {
|
||||
console.log(qrCode)
|
||||
let param = {
|
||||
"qrCode":qrCode,
|
||||
"boxId":boxInfo.value.boxId,
|
||||
// 相机相关方法
|
||||
const showToast = (message, position = 'top', duration = 2000) => {
|
||||
message.value = message
|
||||
position.value = position
|
||||
isShow.value = true
|
||||
setTimeout(() => {
|
||||
isShow.value = false
|
||||
}, duration)
|
||||
}
|
||||
|
||||
// 初始化Cordova - 改进版本
|
||||
const initializeCordova = () => {
|
||||
console.log('开始初始化Cordova...');
|
||||
pluginCheckRetries.value = 0;
|
||||
// 清除之前的超时
|
||||
if (deviceReadyTimeout.value) {
|
||||
clearTimeout(deviceReadyTimeout.value);
|
||||
}
|
||||
// 如果Cordova已经准备好,直接检查插件
|
||||
if (isCordovaReady()) {
|
||||
onDeviceReady();
|
||||
return;
|
||||
}
|
||||
// 监听deviceready事件
|
||||
document.addEventListener('deviceready', onDeviceReady, false);
|
||||
// 设置超时,防止无限等待
|
||||
deviceReadyTimeout.value = setTimeout(() => {
|
||||
console.warn('Cordova初始化超时,尝试直接检查插件');
|
||||
checkCameraPlugin();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// 检查Cordova是否准备好
|
||||
const isCordovaReady = () => {
|
||||
return !!(window.cordova && (
|
||||
document.readyState === 'complete' ||
|
||||
window.cordova.platformId
|
||||
));
|
||||
}
|
||||
|
||||
// 设备准备就绪
|
||||
const onDeviceReady = () => {
|
||||
console.log('Cordova设备准备就绪');
|
||||
document.removeEventListener('deviceready', onDeviceReady);
|
||||
checkCameraPlugin();
|
||||
}
|
||||
|
||||
// 检查相机插件 - 改进版本
|
||||
const checkCameraPlugin = () => {
|
||||
console.log(`检查相机插件... (尝试 ${pluginCheckRetries.value + 1}/${maxRetries.value})`);
|
||||
// 按优先级检查插件可用性
|
||||
const pluginAvailable = getCameraPlugin();
|
||||
if (pluginAvailable) {
|
||||
console.log('相机插件可用:', pluginAvailable);
|
||||
cameraReady.value = true;
|
||||
return;
|
||||
}
|
||||
pluginCheckRetries.value++;
|
||||
if (pluginCheckRetries.value < maxRetries.value) {
|
||||
// 递增延迟重试
|
||||
const delay = Math.min(1000 * pluginCheckRetries.value, 5000);
|
||||
console.log(`插件未就绪,${delay}ms后重试...`);
|
||||
setTimeout(() => {
|
||||
checkCameraPlugin();
|
||||
}, delay);
|
||||
} else {
|
||||
console.error('相机插件检查失败,已达到最大重试次数');
|
||||
cameraReady.value = false;
|
||||
uni.showToast({
|
||||
title: '相机插件初始化失败',
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取相机插件引用
|
||||
const getCameraPlugin = () => {
|
||||
const possiblePaths = [
|
||||
() => window.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;
|
||||
}
|
||||
|
||||
// 获取设备信息
|
||||
const getDeviceInfo = () => {
|
||||
try {
|
||||
const systemInfoData = uni.getSystemInfoSync();
|
||||
console.log('设备信息:', systemInfoData);
|
||||
systemInfo.value = systemInfoData;
|
||||
} catch (error) {
|
||||
console.error('获取设备信息失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 清理资源
|
||||
const cleanup = () => {
|
||||
console.log('清理资源...');
|
||||
if (cameraStarted.value) {
|
||||
const CameraPreview = getCameraPlugin();
|
||||
if (CameraPreview) {
|
||||
CameraPreview.stopCamera();
|
||||
}
|
||||
cameraStarted.value = false;
|
||||
}
|
||||
|
||||
// 清除超时
|
||||
if (deviceReadyTimeout.value) {
|
||||
clearTimeout(deviceReadyTimeout.value);
|
||||
deviceReadyTimeout.value = null;
|
||||
}
|
||||
if (focusTimeout.value) {
|
||||
clearTimeout(focusTimeout.value);
|
||||
focusTimeout.value = null;
|
||||
}
|
||||
// 移除事件监听
|
||||
document.removeEventListener('deviceready', onDeviceReady);
|
||||
// 停止相机
|
||||
if (cameraStarted.value) {
|
||||
stopCamera().catch(console.error);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化相机 - 关键方法
|
||||
const initCamera = async () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const CameraPreview = getCameraPlugin();
|
||||
if (!CameraPreview) {
|
||||
reject(new Error('相机插件不可用'));
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取整个相机包装器的尺寸
|
||||
const cameraWrapper = document.querySelector('.viewfinder-container');
|
||||
if (!cameraWrapper) {
|
||||
reject(new Error('找不到相机容器'));
|
||||
return;
|
||||
}
|
||||
|
||||
const rect = cameraWrapper.getBoundingClientRect();
|
||||
const options = {
|
||||
x: rect.left,
|
||||
y: rect.top,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
camera: CameraPreview.CAMERA_DIRECTION?.BACK || 'back',
|
||||
tapPhoto: false,
|
||||
previewDrag: false,
|
||||
toBack: false, // 确保相机在WebView上层
|
||||
alpha: 1,
|
||||
tapFocus: true,
|
||||
disableExifHeaderStripping: false
|
||||
};
|
||||
console.log('相机配置:', options);
|
||||
CameraPreview.startCamera(
|
||||
options,
|
||||
(result) => {
|
||||
console.log('相机启动成功:', result);
|
||||
cameraStarted.value = true;
|
||||
resolve();
|
||||
},
|
||||
(error) => {
|
||||
console.error('相机启动失败:', error);
|
||||
cameraStarted.value = false;
|
||||
reject(new Error(`相机启动失败: ${error}`));
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 打开相机 - 修改版本
|
||||
const openCamera = async () => {
|
||||
console.log('尝试打开相机...');
|
||||
if (!cameraReady.value) {
|
||||
checkCameraPlugin();
|
||||
if (!cameraReady.value) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '相机插件未准备好,请确保应用已正确安装相机插件,或尝试重启应用',
|
||||
showCancel: false
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
// 显示相机界面
|
||||
showCamera.value = true;
|
||||
// 等待UI更新
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
// 初始化相机
|
||||
await initCamera();
|
||||
} catch (error) {
|
||||
console.error('打开相机失败:', error);
|
||||
showCamera.value = false;
|
||||
uni.showToast({
|
||||
title: error.message || '打开相机失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭相机
|
||||
const closeCamera = async () => {
|
||||
console.log('关闭相机...');
|
||||
try {
|
||||
await stopCamera();
|
||||
} catch (error) {
|
||||
console.error('关闭相机出错:', error);
|
||||
} finally {
|
||||
showCamera.value = false;
|
||||
cameraStarted.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 停止相机
|
||||
const stopCamera = async () => {
|
||||
if (!cameraStarted.value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
const CameraPreview = getCameraPlugin();
|
||||
if (!CameraPreview) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
CameraPreview.stopCamera(
|
||||
() => {
|
||||
console.log('相机已停止');
|
||||
cameraStarted.value = false;
|
||||
resolve();
|
||||
},
|
||||
(error) => {
|
||||
console.error('停止相机失败:', error);
|
||||
cameraStarted.value = false;
|
||||
resolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// 拍照
|
||||
const takePicture = async () => {
|
||||
if (!cameraStarted.value || isTaking.value || isFocusing.value) {
|
||||
if (isFocusing.value) {
|
||||
uni.showToast({
|
||||
title: '正在聚焦,请稍候...',
|
||||
icon: 'none',
|
||||
duration: 1000
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('开始拍照流程...');
|
||||
isTaking.value = true;
|
||||
|
||||
try {
|
||||
const CameraPreview = getCameraPlugin();
|
||||
if (!CameraPreview) {
|
||||
throw new Error('相机插件不可用');
|
||||
}
|
||||
CameraPreview.takePicture({width:640, height:540, quality: 50}, async (base64PictureData) => {
|
||||
console.log('拍照返回数据',base64PictureData);
|
||||
await processImage(base64PictureData);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('拍照过程出错:', error);
|
||||
uni.showToast({
|
||||
title: error.message || '拍照失败',
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
isTaking.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理图片
|
||||
const processImage = async (imageData) => {
|
||||
isProcessing.value = true;
|
||||
try {
|
||||
const cleanBase64 = await removeExifData(imageData);
|
||||
console.log('清理后的纯base64:', cleanBase64);
|
||||
console.log('开始OCR识别...');
|
||||
|
||||
uni.request({
|
||||
url: '/material/app/ocr/getOcrCode',
|
||||
method: 'POST',
|
||||
data: {
|
||||
image: cleanBase64,
|
||||
jiju_type: '',
|
||||
auth_lic: 'xIWDlaDVdijcBB4mjhGCPYk5Kvk8tHZJbUn+vW+ih15+MYx98e/PXyBmKL5gFcWMPznLgDA15QuSAnZQSLddwdy9HkZgtuQDEEZZ351Eyb1eiDUccUnyoSGIrNimbx5TooBNNPYqU4qJeFrPJXAqjBHzRrxoBxuR2CEGKQPgHC4='
|
||||
},
|
||||
header: {
|
||||
"Content-Type": "application/json;charset=UTF-8"
|
||||
},
|
||||
success: async (res) => {
|
||||
if (!res.data.code){
|
||||
res=JSON.parse(decryptWithSM4(res.data))
|
||||
} else {
|
||||
res =res.data
|
||||
}
|
||||
const {data: resData} = res
|
||||
if (resData.code===0){
|
||||
if (resData.data.result) {
|
||||
// 识别成功,将结果填入输入框并自动查询
|
||||
// keyWord.value = resData.data.result;
|
||||
await closeCamera();
|
||||
// 自动触发查询
|
||||
bindMa(resData.data.result,2)
|
||||
uni.showToast({
|
||||
title: '识别成功',
|
||||
icon: 'success'
|
||||
});
|
||||
} else {
|
||||
console.log('识别失败!' + resData.data.msg)
|
||||
showToast('识别失败!', 'bottom')
|
||||
}
|
||||
} else {
|
||||
showToast('识别失败!', 'bottom')
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
showToast('请求失败!', 'bottom')
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('图片处理或OCR识别失败:', error);
|
||||
uni.showToast({
|
||||
title: '识别失败,请重试',
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
isProcessing.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 移除EXIF数据
|
||||
const removeExifData = (pureBase64) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
ctx.drawImage(img, 0, 0);
|
||||
|
||||
const dataUrl = canvas.toDataURL('image/jpeg', 0.8);
|
||||
const cleanPureBase64 = dataUrl.split(',')[1];
|
||||
resolve(cleanPureBase64);
|
||||
};
|
||||
|
||||
img.onerror = () => reject(new Error('图像加载失败'));
|
||||
img.src = `data:image/jpeg;base64,${pureBase64}`;
|
||||
});
|
||||
}
|
||||
|
||||
// 打开相册
|
||||
const openGallery = () => {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sourceType: ['album'],
|
||||
success: (res) => {
|
||||
if (res.tempFilePaths && res.tempFilePaths.length > 0) {
|
||||
const filePath = res.tempFilePaths[0];
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', filePath, true);
|
||||
xhr.responseType = 'blob';
|
||||
xhr.onload = () => {
|
||||
if (xhr.status === 200) {
|
||||
const blob = xhr.response;
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const base64 = e.target.result.split(',')[1];
|
||||
convertImageToBase64(base64);
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('选择图片失败:', error);
|
||||
uni.showToast({
|
||||
title: '选择图片失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 将图片转换为base64
|
||||
const convertImageToBase64 = async (processedBase64) => {
|
||||
isProcessing.value = true;
|
||||
try {
|
||||
console.log('相册base64数据', processedBase64);
|
||||
console.log('开始OCR识别...');
|
||||
|
||||
uni.request({
|
||||
url: '/material/app/ocr/getOcrCode',
|
||||
method: 'POST',
|
||||
data: {
|
||||
image: processedBase64,
|
||||
jiju_type: '',
|
||||
auth_lic: 'xIWDlaDVdijcBB4mjhGCPYk5Kvk8tHZJbUn+vW+ih15+MYx98e/PXyBmKL5gFcWMPznLgDA15QuSAnZQSLddwdy9HkZgtuQDEEZZ351Eyb1eiDUccUnyoSGIrNimbx5TooBNNPYqU4qJeFrPJXAqjBHzRrxoBxuR2CEGKQPgHC4='
|
||||
},
|
||||
header: {
|
||||
"Content-Type": "application/json;charset=UTF-8"
|
||||
},
|
||||
success: async (res) => {
|
||||
if (!res.data.code){
|
||||
res=JSON.parse(decryptWithSM4(res.data))
|
||||
} else {
|
||||
res =res.data
|
||||
}
|
||||
const {data: resData} = res
|
||||
if (resData.code===0){
|
||||
if (resData.data.result) {
|
||||
// 识别成功,将结果填入输入框并自动查询
|
||||
// keyWord.value = resData.data.result;
|
||||
await closeCamera();
|
||||
// 自动触发查询
|
||||
// handleSearch();
|
||||
bindMa(resData.data.result,2)
|
||||
uni.showToast({
|
||||
title: '识别成功',
|
||||
icon: 'success'
|
||||
});
|
||||
} else {
|
||||
showToast('识别失败!', 'bottom')
|
||||
}
|
||||
} else {
|
||||
showToast('识别失败!', 'bottom')
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
uni.showToast({
|
||||
title: '请求失败:' + err.errMsg,
|
||||
icon: 'none',
|
||||
})
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('相册图片处理或OCR识别失败:', error);
|
||||
showToast('识别失败!', 'bottom')
|
||||
} finally {
|
||||
isProcessing.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
//绑定接口
|
||||
const bindMa = (qrCode,type) => {
|
||||
console.log(qrCode)
|
||||
let param;
|
||||
if (type===1){
|
||||
param = {
|
||||
"qrCode":qrCode,
|
||||
"boxId":boxInfo.value.boxId,
|
||||
}
|
||||
} else {
|
||||
param = {
|
||||
"maCode":qrCode,
|
||||
"boxId":boxInfo.value.boxId,
|
||||
}
|
||||
}
|
||||
|
||||
console.log(param)
|
||||
appBindMaCodeApi(param).then(res => {
|
||||
console.log(res)
|
||||
|
|
@ -321,13 +853,197 @@ onLoad((options)=>{
|
|||
boxInfo.value.typeModelName="";
|
||||
console.log(boxInfo.value)
|
||||
// getCodeList()
|
||||
|
||||
initializeCordova()
|
||||
getDeviceInfo()
|
||||
})
|
||||
onShow(()=>{
|
||||
getCodeList()
|
||||
initializeCordova();
|
||||
getDeviceInfo();
|
||||
})
|
||||
|
||||
onHide(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
// 返回按钮处理
|
||||
onBackPress((options) => {
|
||||
if (showCamera.value){
|
||||
console.log('关闭相机...');
|
||||
try {
|
||||
closeCamera();
|
||||
} catch (error) {
|
||||
console.error('关闭相机出错:', error);
|
||||
} finally {
|
||||
showCamera.value = false;
|
||||
cameraStarted.value = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const back = () => {
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
/* 相机相关样式 */
|
||||
.camera-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.viewfinder-container {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 40%;
|
||||
margin-top: 150px;
|
||||
z-index: 10001;
|
||||
}
|
||||
|
||||
.bottom-controls {
|
||||
position: absolute;
|
||||
bottom: 100rpx;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 0 60rpx;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.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);
|
||||
margin: 0 20rpx;
|
||||
}
|
||||
|
||||
.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;
|
||||
margin: 0 20rpx;
|
||||
}
|
||||
|
||||
.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%;
|
||||
z-index: 100;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.custom-toast {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 12px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
color: white;
|
||||
text-align: center;
|
||||
z-index: 9999;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
.custom-toast.top { top: 0; }
|
||||
.custom-toast.bottom { bottom: 0; }
|
||||
.custom-toast.show { opacity: 1; }
|
||||
|
||||
|
||||
.accept {
|
||||
padding: 10px;
|
||||
height: 95vh;
|
||||
|
|
|
|||
Loading…
Reference in New Issue