小程序重构,采用 uniapp 框架。记录一下踩过的坑。关于用户拒绝再次调起授权,及如何识别语音识别、微信地址、附近地址的处理。
语音识别 组件
- 语音识别,小程序只有录音功能,若要识别录音文件,常规做法是把录音文件传递给后端,然后由后端调用百度或讯飞语音识别接口,然后返回结果。
- 但是微信小程序官方提供了“同声传译”插件,支持前端直接识别。可参考:插件介绍、插件使用文档
- uniapp 插件配置,在 manifest.json 文件中,源码模式,加入:
...
"mp-weixin": {
...
"plugins" : {
// 语音识别 - 同声传译
"WechatSI" : {
"version" : "0.3.1",
"provider" : "wx069ba97219f66d99"
}
}
}
- 调用
<template>
<view @click="asrStart"> 语音识别 </view>
<view>{{arsRes}}</view>
<!-- 语音识别 -->
<wechat-asr ref="weixinAsr" @callback="asrResult"/>
</template>
<script>
import WechatAsr from '@/components/wechatASR.vue';
export default {components: {WechatAsr},
data () {
return {arsRes: ''}
},
methods: {
// 语音识别
asrStart () {this.$refs.weixinAsr.show();
},
asrResult (res) {this.arsRes = res;}
}
}
</script>
- 编写组件,其中关于授权,用户若拒绝了,再次点击,本来是会一直进入失败的回调中,导致没法再次打开授权界面。所以先获取授权信息,针对没有授权、授权拒绝、授权成功,分别再次处理。若授权拒绝,需要打开授权设置界面,让用户再次授权处理。
<template>
<!-- 微信语音识别 -->
<view class="mask" v-show="isShow">
<view class="weixin-asr">
<view class="title"> 语音识别 </view>
<!-- 动画 -->
<view class="spinner">
<view class="rect rect1"></view>
<view class="rect rect2"></view>
<view class="rect rect3"></view>
<view class="rect rect4"></view>
<view class="rect rect5"></view>
</view>
<view class="tip"> 说出姓名、电话和详细地址 </view>
<button class="btn" type="default" @click="recordStop"> 说完了 </button>
</view>
</view>
</template>
<script>
const WechatSI = requirePlugin("WechatSI");
const ASRManager = WechatSI.getRecordRecognitionManager();
export default {data () {
return {isShow: false}
},
onReady () {
// 录音开启成功回调
ASRManager.onStart = function (res) {_this.isShow = true;}
const _this = this;
// 识别错误事件
ASRManager.onError = (res) => {
_this.isShow = false;
console.log(res.msg);
}
// 录音停止回调
ASRManager.onStop = function (res) {if (res && res.result) {_this.$emit('callback', res.result);
} else {
uni.showToast({
icon: 'none',
title: '抱歉,没听到您的声音哦'
})
}
}
},
methods: {data () {
return {isShow: false,}
},
show () {
const _this = this;
// 获取是否授权信息
uni.getSetting({success(res) {if (res && res.authSetting && res.authSetting.hasOwnProperty('scope.record')) {if (res.authSetting['scope.record']) {start();
} else { // 拒绝授权,打开授权设置
uni.openSetting({success() {start();
}
})
}
} else {start();
}
}
})
function start () {
ASRManager.start({lang: "zh_CN"});
}
},
// 录音停止
recordStop () {
this.isShow = false;
ASRManager.stop();}
}
}
</script>
<style lang="scss" scoped>
.mask {
position: fixed;
top: 0;
left: 0;
z-index: 300;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .54);
}
.weixin-asr {
position: absolute;
top: calc(50% - #{477upx / 2});
left: 0;
right: 0;
margin: 0 auto;
width: 560upx;
height: 477upx;
background: #fff;
text-align: center;
transform: .5s ease-out .5s;
.title {
margin-top: 42upx;
color: #000;
font-size: 34upx;
font-weight: 500;
}
.spinner {
margin: 50upx;
height: 100upx;
}
.tip {color: #787878;}
.btn {
margin-top: 28upx;
width: 225upx;
height: 82upx;
background: $theme1;
color: #fff;
font-size: 34upx;
line-height: 82upx;
border-radius: 82upx;
}
}
.spinner {text-align: center;}
.spinner > .rect {
background-color: #EDAA35;
height: 100%;
border-radius: 13upx;
width: 13upx;
display: inline-block;
-webkit-animation: stretchdelay 1.2s infinite ease-in-out;
animation: stretchdelay 1.2s infinite ease-in-out;
& + .rect {margin-left: 15upx;}
}
.spinner .rect2 {
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s;
}
.spinner .rect3 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}
.spinner .rect4 {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s;
}
.spinner .rect5 {
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s;
}
@-webkit-keyframes stretchdelay {0%, 40%, 100% { -webkit-transform: scaleY(0.4) }
20% {-webkit-transform: scaleY(1.0) }
}
@keyframes stretchdelay {
0%, 40%, 100% {transform: scaleY(0.4);
-webkit-transform: scaleY(0.4);
} 20% {transform: scaleY(1.0);
-webkit-transform: scaleY(1.0);
}
}
</style>
微信地址、附近地址
它们的处理,和上面逻辑一样,只是调用的 api 不一样。
逻辑也是先获取授权信息,未授权、用户拒绝授权、授权成功,在用户拒绝授权时,打开授权设置页面,没授权由小程序主动调起授权弹窗。
主要处理逻辑如下:
- 微信地址
chooseAddress (type) {
const _this = this;
if (type === 'weixin') {
// 处理拒绝再次打开调用设置
uni.getSetting({success (res) {if (res && res.authSetting && res.authSetting.hasOwnProperty('scope.address')) {if (res.authSetting['scope.address']) {choose();
} else {
uni.openSetting({success () {choose();
}
})
}
} else {choose();
}
}
});
function choose () {
uni.chooseAddress({success(res) {if (res) {// 调用接口将省市区转换成项目需要的,带 id 的,然后进行后续处理}
}
})
}
}
}
- 附近地址
nearAddress () {
const _this = this;
// 处理拒绝再次打开调用设置
uni.getSetting({success (res) {if (res && res.authSetting && res.authSetting.hasOwnProperty('scope.userLocation')) {if (res.authSetting['scope.userLocation']) {chooseLocation();
} else {
uni.openSetting({success () {chooseLocation();
}
})
}
} else {chooseLocation();
}
}
})
function chooseLocation () {
uni.chooseLocation({success: function (res) {if (res) {// 调用接口将省市区转换成项目需要的,带 id 的,然后进行后续处理}
}
});
}
}