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">
<text
class="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 组ID
MAX_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.driverAccountId
console.log("selectedValues.value",selectedLabels.value)
// 在下拉列表中找到对应的id
const 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) return
const res = await getDataReversion1(vehicleListValue.value)
if (res && res.data) {
dataReversion.value.vehicleType = res.data.vehicleType
dataReversion.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 和 loginUserName
dataReversion.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.value
submitData.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 H5
function 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 H5
function getBase64FromFile(filePath) {
return new Promise((resolve, reject) => {
try {
// #ifdef APP-PLUS
plus.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 MP
const 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 || '未知错误')));
}
});
}
// #endif
const takePhoto = async () => {
// #ifdef APP-PLUS
// 只检测相机权限
let status = await checkCameraPermission();
if (status !== 1) {
// 没有权限,直接 return
return;
}
// #endif
uni.chooseImage({
count: 1,
sourceType: ['camera'],
sizeType: ['original', 'compressed'],
success: async (res) => {
try {
let base64 = '';
// #ifdef H5
base64 = await getBase64FromFileToWeb(res.tempFiles[0]);
// #endif
// #ifndef H5
if (res.tempFilePaths && res.tempFilePaths.length > 0) {
const filePath = res.tempFilePaths[0];
console.log('开始转换图片,文件路径:', filePath);
base64 = await getBase64FromFile(filePath);
} else {
throw new Error('未能获取到图片路径');
}
// #endif
if (!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 () => {
// 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;
};
</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 isIOS
function 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
核心流程:
- 权限检查
- 调用相机获取图片
- 图片 Base64 转换
- 图片识别与车辆绑定
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 H5
function 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
- 如权限未开启,弹出模态框引导用户进入系统设置开启权限
- 图片获取:使用 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-PLUS
plus.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 MP
const 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函数处理识别和绑定逻辑,实现了跨平台的统一处理