w-笔记:uni-app的H5平台和非H5平台的拍照识别功能:

uni-app的H5平台和非H5平台的拍照识别功能:

<template><view class="humanVehicleBinding"><view v-if="warn" class="shadow"></view><view class="header"><uni-nav-bar left-icon="left" shadow :border="false" height="44" title="人车绑定" @clickLeft="back"></uni-nav-bar></view><view class="body"><uni-forms :modelValue="formData" label-position="top"><uni-forms-item label="使用车辆"><view class="useCar"><template v-if="dataReversion.code"><text class="textValue">{{dataReversion.code}}</text></template><template v-else><w-select v-model="vehicleListValue" :list="vehicleList" valueName="text" keyName="value":filterable="true" @change="vehicleValueChange" /></template><uni-icons type="scan" size="30" class="scan" @click="takePhoto"></uni-icons></view></uni-forms-item><uni-forms-item label="车辆类型" class="disable"><text class="textValue">{{dataReversion.vehicleType ? dataReversion.vehicleType : "--:--"}}</text></uni-forms-item><uni-forms-item label="使用人员" class="disable"><textclass="textValue">{{dataReversion.loginUserName ? dataReversion.loginUserName : "--:--"}}</text></uni-forms-item><uni-forms-item label="同行人"><view class="companions"><!-- <uni-data-picker :localdata="driverAccountNameList" popup-title="请选择同行人"v-model="dataReversion.driverAccountId" :map="{text:'text',value:'value'}"placeholder="请选择同行人" ref="companionsRef" @change="onchange" class="dataPicker"></uni-data-picker> --><!-- <select  multiple  v-model="dataReversion.driverAccountId"><option value="item.value" v-for="(item,index) in driverAccountNameList" :key="index">{{item.text}}</option></select> --><!-- 输入框用于触发弹出层 --><view class="trigger" @click="togglePopup" >{{ selectedLabels.length > 0 ? selectedLabels.join('、'): '请选择' }}</view><!-- uni-popup 弹出层 --><uni-popup ref="popup" type="bottom"><view class="popup-content"><view :class="isSelected(item) ? 'selected':'unselected'"  v-for="item in driverAccountNameList" :key="item.value" class="checkbox-item" @click="toggleSelection(item)"><text >{{ item.text }}</text></view><!-- <uni-list><uni-list-item v-for="item in driverAccountNameList" :key="item.value" :show-arrow="false"><template #default><view class="checkbox-item" @click="toggleSelection(item)"><text>{{ item.text }}</text><uni-icons v-if="isSelected(item)" type="checkbox-filled" size="18"color="#4CAF50"></uni-icons><uni-icons v-else type="circle" size="18" color="#999"></uni-icons></view></template></uni-list-item></uni-list> --><button @click="confirmSelection">确认</button></view></uni-popup><uni-icons type="forward" size="30" class="scan" @click="openDataPicker"></uni-icons></view></uni-forms-item><uni-forms-item label="使用开始时间" class="disable"><text class="textValue">{{dataReversion.beginTime ? dataReversion.beginTime : "--:--"}}</text></uni-forms-item><uni-forms-item label="使用结束时间" class="disable"><text class="textValue">{{dataReversion.endTime ? dataReversion.endTime : "--:--"}}</text></uni-forms-item></uni-forms><!-- <camera device-position="back" flash="auto" style="width: 100%; height: 100vh"></camera> --></view><view class="bottom"><view class="bottonBox"><button class="endUse" @click="usageEnd">结束使用</button><button class="startUse" @click="useAtOnce">立即使用</button></view></view></view>
</template><script setup>import {onMounted,ref,computed } from "vue";import permision from "@/common/permission.js";import {getVehicleList,getDataReversion,getDataReversion1,getCompanions,postUseAtOnce,postUsageEnd,getRecognize} from '../../../api/work/index.js'import SpeechSynthesisUtil from 'uniapp-text-to-speech';const basicText = ref('您已超速,请减缓速度,注意安全');const tts = new SpeechSynthesisUtil({API_KEY: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJHcm91cE5hbWUiOiJjeGwiLCJVc2VyTmFtZSI6ImN4bCIsIkFjY291bnQiOiIiLCJTdWJqZWN0SUQiOiIxOTMzNzc2NTc5MDI0OTE3MTI0IiwiUGhvbmUiOiIxNzM0NTM2MTYzMSIsIkdyb3VwSUQiOiIxOTMzNzc2NTc5MDE2NTI4NzczIiwiUGFnZU5hbWUiOiIiLCJNYWlsIjoiIiwiQ3JlYXRlVGltZSI6IjIwMjUtMDYtMTcgMTc6MjM6MzMiLCJUb2tlblR5cGUiOjEsImlzcyI6Im1pbmltYXgifQ.bd8aLiydYgr_m2GhQV5_Di92JkiQSCCN_TPIXmLCKC8gr64ImSWbvJuolAVzGFASzDVer5n21F-cznURRCc04s9ZwRFwVoQNcPMM-A-ChupBU2IJooYiKQgywmgki-ae0f_R7N328N6-62eaDCaVDWa23bhnHIxm7vzl5AP5CY9vwmkQGy4LPPgylhGLcT6RGRS0GYeRuezfs9qQb8L8NmrD4yYDxfJYQ6yZzPETN2FfV1OgsWQf1j0KEIpv0KdgJtUZ0ugaCdNa_pnjDpCzOZWLH1rnIpT0q-86BJGV1aCRxETfq6HZ8BBXvVOHaIJ7vitZt2-JqFu3I6dUpYdhyg",// Minimax API密钥GroupId: '1933776579016528773', // Minimax 组IDMAX_QUEUE_LENGTH: 3, // 可选:音频队列最大长度// 可选:音频生成配置modelConfig: {model: 'speech-02-hd',voice_setting: {"voice_id": "Chinese (Mandarin)_Radio_Host","speed": 0.8,"vol": 1,}},// 其他配置...})const warn = ref(false)const vehicleList = ref([])const vehicleListValue = ref()const dataReversion = ref({"carId": 0,"code": "","simCode": "","vehicleType": "","loginUserId": 0,"loginUserName": "","driverAccountId": [0],"driverAccountName": [""],"bindStatus": 0,"beginTime": "","endTime": ""})const driverAccountNameList = ref([])const selectedCompanion = ref('')onMounted((option) => {getVehicleListValue()getCompanionsValue()getDataReversionValue()})// 当前选中项const selectedValues = ref([])const selectedLabels = computed(() =>selectedValues.value.map(val => {console.log(driverAccountNameList.value)console.log(val,"val")console.log(driverAccountNameList.value.find(opt => opt.value === val))if(driverAccountNameList.value.find(opt => opt.value === val)){return driverAccountNameList.value.find(opt => opt.value === val).text}else{return  ""}}))// popup 控制const popup = ref(null)const togglePopup = () => {popup.value.open()}// 切换选项选中状态const toggleSelection = (item) => {const index = selectedValues.value.indexOf(item.value)if (index === -1) {selectedValues.value.push(item.value)} else {selectedValues.value.splice(index, 1)}console.log(selectedValues.value)}// 检查是否已选中const isSelected = (item) => {return selectedValues.value.includes(item.value)}// 确认选择并关闭弹窗const confirmSelection = () => {popup.value.close()}const getVehicleListValue = async () => {const res = await getVehicleList()if (res && res.data) {vehicleList.value = res.data.map(item => {return {text: item.code,// value: item.id,value: item.simCode,}})}}const getCompanionsValue = async () => {const res = await getCompanions()if (res && res.data) {driverAccountNameList.value = res.data.map(item => {return {text: item.chinaName,value: item.id}})console.log(driverAccountNameList.value, "66666666666");}}const getDataReversionValue = async () => {const res = await getDataReversion()if (res && res.data) {dataReversion.value = res.data// 如果有车牌号,则设置下拉框值if (res.data.code) {vehicleListValue.value = res.data.code}// 如果有同行人数据,需要设置选中状态if (res.data.driverAccountName && res.data.driverAccountName.length > 0) {const selectedName = res.data.driverAccountName[0]selectedValues.value = res.data.driverAccountIdconsole.log("selectedValues.value",selectedLabels.value)// 在下拉列表中找到对应的idconst selectedPerson = driverAccountNameList.value.find(item => item.text === selectedName)console.log("selectedPerson", selectedPerson);if (selectedPerson) {// 设置选中的值为找到的id,这样下拉框会显示为选中状态dataReversion.value.driverAccountId = [selectedPerson.value]dataReversion.value.driverAccountName = [selectedName]}}}}const vehicleValueChange = async (e) => {// if (!vehicleListValue.value) returnconst res = await getDataReversion1(vehicleListValue.value)if (res && res.data) {dataReversion.value.vehicleType = res.data.vehicleTypedataReversion.value.loginUserName = red.data.loginUserName}}const onchange = (e) => {console.log('onchange:', e);if (e.detail.value && e.detail.value.length > 0) {const selectedItem = e.detail.value[0];const text = selectedItem.text; // 申文昊const value = selectedItem.value; // 41// 直接更新 dataReversion 中的数组dataReversion.value.driverAccountId = [value];dataReversion.value.driverAccountName = [text];// 同时更新 loginUserId 和 loginUserNamedataReversion.value.loginUserId = value;dataReversion.value.loginUserName = text;console.log('更新后的 driverAccountId:', dataReversion.value.driverAccountId);console.log('更新后的 driverAccountName:', dataReversion.value.driverAccountName);}};const submitData = ref()const updateData = () => {console.log(selectedValues.value)dataReversion.value.driverAccountId = selectedValues.valuesubmitData.value = {"carId": dataReversion.value.carId,"code": dataReversion.value.code,"simCode": dataReversion.value.simCode,"vehicleType": dataReversion.value.vehicleType,"loginUserId": dataReversion.value.loginUserId,"loginUserName": dataReversion.value.loginUserName,"driverAccountId": dataReversion.value.driverAccountId,"bindStatus": dataReversion.value.bindStatus}}const getCurrentTime = () => {const now = new Date();let nowYear = now.getFullYear();let nowMonth = now.getMonth() + 1;let nowDay = now.getDate();let nowHours = now.getHours();let nowMinutes = now.getMinutes();let nowSeconds = now.getSeconds();nowMonth = nowMonth > 9 ? nowMonth : '0' + nowMonth;nowDay = nowDay > 9 ? nowDay : '0' + nowDay;nowHours = nowHours > 9 ? nowHours : '0' + nowHours;nowMinutes = nowMinutes > 9 ? nowMinutes : '0' + nowMinutes;nowSeconds = nowSeconds > 9 ? nowSeconds : '0' + nowSeconds;return  `${nowYear}-${nowMonth}-${nowDay} ${nowHours}:${nowMinutes}:${nowSeconds}`}const useAtOnce = async () => {updateData()await postUseAtOnce(submitData.value)}const usageEnd = async () => {dataReversion.value.beginTime = getCurrentTime()updateData()await postUsageEnd(submitData.value)}const back = () => {uni.navigateBack({delta: 1})}const openDataPicker = () => {companionsRef.value.show()}const endUse = async () => {// warn.value = !warn.value// try {// 	await tts.textToSpeech(basicText.value);// } catch (error) {// 	console.log("播放失败", error)// }}// #ifdef H5function getBase64FromFileToWeb(file) {console.log('调用了 H5 端 getBase64FromFile');return new Promise((resolve, reject) => {const reader = new FileReader();reader.onload = (e) => {const base64 = e.target.result.split(',')[1];resolve(base64);};reader.onerror = (error) => {reject(error);};reader.readAsDataURL(file);});}// #endif// #ifndef H5function getBase64FromFile(filePath) {return new Promise((resolve, reject) => {try {// #ifdef APP-PLUSplus.io.resolveLocalFileSystemURL(filePath, (entry) => {entry.file((file) => {const fileReader = new plus.io.FileReader();fileReader.onload = function(e) {const base64 = e.target.result.split(',')[1];resolve(base64);};fileReader.onerror = function(error) {console.error('FileReader错误:', error);reject(new Error('读取文件失败'));};fileReader.readAsDataURL(file);}, (error) => {console.error('获取文件对象失败:', error);reject(new Error('获取文件对象失败'));});}, (error) => {console.error('解析文件路径失败:', error);reject(new Error('解析文件路径失败'));});// #endif// #ifdef MPconst fsm = uni.getFileSystemManager();fsm.readFile({filePath: filePath,encoding: 'base64',success: (res) => {if (res.data) {console.log('Base64转换成功');resolve(res.data);} else {reject(new Error('Base64数据为空'));}},fail: (error) => {console.error('读取文件失败:', error);reject(new Error('读取文件失败: ' + (error.errMsg || '未知错误')));}});// #endif} catch (error) {console.error('文件处理错误:', error);reject(new Error('文件处理错误: ' + (error.message || '未知错误')));}});}// #endifconst takePhoto = async () => {// #ifdef APP-PLUS// 只检测相机权限let status = await checkCameraPermission();if (status !== 1) {// 没有权限,直接 returnreturn;}// #endifuni.chooseImage({count: 1,sourceType: ['camera'],sizeType: ['original', 'compressed'],success: async (res) => {try {let base64 = '';// #ifdef H5base64 = await getBase64FromFileToWeb(res.tempFiles[0]);// #endif// #ifndef H5if (res.tempFilePaths && res.tempFilePaths.length > 0) {const filePath = res.tempFilePaths[0];console.log('开始转换图片,文件路径:', filePath);base64 = await getBase64FromFile(filePath);} else {throw new Error('未能获取到图片路径');}// #endifif (!base64) {throw new Error('Base64转换失败: 结果为空');}console.log('Base64转换成功,长度:', base64.length);await recognizeAndBind(base64);} catch (error) {console.error('图片处理错误:', error);uni.showToast({title: error.message || '图片处理失败',icon: 'none',duration: 2000});}},fail: (err) => {if (err && err.errMsg && err.errMsg.indexOf('cancel') !== -1) return;uni.showToast({title: '打开相机失败,请检查权限',icon: 'none',duration: 2000});console.error('chooseImage fail:', err);}});};const recognizeAndBind = async (base64) => {try {if (!base64) {throw new Error('Base64数据为空');}console.log('开始识别处理,Base64长度:', base64.length);const options = {"data.format": "text","thpu.parser": "single_line","ocr.cls": true,"ocr.limit_side_len": 2880,"ocr.language": "models/config_chinese.txt"};const res = await getRecognize({base64,options});console.log('识别结果:', res);if (!res) {throw new Error('识别接口返回为空');}if (res && res.data) {if (!res.data.statusBit) {// 未绑定,更新表单数据并自动绑定console.log("此时的使用人员和id", dataReversion.value);dataReversion.value = {...dataReversion.value,carId: res.data.id,code: res.data.code,simCode: res.data.simCode,vehicleType: res.data.vehicleType,loginUserId: dataReversion.value.loginUserId, // 直接取当前值loginUserName: dataReversion.value.loginUserName, // 直接取当前值// driverAccountId: [0],// driverAccountName: [""]driverAccountId: dataReversion.value.driverAccountId,driverAccountName: dataReversion.value.driverAccountName}await useAtOnce()uni.showToast({title: '自动绑定成功',icon: 'success',duration: 2000});} else {// 已绑定,弹窗确认uni.showModal({title: '提示',content: `当前车辆已被绑定,确认是否继续绑定,绑定后默认结束上一使用人绑定记录。`,success: async (modalRes) => {if (modalRes.confirm) {console.log("11此时的使用人员和id", dataReversion.value);console.log("此时的使用人员", dataReversion.value.loginUserName);// 用户确认,更新表单数据并绑定dataReversion.value = {...dataReversion.value,carId: res.data.id,code: res.data.code,simCode: res.data.simCode,vehicleType: res.data.vehicleType,// driverAccountId: [0],// driverAccountName: [""],driverAccountId: dataReversion.value.driverAccountId,driverAccountName: dataReversion.value.driverAccountName,loginUserId: dataReversion.value.loginUserId, // 直接取当前值loginUserName: dataReversion.value.loginUserName, // 直接取当前值}console.log("更新后的", dataReversion.value)await useAtOnce()uni.showToast({title: '绑定成功',icon: 'success',duration: 2000});}// 用户取消,不做任何处理,保持原有数据}});}} else {uni.showToast({title: '未识别到车牌号',icon: 'none',duration: 2000});}} catch (error) {console.error('识别绑定过程出错:', error);uni.showToast({title: '识别处理失败: ' + (error.message || '未知错误'),icon: 'none',duration: 2000});}};// 检查相机权限const checkCameraPermission = async () => {// iOSif (permision.isIOS) {let status = await permision.requestIOS('camera');if (status !== 1) {uni.showModal({content: "没有开启相机权限",confirmText: "去设置",success: function(res) {if (res.confirm) {permision.gotoAppSetting();}}});}return status;}// Androidlet status = await permision.requestAndroid('android.permission.CAMERA');if (status !== 1) {uni.showModal({content: "没有开启相机权限",confirmText: "去设置",success: function(res) {if (res.confirm) {permision.gotoAppSetting();}}});}return status;};
</script><style lang="scss">.humanVehicleBinding {width: 750rpx;height: 100%;background-color: rgba(242, 242, 246, 1);display: flex;flex-direction: column;justify-content: space-between;:deep(.uni-data-tree) {font-size: 28rpx;color: #000;}.shadow {position: fixed;width: 750rpx;height: 100%;box-shadow: inset 0px 0px 20rpx red;}.header {width: 100%;height: 88rpx;:deep(.uni-navbar__header) {height: 88rpx;font-size: 24rpx;}:deep(.uni-nav-bar-text) {font-size: 28rpx;}}.body {width: 100%;height: 100%;margin: 0 0 3% 0;background-color: #fff;// overflow-y:auto:deep(.uni-forms-item) {margin: 20rpx 32rpx 0 32rpx;border-bottom: 1px solid rgba(17, 31, 44, 0.12);height: 120rpx;}:deep(.uni-forms-item__label) {padding: 0;line-height: 48rpx;height: 60rpx;width: 100% !important;font-size: 34rpx;font-weight: 400;letter-spacing: 0px;color: rgba(23, 26, 29, 1);// font-family:'Noto Sans SC', sans-serif;}:deep(.uni-forms-item__content) {font-size: 34rpx;color: rgba(23, 26, 29, 0.4);}:deep(.uni-select__selector-empty) {line-height: 70rpx;font-size: 34rpx;}:deep(.uni-select__selector-item) {line-height: 70rpx;font-size: 34rpx;}.disable {:deep(.uni-forms-item__label) {color: rgba(23, 26, 29, 0.24);}.textValue {color: rgba(23, 26, 29, 0.24);}}:deep(.input-value) {padding-left: 0;height: 60rpx;padding: 0 0 8px;}.useCar {position: relative;// bottom: 5px;:deep(.w-select) {color: rgba(23, 26, 29, 0.4);}:deep(.w-select .select-wrap) {width: 100%;border: none;}:deep(.w-select .select-wrap .select-options) {padding: 0;}:deep(.input-arrow) {// position: absolute;// top: -12.5px;// right: 0;border-left: 0;border-bottom: 0;}:deep(.placeholder) {font-size: 34rpx;font-weight: 400;color: rgba(23, 26, 29, 0.4);}.select {width: 646rpx;height: 44rpx;:deep(.uni-select) {border: none;padding-left: 0;}:deep(.uni-select__input-placeholder) {font-size: 34rpx;font-weight: 400;color: rgba(23, 26, 29, 0.4);}:deep(.uni-icons) {color: #fff !important;}}.scan {position: absolute;width: 48rpx;height: 44rpx;top: -40rpx;right: 0;font-size: 60rpx !important;}}}.companions {position: relative;// bottom: 10rpx;:deep(.input-value-border) {border: none;border-radius: none;}.popup-content{background-color: #fff;.selected{color: rgba(0, 127, 255, 1);line-height: 72rpx;padding: 20rpx;}.unselected{line-height: 72rpx;padding: 20rpx;}}.dataPicker {:deep(.uni-icons) {color: #fff !important;}}:deep(.input-arrow) {// position: absolute;// top: -12.5px;// right: 0;border-left: 0;border-bottom: 0;}:deep(.placeholder) {font-size: 34rpx;font-weight: 400;color: rgba(23, 26, 29, 0.4);}:deep(.input-value) {padding-left: 0;height: 60rpx;}.scan {position: absolute;width: 32rpx;height: 32rpx;top: -40rpx;right: 0;font-size: 60rpx !important;}}.bottom {// height: 102px;width: 100%;flex: 2.55;background-color: #fff;.bottonBox {display: flex;align-items: center;margin: 24rpx 0;:deep(uni-button) {font-size: 36rpx;display: flex;align-items: center;justify-content: center;}:deep(uni-button:after) {border: none;}.endUse {width: 330rpx;height: 88rpx;color: rgba(0, 127, 255, 1);border: 1px solid rgba(0, 127, 255, 1);background-color: rgba(255, 255, 255, 1);border-radius: 16rpx;margin-right: 14rpx;}.startUse {width: 330rpx;height: 88rpx;color: #fff;border: 1px solid rgba(0, 127, 255, 1);background-color: rgba(0, 127, 255, 1);border-radius: 16rpx;margin-left: 14rpx;}}}}
</style>

@/common/permission.js:

/// null = 未请求,1 = 已允许,0 = 拒绝|受限, 2 = 系统未开启var isIOSfunction album() {var result = 0;var PHPhotoLibrary = plus.ios.import("PHPhotoLibrary");var authStatus = PHPhotoLibrary.authorizationStatus();if (authStatus === 0) {result = null;} else if (authStatus == 3) {result = 1;} else {result = 0;}plus.ios.deleteObject(PHPhotoLibrary);return result;
}function camera() {var result = 0;var AVCaptureDevice = plus.ios.import("AVCaptureDevice");var authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide');if (authStatus === 0) {result = null;} else if (authStatus == 3) {result = 1;} else {result = 0;}plus.ios.deleteObject(AVCaptureDevice);return result;
}function location() {var result = 0;var cllocationManger = plus.ios.import("CLLocationManager");var enable = cllocationManger.locationServicesEnabled();var status = cllocationManger.authorizationStatus();if (!enable) {result = 2;} else if (status === 0) {result = null;} else if (status === 3 || status === 4) {result = 1;} else {result = 0;}plus.ios.deleteObject(cllocationManger);return result;
}function push() {var result = 0;var UIApplication = plus.ios.import("UIApplication");var app = UIApplication.sharedApplication();var enabledTypes = 0;if (app.currentUserNotificationSettings) {var settings = app.currentUserNotificationSettings();enabledTypes = settings.plusGetAttribute("types");if (enabledTypes == 0) {result = 0;console.log("推送权限没有开启");} else {result = 1;console.log("已经开启推送功能!")}plus.ios.deleteObject(settings);} else {enabledTypes = app.enabledRemoteNotificationTypes();if (enabledTypes == 0) {result = 3;console.log("推送权限没有开启!");} else {result = 4;console.log("已经开启推送功能!")}}plus.ios.deleteObject(app);plus.ios.deleteObject(UIApplication);return result;
}function contact() {var result = 0;var CNContactStore = plus.ios.import("CNContactStore");var cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0);if (cnAuthStatus === 0) {result = null;} else if (cnAuthStatus == 3) {result = 1;} else {result = 0;}plus.ios.deleteObject(CNContactStore);return result;
}function record() {var result = null;var avaudiosession = plus.ios.import("AVAudioSession");var avaudio = avaudiosession.sharedInstance();var status = avaudio.recordPermission();console.log("permissionStatus:" + status);if (status === 1970168948) {result = null;} else if (status === 1735552628) {result = 1;} else {result = 0;}plus.ios.deleteObject(avaudiosession);return result;
}function calendar() {var result = null;var EKEventStore = plus.ios.import("EKEventStore");var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0);if (ekAuthStatus == 3) {result = 1;console.log("日历权限已经开启");} else {console.log("日历权限没有开启");}plus.ios.deleteObject(EKEventStore);return result;
}function memo() {var result = null;var EKEventStore = plus.ios.import("EKEventStore");var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1);if (ekAuthStatus == 3) {result = 1;console.log("备忘录权限已经开启");} else {console.log("备忘录权限没有开启");}plus.ios.deleteObject(EKEventStore);return result;
}function requestIOS(permissionID) {return new Promise((resolve, reject) => {switch (permissionID) {case "push":resolve(push());break;case "location":resolve(location());break;case "record":resolve(record());break;case "camera":resolve(camera());break;case "album":resolve(album());break;case "contact":resolve(contact());break;case "calendar":resolve(calendar());break;case "memo":resolve(memo());break;default:resolve(0);break;}});
}function requestAndroid(permissionID) {return new Promise((resolve, reject) => {plus.android.requestPermissions([permissionID],function(resultObj) {var result = 0;for (var i = 0; i < resultObj.granted.length; i++) {var grantedPermission = resultObj.granted[i];console.log('已获取的权限:' + grantedPermission);result = 1}for (var i = 0; i < resultObj.deniedPresent.length; i++) {var deniedPresentPermission = resultObj.deniedPresent[i];console.log('拒绝本次申请的权限:' + deniedPresentPermission);result = 0}for (var i = 0; i < resultObj.deniedAlways.length; i++) {var deniedAlwaysPermission = resultObj.deniedAlways[i];console.log('永久拒绝申请的权限:' + deniedAlwaysPermission);result = -1}resolve(result);},function(error) {console.log('result error: ' + error.message)resolve({code: error.code,message: error.message});});});
}function gotoAppPermissionSetting() {if (permission.isIOS) {var UIApplication = plus.ios.import("UIApplication");var application2 = UIApplication.sharedApplication();var NSURL2 = plus.ios.import("NSURL");var setting2 = NSURL2.URLWithString("app-settings:");application2.openURL(setting2);plus.ios.deleteObject(setting2);plus.ios.deleteObject(NSURL2);plus.ios.deleteObject(application2);} else {var Intent = plus.android.importClass("android.content.Intent");var Settings = plus.android.importClass("android.provider.Settings");var Uri = plus.android.importClass("android.net.Uri");var mainActivity = plus.android.runtimeMainActivity();var intent = new Intent();intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);intent.setData(uri);mainActivity.startActivity(intent);}
}const permission = {get isIOS(){return typeof isIOS === 'boolean' ? isIOS : (isIOS = uni.getSystemInfoSync().platform === 'ios')},requestIOS: requestIOS,requestAndroid: requestAndroid,gotoAppSetting: gotoAppPermissionSetting
}export default permission

核心流程:

  1. 权限检查
  2. 调用相机获取图片
  3. 图片 Base64 转换
  4. 图片识别与车辆绑定

uni-app 官网上的 camera 是不支持 App 和 H5 端的

H5 端拍照识别实现:H5 端的拍照识别功能主要依赖浏览器原生 API,通过条件编译 #ifdef H5 标识 H5 端特有代码:

  • 权限处理:H5 端相机权限由浏览器管理,无需额外处理,用户首次调用相机时浏览器会自动弹出请求权限
  • 图片获取:通过 uni.chooseImage 接口调用相机,设置 sourceType: ['camera'] 指定从相机获取图片
uni.chooseImage({count: 1,                 // 最多选择1张图片sourceType: ['camera'],   // 仅从相机获取sizeType: ['original', 'compressed'], // 获取原图和压缩图success: async (res) => {// 处理获取到的图片}
});
  • 图片转换为 Base64:H5 端使用 FileReader 将图片转换为 Base64 格式
// H5端特有函数,用于将图片转换为Base64
// #ifdef H5function getBase64FromFileToWeb(file) {console.log('调用了 H5 端 getBase64FromFile');return new Promise((resolve, reject) => {const reader = new FileReader();reader.onload = (e) => {const base64 = e.target.result.split(',')[1];resolve(base64);};reader.onerror = (error) => {reject(error);};reader.readAsDataURL(file);});}// #endif

调用示例:

const base64 = await getBase64FromFileToWeb(res.tempFiles[0]);
  • 图片识别与车辆绑定:将 Base64 格式的图片数据传递给 OCR 识别接口,并处理识别结果
const recognizeAndBind = async (base64) => {try {// 配置识别参数const options = {"data.format": "text","thpu.parser": "single_line","ocr.cls": true,"ocr.limit_side_len": 2880,"ocr.language": "models/config_chinese.txt"};// 调用识别接口const res = await getRecognize({base64,options});// 处理识别结果if (res && res.data) {if (!res.data.statusBit) {// 未绑定,更新表单数据并自动绑定dataReversion.value = {...dataReversion.value,carId: res.data.id,code: res.data.code,simCode: res.data.simCode,vehicleType: res.data.vehicleType,// 保留其他已填数据}await useAtOnce()} else {// 已绑定,弹窗确认是否覆盖uni.showModal({title: '提示',content: `当前车辆已被绑定,确认是否继续绑定,绑定后默认结束上一使用人绑定记录。`,success: async (modalRes) => {if (modalRes.confirm) {// 更新数据并绑定dataReversion.value = {...dataReversion.value,carId: res.data.id,code: res.data.code,// 保留其他已填数据}await useAtOnce()}}});}}} catch (error) {console.error('识别绑定过程出错:', error);uni.showToast({title: '识别处理失败: ' + (error.message || '未知错误'),icon: 'none',duration: 2000});}
};

非 H5 端(App / 小程序)拍照识别功能:非 H5 端(App / 小程序)拍照识别功能需要处理原生权限,并使用不同的文件系统 API,通过条件编译 #ifndef H5 标识相关代码

  • 权限检查:区分 iOS 和 Android 平台:
// 检查相机权限const checkCameraPermission = async () => {// iOS 平台处理if (permision.isIOS) {let status = await permision.requestIOS('camera');if (status !== 1) {uni.showModal({content: "没有开启相机权限",confirmText: "去设置",success: function(res) {if (res.confirm) {permision.gotoAppSetting();}}});}return status;}// Android 平台处理let status = await permision.requestAndroid('android.permission.CAMERA');if (status !== 1) {uni.showModal({content: "没有开启相机权限",confirmText: "去设置",success: function(res) {if (res.confirm) {permision.gotoAppSetting();}}});}return status;};
      • 针对 iOS 和 Android 平台分别调用对应的权限请求 API
      • 如权限未开启,弹出模态框引导用户进入系统设置开启权限
    1. 图片获取:使用 uni.chooseImage 接口获取图片,非 H5 端返回的是本地文件路径:
    uni.chooseImage({count: 1,sourceType: ['camera'],success: (res) => {const filePath = res.tempFilePaths[0]; // 本地文件路径// 转换为Base64}
    });
    • 图片转换为 Base64:根据不同平台使用不同的文件读取方式:
        • App 端(5+App)使用 plus.io 模块
        • 小程序端使用 uni.getFileSystemManager()
    // 非H5端特有函数,用于将图片转换为Base64
    function getBase64FromFile(filePath) {return new Promise((resolve, reject) => {try {// 5+App平台#ifdef APP-PLUSplus.io.resolveLocalFileSystemURL(filePath, (entry) => {entry.file((file) => {const fileReader = new plus.io.FileReader();fileReader.onload = (e) => {const base64 = e.target.result.split(',')[1];resolve(base64);};fileReader.readAsDataURL(file);}, (error) => {console.error('获取文件对象失败:', error);reject(new Error('获取文件对象失败'));});}, (error) => {console.error('解析文件路径失败:', error);reject(new Error('解析文件路径失败'));});#endif// 小程序平台#ifdef MPconst fsm = uni.getFileSystemManager();fsm.readFile({filePath: filePath,encoding: 'base64',success: (res) => {if (res.data) {console.log('Base64转换成功');resolve(res.data);} else {reject(new Error('Base64数据为空'));}},fail: (error) => {console.error('读取文件失败:', error);reject(new Error('读取文件失败: ' + (error.errMsg || '未知错误')));}});#endif} catch (error) {console.error('文件处理错误:', error);reject(new Error('文件处理错误: ' + (error.message || '未知错误')));}});
    }
    • 图片识别与车辆绑定:非 H5 端使用与 H5 端相同的recognizeAndBind函数处理识别和绑定逻辑,实现了跨平台的统一处理

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
    如若转载,请注明出处:http://www.pswp.cn/web/87242.shtml
    繁体地址,请注明出处:http://hk.pswp.cn/web/87242.shtml

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    TCP 半连接队列和全连接队列(结合 Linux 2.6.32 内核源码分析)

    文章目录 一、什么是 TCP 半连接队列和全连接队列二、TCP 全连接队列1、如何查看进程的 TCP 全连接队列大小&#xff1f;注意 2、TCP 全连接队列溢出问题注意 3、TCP 全连接队列最大长度 三、TCP 半连接队列1、TCP 半连接队列溢出问题2、TCP 半连接队列最大长度3、引申问题 一、…

    linux下fabric环境搭建

    参考教程&#xff1a; https://devpress.csdn.net/cloudnative/66d58e702045de334a569db3.html?dp_tokeneyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MjA2MzY4NywiZXhwIjoxNzQwMzY4MDc0LCJpYXQiOjE3Mzk3NjMyNzQsInVzZXJuYW1lIjoiaHVhbmd0dXBpIn0.oh8e4F6Sw_A4SV2ODQ5W0pYK0…

    Redis Pipeline介绍:提高操作Redis数据库的执行效率

    Redis Pipeline是一种用于提高Redis执行效率的技术&#xff0c;通过减少客户端与服务器之间的通信开销&#xff0c;显著提升批量操作的性能。本文将详细介绍Redis Pipeline的概念、使用场景、实现方式及其优势。 一、Redis Pipeline的概念 Redis Pipeline是一种批处理机制&am…

    linux长时间锁屏无法唤醒

    是的&#xff0c;您这么理解很直接&#xff0c;抓住了要点。 简单来说&#xff0c;就是这样&#xff1a; 电脑睡觉有两种方式&#xff1a; 打个盹&#xff08;挂起/Suspend&#xff09;&#xff1a; 把工作状态保存在内存里。这个一般和 Swap 分区没关系。睡死过去&#xff…

    STM32F103_Bootloader程序开发11 - 实现 App 安全跳转至 Bootloader

    导言 想象一下&#xff0c;我们的单片机 App 正在稳定地运行着&#xff0c;突然我们想给它升级一下&#xff0c;添加个新功能。我们该如何安全地通知它&#xff1a;“嘿&#xff0c;准备好接收新固件了” ? 这就需要 App 和 Bootloader 之间建立一个可靠的"秘密握手"…

    Explain解释

    参考官方文档&#xff1a;https://dev.mysql.com/doc/refman/5.7/en/explain-output.html explain关键字可以分析你的查询语句的结构和性能。 explain select查询&#xff0c; 执行会返回执行计划的信息。 注意&#xff1a;如果from中有子查询&#xff0c;仍然会执行该子查询…

    选择 PDF 转 HTML 转换器的 5 个关键特性

    市面上有很多 PDF 转 HTML 的转换器&#xff0c;每一款产品都有不同的功能组合。要理清并理解每个功能可能会让人感到困惑。那么&#xff0c;真正重要的是什么呢&#xff1f; 这篇文章将介绍我们认为在选择最佳 PDF 转 HTML 转换器时最重要的 5 个关键特性&#xff1a; 1. 转换…

    使用堡塔在服务器上部署宝塔面板-linux版

    使用堡塔在服务器上部署宝塔面板-linux版 使用堡塔多机管理登录服务器 进入宝塔官网&#xff0c;获取安装脚本 wget -O install_panel.sh https://download.bt.cn/install/install_panel.sh && sudo bash install_panel.sh ed8484bec3. 在堡塔多机管理中&#xff0c;…

    【Unity高级】Unity多界面游戏场景管理方案详解

    引言&#xff1a;游戏界面管理的挑战 在Unity游戏开发中&#xff0c;尤其是包含多个功能界面&#xff08;如主菜单、关卡选择、游戏页面、设置和商城&#xff09;的游戏&#xff0c;如何高效管理场景与界面是架构设计的核心挑战。本文将深入探讨三种主流实现方案&#xff1a;单…

    WINDOWS最快布署WEB服务器:apache2

    安装JDK下载 https://tomcat.apache.org/ Index of /dist/tomcat/tomcat-9 安装测试 http://localhost:8080/ 替换自己的文件 把自己的文件复制到&#xff1a; C:\Program Files\Apache Software Foundation\Tomcat 9.0\webapps\ROOT

    Microsoft Edge 打开无反应、打开后显示兼容性问题、卸载重装 解决方案。一键卸载Microsoft Edge 。

    背景&#xff1a;网络上的浏览器修复、重装、恢复默认应用测试后无用&#xff0c;以下卸载重装方案经实测可以正常使用Microsoft Edg。 卸载软件在资源里&#xff0c;请自取。 一、卸载软件&#xff1a;Remove-Edge_GUI.exe 双击卸载等待即可。 二、在微软商店重新安装Micro…

    Spring Boot - 参数校验:分组校验、自定义注解、嵌套对象全解析

    01 依赖配置 在构建高效的校验体系前&#xff0c;需先完善项目依赖配置。 以下是优化后的依赖示例&#xff1a; <dependencies><!-- Web 依赖&#xff0c;提供 RESTful 接口支持 --><dependency><groupId>org.springframework.boot</groupId>…

    深入浅出多模态》(十一)之多模态经典模型:Flamingo系列

    &#x1f389;AI学习星球推荐&#xff1a; GoAI的学习社区 知识星球是一个致力于提供《机器学习 | 深度学习 | CV | NLP | 大模型 | 多模态 | AIGC 》各个最新AI方向综述、论文等成体系的学习资料&#xff0c;配有全面而有深度的专栏内容&#xff0c;包括不限于 前沿论文解读、…

    基于Pandas和FineBI的昆明职位数据分析与可视化实现(三)- 职位数据统计分析

    文章目录 一、职位数据分析1. 一级分类职位数量统计分析2. 职位二级分类分布分析3. 职位分布分析4. 工作经验需求分布分析5. 学历要求职位分布分析6. 企业职位供给前507. 不同规模公司的职位数量统计8. 区域职位分布分析9. 各区域平均薪资范围分布分析10. 不同工作经验平均薪资…

    大数据Hadoop之——安装部署hadoop

    目录 前期准备 一、JDK的安装 1、安装jdk 2、配置Java环境变量 3、加载环境变量 4、进行校验 二、hadoop的环境搭建 1、hadoop的下载安装 2、配置文件设置 2.1. 配置 hadoop-env.sh 2.2. 配置 core-site.xml 2.3. 配置 hdfs-site.xml 2.4. 配置 yarn-site.xml 2.…

    Spring IoC DI介绍

    文章目录 IoC & DI 介绍IoC介绍DI 介绍 组件注册Bean 命名约定方法注解 Bean总结 扫描路径DI 详解属性注入构造方法注入Setter 注入三种注入优缺点分析 当同一类型存在多个Bean时,直接使用Autowired会存在问题使用Primary注解使用Qualifier注解使用Bean的名称使用Resource注…

    【Flutter】解决 flutter_inappwebview在 Windows 上使用导致应用闪退问题

    问题背景 在 Windows 11 上运行 Flutter 桌面应用时&#xff0c;应用出现闪退现象。通过系统事件日志分析&#xff0c;发现是 MSVCP140.dll 模块的访问冲突异常&#xff08;错误代码 c0000005&#xff09;导致的崩溃。 问题分析 1. 错误现象 应用启动后立即闪退Windows 事件…

    使用 JavaScript、Mastra 和 Elasticsearch 构建一个具备代理能力的 RAG 助手

    作者&#xff1a;来自 Elastic JD Armada 了解如何在 JavaScript 生态系统中构建 AI 代理。 Elasticsearch 与业界领先的生成式 AI 工具和服务商有原生集成。查看我们的网络研讨会&#xff0c;了解如何超越 RAG 基础&#xff0c;或使用 Elastic 向量数据库构建可投入生产的应用…

    Active Directory 环境下 Linux Samba 文件共享服务建设方案

    Active Directory 环境下 Linux Samba 文件共享服务建设方案 目录 需求分析方案总体设计技术架构与选型详细部署规划共享文件性能测试非域终端共享配置运维与权限安全管理建议1. 需求分析 因某公司(编的)新增多个部门,各部门之间存在多类型终端系统,但又有同时访问文件库…

    Python爬虫网安-项目-简单网站爬取

    源码&#xff1a; https://github.com/Wist-fully/Attack/tree/pc pc_p1 目标&#xff1a; 1.进入列表页&#xff0c;顺着列表爬取每个电影详情页 2.利用正则来提取&#xff0c;海报&#xff0c;名称&#xff0c;类别&#xff0c;上映的时间&#xff0c;评分&#xff0c;剧…