本实训项目结合云开发的云数据库和 “微信同声传译”插件,制作一个可真实运营的小学生语文听写工具,页面效果如图1所示。

 ▍图1 “听写小助手”页面

基于云开发的微信小程序具有众多优势,云开发模式真正解放了开发者,使得开发效率大大提升,其模式下的小程序开发和交付流程也更加便捷;云开发建立了小程序端通向腾讯云和小程序端通向微信的捷径,也为连接其他更多的腾讯云资源提供了捷径,还可以打通云到云、端到端的界限,其计算资源计费更合理,成本也更低。

在小程序互联网飞速发展的时代,教育场景被重塑,教育类小程序迎来猛增。2020年的新冠疫情为在线教育带来了新活力,推动了用户对在线教育的需求。因此,本团队基于在线教育需求研发了“听写好助手”这款小程序。

“听写好助手”是一个以语文为核心,以微信小程序为窗口,以学生及其家长为服务对象的全语音化教学平台。“听写好助手”集语音听写、错题分析、每日十词、复习提醒、个性定制、阶段复习六项功能于一身,采用语音播报模式,减少学生用眼,大大提高了学生的学习效率,同时也减轻了家长在为孩子辅导听写作业上的压力。

本案例以云开发的云数据库为基础,制作一个面向小学语文听写的微信小程序。

01、开发内容

为了实现“听写小助手”的语音播放功能,需要添加插件“微信同声传译”,具体步骤为:登录微信平台,选择“设置”→“第三方设置”→“插件管理”→“搜索插件”并完成添加。添加插件后打开“控制台”→“数据库”,将数据库文件导入数据库,从而完成了小学六年课后的所有单词的储存。最后为了前后端的用户互动需要用云函数来进行操作,为此要完成同步云函数列表以及上传并部署getContent和getUserCollectList云函数操作,重新编译后选择一年级上册的书,即可实现听写功能。同样的导入剩余的数据库集合即可实现所有书册的听写功能。

听写数据单个集合每条记录包含的字段,如图2所示。

▍图2 rn_11集合导入完成

本案例开发主要包括添加插件、数据库页面、云函数上传部署三个步骤。

1、添加插件

听写好助手的代码中使用了微信同声传译的插件,这是由于听写好助手需要将存在数据库中的文字转换成语音,要让代码正常跑起来,需要登录微信公众平台,在“设置”→“第三方设置”→“插件管理”中,添加插件“微信同声传译”,添加插件后,如图3所示。

▍图3添加插件“微信同声传译”

2、页面数据库

添加完插件后再进行重新编译,会发现还有报错,原因是云开发数据库里没有需要的课本对应的数据记录,因此需要进行数据库的导入。数据库文件具体如图四所示。其中,rn_11对应的是一年级上册的听写数据,rn_12对应的是一年级下册的听写数据,以此类推。

▍图4 数据库文件

3、云函数的上传部署

右击cloudfunctions,选择“同步云函数列表”,完成同步云函数列表以及上传并部署getContent和getUserCollectList云函数操作,重新编译后选择一年级上册的书,即可实现听写功能。同样的导入剩余的数据库集合即可实现所有书册的听写功能,如图5所示。

▍图5 同步云函数列表

02、项目代码

pages/chooseBook/chooseBook.wxml的代码如下:

<view id="chooseBook">
  <button 
    class='toCollect' 
    bindtap='toCollect'
  >错题</button>
  <button class='button' open-type="feedback">
    <icon type="info_circle" color='rgba(255, 0, 0, 0.6)' size="16" style='margin-right:2px;'></icon> 
    <text class='button_title'>反馈建议</text>
  </button>
  <view class='tab'>
      <scroll-view scroll-x="true" class='tab-nav' scroll-left='{{scrollLeft}}' scroll-with-animation="true">
            <view wx:for="{{navlist}}" wx:key="unique" class='{{current==index?"on":""}}' data-current="{{index}}" bindtap='tab'>{{item}}</view> 
      </scroll-view>
      <swiper class='tab-box'zz current="{{current}}" bindchange="eventchange">
        <swiper-item wx:for="{{conlist}}" wx:key="unique">
        <view class='tip'>左右滑动切换哦</view>
          <view class="module-container">
            <view class="box-wrapper" wx:for="{{item.moudles}}" wx:key="index">
              <navigator url="{{item.url}}" hover-class="none">
                <view class="servicebox">
                  <image src="{{item.src}}" class="box-img"/>
                  <text style='font-size: 35rpx;'>{{item.text}}</text>
                </view>
              </navigator>
            </view>
          </view>
        </swiper-item>
    </swiper>
  </view>
</view>

 pages/chooseBook/chooseBook.js的代码如下:

const app = getApp()
Page({
  data: {
    current: 0,//当前所在滑块的 index
    navlist: ["一二年级", "三四年级", "五六年级"],
    //课本列表
    conlist: []
  },
  //tab切换
  tab: function (event) {
    this.setData({ current: event.target.dataset.current })
    //锚点处理
  },
  //滑动事件
  eventchange: function (event) {
    this.setData({ current: event.detail.current })
    //锚点处理
  },
  //生命周期函数--监听页面加载
  onLoad: function (options) {
    this.setData({
      conlist: [
        {
          moudles: [
            {
              url: './chooseLesson/chooseLesson?book=rn_11',
              src: '/img/book/ch_rn_11.jpg',
              text: '部编版一年级上册'
            },
            {
              url: './chooseLesson/chooseLesson?book=rn_12',
              src: '/img/book/ch_rn_12.jpg',
              text: '部编版一年级下册'
            },
            {
              url: './chooseLesson/chooseLesson?book=rn_21',
              src: '/img/book/ch_rn_21.jpg',
              text: '部编版二年级上册'
            },
            {
              url: './chooseLesson/chooseLesson?book=rn_22',
              src: '/img/book/ch_rn_22.jpg',
              text: '部编版二年级下册'
            }
          ]
        },
        {
          moudles: [
            {
              url: './chooseLesson/chooseLesson?book=rn_31',
              src: '/img/book/ch_rn_31.jpg',
              text: '部编版三年级上册'
            },
            {
              url: './chooseLesson/chooseLesson?book=rn_32',
              src: '/img/book/ch_rn_32.jpg',
              text: '部编版三年级下册'
            },
            {
              url: './chooseLesson/chooseLesson?book=rn_41',
              src: '/img/book/ch_rn_41.jpg',
              text: '人教版四年级上册'
            },
            {
              url: './chooseLesson/chooseLesson?book=rn_42',
              src: '/img/book/ch_rn_42.jpg',
              text: '人教版四年级下册'
            }
          ]
        },
        {
          moudles: [
            {
              url: './chooseLesson/chooseLesson?book=rn_51',
              src: '/img/book/ch_rn_51.jpg',
              text: '人教版五年级上册'
            },
            {
              url: './chooseLesson/chooseLesson?book=rn_52',
              src: '/img/book/ch_rn_52.jpg',
              text: '人教版五年级下册'
            },
            {
              url: './chooseLesson/chooseLesson?book=rn_61',
              src: '/img/book/ch_rn_61.jpg',
              text: '人教版六年级上册'
            },
            {
              url: './chooseLesson/chooseLesson?book=rn_62',
              src: '/img/book/ch_rn_62.jpg',
              text: '人教版六年级下册'
            }
          ]
        },
      ],
    })
  },
  toCollect: function () {
    wx.navigateTo({
      url: "../user/collectList/collectList",
    })
  },
  onReady: function () {},
  onShow: function () {},
  onHide: function () {},
  onUnload: function () {},
  onPullDownRefresh: function () {},
  onReachBottom: function () {},
  onShareAppMessage: function () {}
})

 pages/chooseBook/chooseBook.wxss的代码如下:

.button {
  position: fixed;
  left: 20rpx;
  bottom: 30rpx;
  background: #FAF0E6;
  border: none;
  text-align: left;
  margin: 0px;
  line-height: 1.6;
  border-radius: 0;
}
.button::after {
 border: none;
 border-radius: 0;
}
.button_title {
 font-size: 12px;
 color: rgb(114, 112, 112);
}
.toCollect {
  position: fixed;
  bottom: 100rpx;
  right: 40rpx;
  font-size: 40rpx;
  height: 70rpx;
  line-height: 70rpx;
  background-color: rgba(255, 213, 124, 0.925);
  z-index: 999;
  box-shadow: 2px 2px 2px #bbb;
}
/* tab切换效果 */
swiper {
  height: 1000rpx;
}
.tab{ padding: 20rpx 0;}
.tab-nav{
  height: 80rpx;
  line-height: 80rpx;
}
.tab-nav view{
  float: left;
  height: 80rpx;
  line-height: 80rpx;
  background: #FAF0E6;
  width: 33.33%;
  font-size: 30rpx;
  text-align: center;
  color: #000;
}
.tab-nav view.on{
  background: #FAF0E6;
  color: rgb(255, 201, 18);
  position: relative;
}
.tab-nav view.on:after{
   content: "";
   display: block;
   height: 6rpx;
   width: 26px;
   background: rgb(243, 189, 10);
   position: absolute;
   bottom: 2px;
   left: calc(50% - 12px);
   border-radius: 16rpx;
}
.tip {
  color: #aaa;
  text-align: center;
  font-size: 35rpx;
  margin-top: 20rpx;
}
/* 书本选项 */
#chooseBook .module-container {
  width: 100%;
  display: flex;
  flex-wrap:wrap;
  box-sizing: border-box;
  flex-direction:row;
  justify-content: center;
  margin-top: 55rpx;
}
#chooseBook .module-container .box-wrapper{
  height: 300rpx;
  width: 200rpx;
  margin: 0 70rpx;
  margin-bottom: 95rpx;
}
/* 服务选项 */
#chooseBook .module-container .box-wrapper .servicebox{
  display:flex;
  flex-direction:column;
  justify-content:center;
  align-items:center;
  text-align: center;
}
#chooseBook .module-container .box-wrapper .servicebox .box-img{
  height:250rpx;
  width: 100%;

  margin-bottom: 10rpx;
  box-shadow: 2px 2px 3px #aaa;
}

 代码讲解

chooseBook.js的onLoad()函数为conlist列表中每个元素设置对应的url、src和text内容,以此将这些数据绑定在chooseBook.wxml中,运行程序便可渲染显示出来。

pages/chooseBook/chooseLesson/chooseLesson.wxml的代码如下:

<view id="listen">
  <view class='tab'>
      <scroll-view scroll-x="true" class='tab-nav' scroll-left='{{scrollLeft}}' scroll-with-animation="true">
        <view class='tab-nav-c' style='width:{{conlist.length*90}}px'>
            <view wx:for="{{conlist}}" wx:key="unit" class='{{current==index?"on":""}}' data-current="{{index}}" bindtap='tab'>第{{index==0?'一':index==1?'二':index==2?'三':index==3?'四':index==4?'五':index==5?'六':index==6?'七':index==7?'八':index==8?'九':index==9?'十':''}}单元</view>
        </view>
      </scroll-view>
  </view>
  <view class='swiper-box'>
      <swiper class='swiper'  style='height:{{conlist[current].length*150+135}}rpx;' current="{{current}}" bindchange="eventchange">
        <swiper-item wx:for="{{conlist}}" wx:key="unit">
          <view class='tip'>左右滑动切换哦</view>
          <view class="module-container">
            <view class="box-wrapper" wx:for="{{item}}" wx:key="index">
              <view class="text-box">
                <text>{{item.title}}</text>
              </view>
              <view class="img-box" data-content='{{item}}' bindtap='toDetail'>
                <image src='/img/listen2.png' mode="widthFix"></image>
              </view>
            </view>
          </view>
        </swiper-item>
    </swiper>
  </view>
</view>

 pages/chooseBook/chooseLesson/chooseLesson.js的代码如下:

const db = wx.cloud.database();
const _ = db.command;
let plugin = requirePlugin("WechatSI");
let manager = plugin.getRecordRecognitionManager();
const innerAudioContext = wx.createInnerAudioContext();
let that;
let book;
Page({
  data: {
    current: 0,//当前所在滑块的 index
    scrollLeft: -90,//滚动条的位置,一个选项卡宽度是90(自定义来自css),按比例90*n设置位置
    conlist: [],
  },
  //tab切换
  tab: function (event) {
    // console.log(event.target.dataset.current);
    this.setData({ current: event.target.dataset.current })
    //锚点处理
    this.setData({
      scrollLeft: event.target.dataset.current * 90 - 90,
    })
  },
  //滑动事件
  eventchange: function (event) {
    console.log(event.detail.current)
    this.setData({ current: event.detail.current })
    //锚点处理
    this.setData({
      scrollLeft: event.detail.current * 90 - 90,
    })
  },
  toDetail: function (e) {
    let content = '';
    let speak = '';
    for (let word of e.currentTarget.dataset.content.content) {
      content = content + word + '/';
    }
    if (e.currentTarget.dataset.content.speak) {
      for (let word of e.currentTarget.dataset.content.speak) {
        speak = speak + word + '/';
      }
    }
    wx.navigateTo({
      url: './detail/detail?content=' + content + '&speak=' + speak + '&book=' + book,
    })
  },
  onLoad: function (options) {
    wx.showLoading({
      title: '加载中',
    });
    book = options.book;
    that = this;
    // setNavigationBarTitle
    let bookName = '语文';
    let bookLevel = {
      "11": "一年级上册",
      "12": "一年级下册",
      "21": "二年级上册",
      "22": "二年级下册",
      "31": "三年级上册",
      "32": "三年级下册",
      "41": "四年级上册",
      "42": "四年级下册",
      "51": "五年级上册",
      "52": "五年级下册",
      "61": "六年级上册",
      "62": "六年级下册",
    }
    if (book.search("su") != -1) { bookName += '苏教版' } else if (book.search("zh") != -1) { bookName += '浙教版' } else if (book.search("rn") != -1 && (book.search("4") != -1 || book.search("5") != -1 || book.search("6") != -1)) { bookName += '人教版' } else { bookName += '部编版' }
    for (let key in bookLevel) {
      if (book.search(key) != -1) {
        bookName += bookLevel[key]
      }
    }
    wx.setNavigationBarTitle({
      title: bookName
    })
    let dbBook = book;
    let conlist = [];
    // 使用云函数,能读100条
    wx.cloud.callFunction({
      name: 'getContent',
      data: {
        dbBook: dbBook
      }
    }).then(res => {
      that.setData({
        conlist: res.result
      });
      wx.hideLoading();
    })
  },
  onReady: function () {
  },
  onShow: function () {
  },
  onHide: function () {
  },
  onUnload: function () {
    innerAudioContext.offPlay();
  },
  onPullDownRefresh: function () {
  },
  onReachBottom: function () {},
  onShareAppMessage: function () {
  }
})

 pages/chooseBook/chooseLesson/chooseLesson.wxss的代码如下:

page {
  background-color: #fff;
}
/* tab切换效果 */
.swiper-box {
  /* overflow-y: scroll; */
  height: 90%;
  position: absolute;
  width: 100%;
}
.swiper {
  min-height: 100%;
  width: 100%;
  height: 100%;
}
.tip {
  color: #888;
  /* border-bottom: 1px solid #f2f2f2; */
  text-align: center;
  font-size: 35rpx;
  line-height: 35rpx;
  padding: 30rpx;
}
scroll-view{
  width: 100%;
  height: 100%;/*动态高度*/
  overflow-y: scroll;
}
/* 顶部tab */
.tab{
  height: 80rpx;
  box-shadow: 0px 2px 3px #888888;
}
.tab-nav{
  height: 80rpx;
  line-height: 80rpx;
  width: 100%;
  background-color: #FAF0E6;
}
.tab-nav .tab-nav-c view{
  height: 80rpx;
  line-height: 80rpx;
  float: left;
  width: 90px;
  font-size: 30rpx;
  text-align: center;
  color: #000;
}
.tab-nav view.on{
  background: #FAF0E6;
  color: rgb(255, 201, 18);
  position: relative;
}
.tab-nav view.on:after{
   content: "";
   display: block;
   height: 6rpx;
   width: 26px;
   background: rgb(243, 189, 10);
   position: absolute;
   bottom: 2px;
   left: 32px;
   border-radius: 16rpx;
}
/* 词语 */
#listen .module-container {
  width: 100%;
  display: flex;
  flex-wrap:nowrap;
  flex-direction:column;
  justify-content: center;
  align-items: center;
}
#listen .module-container .box-wrapper{
  background-color: #f2f2f2;
  border-bottom: 1px solid #c2c2c2;
  display: flex;
  flex-direction: row;
  align-items: center;
  flex-wrap:nowrap;
  width: 100%;
  height: 150rpx;
  justify-content: center;
}
#listen .module-container .box-wrapper .text-box{
  display: flex;
  width: 70%;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
}
#listen .module-container .box-wrapper .text-box text{
  font-size: 40rpx;
  text-align: center;
  line-height: 60rpx;
}
#listen .module-container .box-wrapper .img-box {
  width: 20%;
}
#listen .module-container .box-wrapper .img-box image {
  width: 100%;
}
/* 服务选项 */
#listen .module-container .box-wrapper .servicebox{
  display:flex;
  flex-direction:column;
  justify-content:center;
  align-items:center;
  text-align: center;
}
#listen .module-container .box-wrapper .servicebox .box-img{
  height:250rpx;
  width: 100%;
  margin-bottom: 5rpx;
}

 代码讲解

chooseLesson .js的onLoad()函数自动执行对云数据库的查询操作,获取到云数据库中课本的数据,并赋值给“book”,然后通过数据绑定的方式在chooseLesson.wxml中进行渲染显示。

pages/chooseBook/chooseLesson/detail/detail.wxml的代码如下:

<view id='detail'>
  <van-transition name="fade" duration='1000' show="{{show}}" style="{{i==sum?'display:none':''}}">
    <view style="width:80%;margin:0 auto;position:relitive;top:-80rpx;">
      <van-steps
      steps="{{ steps }}"
      active="{{ active }}"
      /> 
    </view>
    <view class="page__bd">
        <view class="icon-box" bindtap='preWord'>
            <image 
              class='icon' 
              style=' width: 150rpx;height: 150rpx;'
              src="/img/pre.png"
            >上一个</image>
            <view class="icon-box__ctn">
                <view class="icon-box__title">上一个</view>
            </view>
        </view>
        <view class="icon-box" bindtap='nextWord'>
            <image 
              class='icon' 
              src="/img/{{(i==-1?'start':i==sum-1?'end':'next')}}.png"
            >下一个</image>
            <view class="icon-box__ctn">
                <view class="icon-box__title">下一个</view>
            </view>
        </view>
        <view class="icon-box" style='margin-bottom: 0;' bindtap='again'>
            <image 
              class='icon' 
              style=' width: 150rpx;height: 150rpx;'
              src="/img/again.png"
            >再读一遍</image>
            <view class="icon-box__ctn">
                <view class="icon-box__title">再读一遍</view>
            </view>
        </view>
    </view>
  </van-transition>
  <view style="{{i<sum?'display:none':''}}">
    <view class="weui-cells__title" style="font-size:16px;color:#000;margin-bottom:40rpx;">请校对:</view>
    <view class="weui-cells weui-cells_after-title">
      <checkbox-group bindchange="checkboxChange">
        <label class="weui-cell weui-check__label" wx:for="{{content}}" wx:key="index">
          <checkbox class="weui-check" value="{{item.value}}" checked="{{item.checked}}"/>
          <view class="weui-cell__hd weui-check__hd_in-checkbox">
            <icon class="weui-icon-checkbox_circle" type="circle" size="23" wx:if="{{!item.checked}}"></icon>
            <icon class="weui-icon-checkbox_success" type="cancel" size="23" wx:if="{{item.checked}}"></icon>
          </view>
          <view class="weui-cell__bd">{{item.name}}</view>
        </label>
      </checkbox-group>
    </view>
    <view class="weui-btn-area">
      <button class="weui-btn" style='background-color:#fff' plain="" type="default" bindtap="submit" disabled='{{submit}}'>提交错题</button>
      <button class="weui-btn weui_btn_primary" style='color:#fff;background-color:#33CC99' plain="" type="default" bindtap="submitAndAgain" disabled='{{submit}}'>再听一遍</button>
    </view>
  </view>
</view>

 pages/chooseBook/chooseLesson/detail/detail.js的代码如下:

const db = wx.cloud.database();
const _ = db.command;
let plugin = requirePlugin("WechatSI");
let manager = plugin.getRecordRecognitionManager();
const innerAudioContext = wx.createInnerAudioContext();
let that;
let i;
let active;
let oriSpeak;
let oriContent;
let book;
Page({
  data: {
    i: -1,
    sum: 99,
    userCollect: [],
    content: [],
    speak: [],
    steps: [],
    active: -1,
    show: true,
    submit: false
  },
  // 文字转语音(语音合成)
  wordToSpeak: function (word) {
    let that = this;
    plugin.textToSpeech({
      lang: "zh_CN",
      tts: true,
      content: word,
      success: function (res) {
        console.log(" tts", res)
        innerAudioContext.autoplay = true
        innerAudioContext.src = res.filename
        wx.showLoading({
          // 提交时取消注释
          mask: true,
          title: '正在播放',
        })
      },
      fail: function (res) {
        console.log("fail tts", res)
      }
    })
  },
  // 下一个
  nextWord: function (e) {
    active = this.data.active;
    i = this.data.i;
    this.setData({
      active: ++active,
      i: i+1
    });
    that.wordToSpeak(this.data.speak[i+1]);
  },
  // 上一个
  preWord: function (e) {
    i = this.data.i;
    i = this.data.i;
    if (i > 0) {
      this.setData({
        active: --active,
        i: i - 1
      });
      that.wordToSpeak(this.data.speak[i-1]);
    } else {
      wx.showToast({
        icon: 'none',
        title: '没有上一个了!',
      })
    }
  },
  // 重复
  again: function (e) {
    i = this.data.i;
    if (i > -1) {
      that.wordToSpeak(this.data.speak[i]);
    } else {
      wx.showToast({
        icon: 'none',
        title: '请先开始噢!',
      })
    }
  },
  onLoad: function (options) {
    oriSpeak = options.speak;
    oriContent = options.content;
    book = options.book;
    let content = [];
    let speak = [];
    let contentTemp = [];
    console.log(options);
    that = this;
    speak = options.speak.split('/');
    speak.pop();
    content = options.content.split('/');
    content.pop();
    this.setData({
      sum: content.length,
      speak: (speak.length == 0 ? content : speak),
      steps: content
    })
    for (let name of content) {
      let o = {};
      o['name'] = name;
      o['value'] = name;
      contentTemp.push(o);
    }
    that.setData({
      content: contentTemp
    })
    innerAudioContext.onPlay(() => {
      console.log('开始播放')
    })
    innerAudioContext.onError((res) => {
      if (res) {
        console.log(res)
        wx.hideLoading(),
          wx.showToast({
            title: '文本格式错误',
            image: '/images/fail.png',
          })
      }
    })
    innerAudioContext.onEnded(function () {
      manager.start({
        lang: "zh_CN"
      })
      wx.hideLoading()
    })
  },
  checkboxChange: function (e) {
    console.log('checkbox发生change事件,携带value值为:', e.detail.value);
    var checkboxItems = this.data.content, values = e.detail.value;
    for (var i = 0, lenI = checkboxItems.length; i < lenI; ++i) {
      checkboxItems[i].checked = false;
      for (var j = 0, lenJ = values.length; j < lenJ; ++j) {
        if (checkboxItems[i].value == values[j]) {
          checkboxItems[i].checked = true;
          break;
        }
      }
    }
    this.setData({
      content: checkboxItems,
      userCollect: e.detail.value
    });
  },
  submit: function () {
    this.setData({
      submit: true
    })
    wx.showLoading({
      title: '提交中...',
      mask:true
    })
    let userCollectID;
    if (that.data.userCollect) {
      db.collection('userCollectList').add({
        data: {
          collect: that.data.userCollect,
          book: book,
          createTime: db.serverDate()
        },
        success(res) {
          wx.hideLoading();
          wx.showToast({
            title: '提交成功!',
            duration: 3000,
            mask: true
          })
          setTimeout(() => {
            wx.navigateBack({
            })
          }, 1000)
        }
      })
    } else {
      wx.hideLoading();
      wx.showToast({
        title: '提交成功!',
        duration: 3000,
        mask: true
      })
      setTimeout(() => {
        wx.navigateBack({
        })
      },1000)
    }
  },
  submitAndAgain: function () {
    this.setData({
      submit: true
    })
    wx.showLoading({
      title: '提交中...',
      mask: true
    })
    let userCollectID;
    if (that.data.userCollect) {
      db.collection('userCollectList').add({
        data: {
          collect: that.data.userCollect,
          book: book,
          createTime: db.serverDate()
        },
        success(res) {
          wx.hideLoading();
          wx.showToast({
            title: '提交成功!',
            duration: 3000,
            mask: true
          })
          setTimeout(() => {
            wx.redirectTo({
              url: './detail?content=' + oriContent + '&speak=' + oriSpeak
            })
          }, 300)
        }
      })
    } else {
      wx.hideLoading();
      wx.showToast({
        title: '提交成功!',
        duration: 3000,
        mask: true
      })
      setTimeout(() => {
        wx.redirectTo({
          url:'./detail?content=' + oriContent + '&speak=' + oriSpeak
        })
      }, 800)
    }
  },
  onReady: function () {},
  onShow: function () {},
  onHide: function () {},
  onUnload: function () {
    innerAudioContext.offPlay();
    innerAudioContext.offEnded();
    innerAudioContext.offError();
    innerAudioContext.stop();
    wx.stopBackgroundAudio();
    manager.start({
      lang: "zh_CN"
    })
    wx.hideLoading()
  },
  onPullDownRefresh: function () {},
  onReachBottom: function () {},
  onShareAppMessage: function () {}
})

pages/chooseBook/chooseLesson/detail/detail.wxss的代码如下:

#detail {
  position: relative;
}
.weui-cell {
  width: 40%;
}
checkbox-group {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}
.weui-cell__bd {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
#detail .content-box {
  width: 80%;
  margin: 0 auto;
  margin-top: 220rpx;
  display: flex;
  align-items: center;
  flex-direction: row;
  flex-wrap: wrap;
}
#detail .content-box .content {
  font-size: 60rpx;
  margin: 0 20rpx;
  display: line-block;
}
.page__bd {
    margin-top: 90rpx;
    padding: 0 30px;
    text-align: left;
}
.icon-box{
    margin-bottom: 80rpx;
    display: flex;
    align-items: center;
    border: 2px solid #FF9933;
    border-radius: 80rpx;
    box-shadow: 4px 4px 4px #ddd;
    background-color: rgba(255, 224, 51, 0.329);
    padding: 30rpx 20rpx;
    justify-content: center;
}
.icon-box__ctn{
    flex-shrink: 100;
}
.icon-box__title{
    font-size: 20px;
}
.icon {
  width: 250rpx;
  height: 250rpx;
  margin-right: 30rpx
}

代码讲解

detail.js获取到chooseLesson.js传入的书本数据,利用微信同声传译插件提供的功能,调用wordToSpeak()函数实现文字转语音,并在该页面实现了上下切换和重复播放功能。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐