996 lines
30 KiB
Vue
996 lines
30 KiB
Vue
<template>
|
||
<view class="page-container" v-show="!showCamera">
|
||
<!-- 设备搜索页面 -->
|
||
<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="5">
|
||
<text>设备编码</text>
|
||
</uni-col>
|
||
<uni-col :span="13">
|
||
<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="maName">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.maName }}</text>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="规格型号:" name="maModel">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.maModel }}</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="maStatus">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.maStatus }}</text>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="本次检修时间:" name="thisCheckTime">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.thisCheckTime }}</text>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="下次检修时间:" name="nextCheckTime">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.nextCheckTime }}</text>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="出入库次数:" name="inOutNum">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.inOutNum }}</text>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="初次入库:" name="inTime">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.inTime }}</text>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="服务工程次数:" name="serviceNum">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.serviceNum }}</text>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="检验次数:" name="serviceNum">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.serviceNum }}</text>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="更换配件次数:" name="checkNum">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.checkNum }}</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="scrapTime">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.scrapTime }}</text>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="生产厂家:" name="maVender">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.maVender }}</text>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="领料单位:" name="leaseUnit">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.leaseUnit }}</text>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="领料工程:" name="leaseProject">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.leaseProject }}</text>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="领料时间:" name="leaseTime">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.leaseTime }}</text>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="退料单位:" name="backUnit">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.backUnit }}</text>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="退料工程:" name="backProject">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.backProject }}</text>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="退料时间:" name="backTime">
|
||
<text style="height: 100%;display: flex;align-items: center;">{{ codeData.backTime }}</text>
|
||
</uni-forms-item>
|
||
|
||
</uni-forms>
|
||
</view>
|
||
</view>
|
||
|
||
|
||
<!-- 隐藏的canvas用于图片处理 -->
|
||
<canvas
|
||
canvas-id="imageCanvas"
|
||
style="position: fixed; top: -9999px; left: -9999px; width: 1px; height: 1px;"
|
||
></canvas>
|
||
<div style="width: 100%;">
|
||
<div v-if="isOverToday && codeData.nextCheckTime">该工器具已临近下次检验时间,请及时退还至机具(物流)分公司!</div>
|
||
<ElectronicSeal v-else v-show="codeData.maId" :maCode="codeData?.maCode" :maId="codeData?.maId" :devType="2" />
|
||
</div>
|
||
</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>
|
||
import {decryptWithSM4} from "../../utils/sm";
|
||
import {getDeviceListAPI} from "../../services/picking/outbound";
|
||
import ElectronicSeal from '@/components/ElectronicSeal/index.vue'
|
||
|
||
export default {
|
||
components: { ElectronicSeal },
|
||
data() {
|
||
return {
|
||
showCamera: false,
|
||
isTaking: false,
|
||
isProcessing: false,
|
||
cameraStarted: false,
|
||
queryParams: {},
|
||
queryCodeParams: {
|
||
maCode: "",
|
||
maId: ""
|
||
},
|
||
codeData: {},
|
||
optionList: [],
|
||
cameraReady: false,
|
||
deviceReadyTimeout: null,
|
||
pluginCheckRetries: 0,
|
||
maxRetries: 5,
|
||
// 聚焦相关状态
|
||
isFocusing: false,
|
||
focusSuccess: false,
|
||
showFocusIndicator: false,
|
||
focusIndicatorStyle: {},
|
||
autoFocusEnabled: true,
|
||
focusTimeout: null,
|
||
// 设备信息
|
||
systemInfo: null,
|
||
isOverToday: false,
|
||
isShow: false,
|
||
message: '',
|
||
position: 'bottom',
|
||
screenHeight: null,
|
||
screenWidth: null,
|
||
}
|
||
},
|
||
onBackPress(options) {
|
||
console.log("进了-----")
|
||
// 相机打开时处理返回事件
|
||
if (this.showCamera){
|
||
console.log('关闭相机...');
|
||
try {
|
||
this.stopCamera();
|
||
} catch (error) {
|
||
console.error('关闭相机出错:', error);
|
||
} finally {
|
||
this.showCamera = false;
|
||
this.cameraStarted = false;
|
||
this.resetFocusState();
|
||
}
|
||
return true; // 阻止默认返回行为
|
||
}
|
||
// 其他情况下允许默认返回
|
||
return false;
|
||
},
|
||
onShow() {
|
||
this.initializeCordova();
|
||
// 获取设备信息用于相机配置
|
||
this.getDeviceInfo();
|
||
},
|
||
onHide() {
|
||
this.cleanup();
|
||
},
|
||
beforeDestroy() {
|
||
this.cleanup();
|
||
},
|
||
methods: {
|
||
show(message, position = 'top', duration = 2000) {
|
||
this.message = message
|
||
this.position = position
|
||
this.isShow = true
|
||
setTimeout(() => {
|
||
this.isShow = false
|
||
}, duration)
|
||
},
|
||
handleIsOverToday() {
|
||
if (!this.codeData.nextCheckTime) {
|
||
this.isOverToday = true
|
||
} else {
|
||
const now = Date.now()
|
||
const nextCheckTimestamp = new Date(this.codeData.nextCheckTime).getTime()
|
||
this.isOverToday = nextCheckTimestamp <= now
|
||
console.log('🚀 ~ isOverToday ~ :', this.isOverToday)
|
||
}
|
||
},
|
||
// 获取设备信息
|
||
getDeviceInfo() {
|
||
try {
|
||
const systemInfo = uni.getSystemInfoSync();
|
||
console.log('设备信息:', systemInfo);
|
||
this.systemInfo = systemInfo;
|
||
// 获取屏幕高度和宽度
|
||
const screenHeight = systemInfo.screenHeight; // 屏幕高度:1000
|
||
const screenWidth = systemInfo.screenWidth; // 屏幕宽度:600
|
||
this.screenHeight = screenHeight;
|
||
this.screenWidth = screenWidth;
|
||
} 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 // 全局注册的路径
|
||
];
|
||
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('尝试打开相机...');
|
||
this.codeData = {}
|
||
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) => {
|
||
const CameraPreview = this.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);
|
||
this.cameraStarted = true;
|
||
resolve();
|
||
},
|
||
(error) => {
|
||
console.error('相机启动失败:', error);
|
||
this.cameraStarted = false;
|
||
reject(new Error(`相机启动失败: ${error}`));
|
||
}
|
||
);
|
||
});
|
||
},
|
||
// 点击聚焦
|
||
tapToFocus(x, y) {
|
||
const CameraPreview = this.getCameraPlugin();
|
||
if (CameraPreview && this.cameraStarted) {
|
||
CameraPreview.tapToFocus(
|
||
x,
|
||
y,
|
||
() => console.log('聚焦成功'),
|
||
(error) => console.error('聚焦失败', error)
|
||
);
|
||
}
|
||
},
|
||
|
||
// 关闭相机
|
||
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('拍照前聚焦...');
|
||
console.log('开始拍照...');
|
||
CameraPreview.takePicture({width:this.screenWidth, height:this.screenHeight, quality: 50}, async (base64PictureData) => {
|
||
console.log('拍照返回数据',base64PictureData);
|
||
await this.processImage(base64PictureData);
|
||
});
|
||
} catch (error) {
|
||
console.error('拍照过程出错:', error);
|
||
uni.showToast({
|
||
title: error.message || '拍照失败',
|
||
icon: 'none'
|
||
});
|
||
} finally {
|
||
this.isTaking = false;
|
||
}
|
||
},
|
||
|
||
// 修改后的方法
|
||
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);
|
||
|
||
// 转换为dataURL然后提取纯base64部分
|
||
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}`;
|
||
});
|
||
},
|
||
|
||
// 处理图片
|
||
async processImage(imageData) {
|
||
this.isProcessing = true;
|
||
try {
|
||
// base64PictureData 是纯base64(无前缀)
|
||
const cleanBase64 = await this.removeExifData(imageData);
|
||
// cleanBase64 也是纯base64(无前缀)
|
||
|
||
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) {
|
||
this.queryCodeParams.maCode = resData.data.result
|
||
await this.closeCamera();
|
||
await this.getCode();
|
||
await uni.showToast({
|
||
title: '识别成功',
|
||
icon: 'success'
|
||
});
|
||
} else {
|
||
console.log('识别失败!' + resData.data.msg)
|
||
this.show('识别失败!', 'bottom')
|
||
}
|
||
} else {
|
||
this.show('识别失败!', 'bottom')
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
this.$refs.toast.show('请求失败!', 'bottom')
|
||
},
|
||
})
|
||
} 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) {
|
||
const filePath = res.tempFilePaths[0];
|
||
|
||
// 统一使用XMLHttpRequest处理
|
||
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];
|
||
this.convertImageToBase64(base64);
|
||
};
|
||
reader.readAsDataURL(blob);
|
||
}
|
||
};
|
||
xhr.send();
|
||
}
|
||
},
|
||
fail: (error) => {
|
||
console.error('选择图片失败:', error);
|
||
uni.showToast({
|
||
title: '选择图片失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
});
|
||
},
|
||
// 将图片转换为base64
|
||
async convertImageToBase64(processedBase64) {
|
||
this.isProcessing = 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
|
||
}
|
||
console.log("res", res);
|
||
console.log("res.data",res.data)
|
||
const {data: resData} = res
|
||
console.log("resData",resData)
|
||
console.log("resData.data",resData.data)
|
||
console.log("resData.code",resData.code)
|
||
console.log("resData.data.result",resData.data.result)
|
||
|
||
if (resData.code===0){
|
||
if (resData.data.result) {
|
||
this.queryCodeParams.maCode = resData.data.result
|
||
await this.closeCamera();
|
||
await this.getCode();
|
||
await uni.showToast({
|
||
title: '识别成功',
|
||
icon: 'success'
|
||
});
|
||
} else {
|
||
this.show('识别失败!', 'bottom')
|
||
}
|
||
} else {
|
||
this.show('识别失败!', 'bottom')
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
uni.showToast({
|
||
title: '请求失败:' + err.errMsg,
|
||
icon: 'none',
|
||
})
|
||
},
|
||
});
|
||
} catch (error) {
|
||
console.error('相册图片处理或OCR识别失败:', error);
|
||
this.show('识别失败!', 'bottom')
|
||
} finally {
|
||
this.isProcessing = false;
|
||
}
|
||
},
|
||
|
||
// 编码检索
|
||
getCode() {
|
||
if (!this.queryCodeParams.maCode.trim()) {
|
||
uni.showToast({
|
||
title: '请输入设备编码',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
try {
|
||
getDeviceListAPI({'maCode': this.queryCodeParams.maCode}).then(response => {
|
||
console.log("xxxxxxxxxxx", response)
|
||
if (response.code === 200 && response.data && response.data.length > 0) {
|
||
this.optionList = response.data.map(option => ({
|
||
value: option.maId,
|
||
text: option.maCode
|
||
}));
|
||
if (response.data.length === 1) {
|
||
this.codeData = response.data[0];
|
||
setTimeout(() => {
|
||
this.handleIsOverToday()
|
||
}, 500)
|
||
}
|
||
} else {
|
||
uni.showToast({
|
||
title: '未查询到该编号信息',
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
}
|
||
}).catch(error => {
|
||
console.error("获取设备信息失败", error)
|
||
uni.hideLoading()
|
||
})
|
||
} catch (error) {
|
||
console.error('查询失败:', error);
|
||
uni.showToast({
|
||
title: '查询失败,请重试',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
},
|
||
|
||
// 标签改变
|
||
async changeTag() {
|
||
if (!this.queryCodeParams.maId) return;
|
||
try {
|
||
const response = await getDeviceListAPI({'maId': this.queryCodeParams.maId})
|
||
if (response.data && response.data.length !== 0) {
|
||
this.codeData = response.data[0]
|
||
setTimeout(() => {
|
||
this.handleIsOverToday()
|
||
}, 500)
|
||
}
|
||
} catch (error) {
|
||
console.error("获取编号信息失败", error);
|
||
uni.showToast({
|
||
title: '获取信息失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
},
|
||
|
||
// 清理资源
|
||
cleanup() {
|
||
console.log('清理资源...');
|
||
if (this.cameraStarted) {
|
||
const CameraPreview = this.getCameraPlugin();
|
||
if (CameraPreview) {
|
||
CameraPreview.stopCamera();
|
||
}
|
||
this.cameraStarted = false;
|
||
}
|
||
|
||
// 清除超时
|
||
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>
|
||
|
||
.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; }
|
||
|
||
.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;
|
||
}
|
||
|
||
/* 控制按钮区域 - 需要单独设置可点击 */
|
||
.bottom-controls {
|
||
position: absolute;
|
||
bottom: 100rpx;
|
||
left: 0;
|
||
right: 0;
|
||
display: flex;
|
||
justify-content: space-around; /* 改为space-around使按钮分布更均匀 */
|
||
align-items: center;
|
||
width: 100%;
|
||
padding: 0 60rpx; /* 添加左右内边距 */
|
||
z-index: 3;
|
||
pointer-events: auto;
|
||
}
|
||
.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;
|
||
}
|
||
|
||
.viewfinder-container {
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 40%;
|
||
margin-top: 150px;
|
||
z-index: 10001; /* 提高取景框层级 */
|
||
}
|
||
|
||
</style> |