小程序重构,采用 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的,然后进行后续处理          }        }      });    }}