微信小程序实训|基于云数据库的语文听写工具
Posted TiAmo zhang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微信小程序实训|基于云数据库的语文听写工具相关的知识,希望对你有一定的参考价值。
本实训项目结合云开发的云数据库和 “微信同声传译”插件,制作一个可真实运营的小学生语文听写工具,页面效果如图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*90px'>
<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+135rpx;' 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()函数实现文字转语音,并在该页面实现了上下切换和重复播放功能。
以上是关于微信小程序实训|基于云数据库的语文听写工具的主要内容,如果未能解决你的问题,请参考以下文章