前言
在我们的开发中,可能需要编写一个答题模板,那这个文章简单地粗略地介绍了答题系统的主要功能和ui页面。
1.页面结构
一般来说,答题系统需要我们写编写三个页面,包括介绍页,答题页,结果页。
介绍页:一般有答题的介绍,题目数,题目内容等待。
答题页:一般有单选多选判断分类,题目数和当前题目序号,进度条,倒计时或者计时,题目标题和答案选项。
结果页:一般有对错题目数,正确率,用时,可能包括错题解析。
2.介绍页
<view class="intro_page" wx:if="{{progressFlag==1}}">
<view class="title">{{title}}</view>
<view class="intro_page__container">
<view>活动介绍</view>
</view>
<button class="around_btn theme_btn" style="width: 200rpx;" hover-class="btn_hover" bindtap="onChangeProgressFlag">进入答题</button>
</view>
这里将3个页面集合到了1个页面中,用progressFlag代表页面的进度。
title指的是答题系统的标题,比如说xxx届答题大赛。
onChangeProgressFlag函数就是修改progressFlag,并且获取答题题目。
onChangeProgressFlag() {
this.getQuestion()
this.setData({
progressFlag: 2,
// startTime: dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss')
})
可以在前端获取答题时间,也可以通过后端服务器时间保存到数据库中(此内容根据需求来定)
3.答题页
此部分为最难的部分,因为需要渲染题目和选项,这里通过跟后端协商来确定api数据的格式。
一般用对象数组表示。
const questionList=[
{id:123,questionType:1,question:'吴彦祖和郭富城谁帅?',optionArr=['郭富城','吴彦祖','博主本人']},
{id:234,questionType:2,question:'吴彦祖和郭富城谁帅?',optionArr=['郭富城','吴彦祖','博主本人']},
{id:345,questionType:3,question:'吴彦祖比郭富城帅?',optionArr=['是','不是']}
]
这里用id区分不同的问题,questionType代表答题的类型,1代表单选,2代表多选,3代表判断。
question代表答题的问题,optionArr代表选项。
let list = []
res.data.result.forEach(item => {
list.push({
id: item.id,
title: item.question,
answerList: item.optionArr,
answerType: item.questionType
})
})
this.setData({
questionList: list,
_maxStep: res.data.result.length
})
在getQuestion函数中设置如上,将api获取的数据保存到data对象中。
<view class="quiz_page" wx:if="{{progressFlag==2}}">
<!-- 答题页 -->
<view>
<view style="text-align: center;line-height: 100rpx;display: flex;justify-content: space-between;">
<view>
<text>{{step}}<text style="color:gray;font-weight: 500;">/{{_maxStep}}题</text></text>//这里是显示当前序号和总题数
</view>
<view>
<t-count-down auto-start format="mm:ss" time="{{time}}" splitWithUnit size="large" t-class="count-down" t-class-count="count-down__item" bind:finish="quizTimeOut" bind:change="onChangeCountdown"></t-count-down>
</view>//这里利用tdesign的count-down组件实现倒计时
</view>
<progress percent="{{step/_maxStep*100}}" activeColor="var(--theme-color)" style="margin:0 0 32rpx 0;" />//这里是进度条
<view>
//以下代码为题目的html代码
<view class="question">
<text style="margin-right: 16rpx;">[{{questionList[step-1].answerType==1?'单选题':(questionList[step-1].answerType==2?'多选题':'判断题')}}]</text>{{step}}.{{questionList[step-1].title}}
</view>
//此部分为单选渲染渲染
<view class="option" wx:if="{{questionList[step-1].answerType=='1'}}">
<view wx:for="{{questionList[step-1].answerList}}" wx:key="index" style="display: flex;align-items: center;margin: 8rpx 0;" data-value="{{index}}" bind:tap="onChangeButtonIndex">
<view class="option__orderNumber {{buttonIndex==index?'theme_btn':''}}">
{{_orderNumberList[index]}}</view>
<view class="{{buttonIndex==index?'option__selected':''}}" style="width: 600rpx;text-align: left;">{{item}}</view>
</view>
</view>
//此部分为多选选项渲染
<view class="option" wx:if="{{questionList[step-1].answerType=='2'}}">
<view wx:for="{{questionList[step-1].answerList}}" wx:key="index" style="display: flex;align-items: center;margin: 8rpx 0;" data-value="{{index}}" bind:tap="onChangeMultiButtonIndex">
<view class="option__orderNumber {{multiButtonIndex[index]==1?'theme_btn':''}}">
{{_orderNumberList[index]}}</view>
<view class="{{multiButtonIndex[index]==1?'option__selected':''}}" style="width: 600rpx;text-align: left;">{{item}}</view>
</view>
</view>
//此部分为判断渲染渲染
<view class="option" wx:if="{{questionList[step-1].answerType=='3'}}">
<view style="display: flex;align-items: center;margin: 8rpx 0;" data-value="1" bind:tap="onChangeTrueOrFalse">
<view class="option__orderNumber {{TFButtonIndex==1?'theme_btn':''}}">A</view>
<view class="{{TFButtonIndex==1?'option__selected':''}}" style="width: 600rpx;text-align: left;">正确</view>
</view>
<view style="display: flex;align-items: center;margin: 8rpx 0;" data-value="0" bind:tap="onChangeTrueOrFalse">
<view class="option__orderNumber {{TFButtonIndex==0?'theme_btn':''}}">B</view>
<view class="{{TFButtonIndex==0?'option__selected':''}}" style="width: 600rpx;text-align: left;">错误</view>
</view>
</view>
</view>
<view>
<button style="margin-bottom: 100rpx;" class="theme_btn" hover-class="btn_hover" bindtap="nextQuestionOrCommit">下一题</button>
</view>
</view>
</view>
这里通过{{questionList[step-1].answerType==1?'单选题':(questionList[step-1].answerType==2?'多选题':'判断题')}} 来显示题型。
wx:if="{{questionList[step-1].answerType=='1'}}"判断题目类型。
wx:for="{{questionList[step-1].answerList}}"渲染出选项。
buttonIndex、multiButtonIndex、TFButtonIndex都表示被点击的按钮索引,默认值为-1、{}、-1。
onChangeButtonIndex(e) {
let val = e.currentTarget.dataset.value
this.setData({
buttonIndex: val
})
},
onChangeMultiButtonIndex(e) {
let val = e.currentTarget.dataset.value
let list = this.data.multiButtonIndex
if (list[val] && list[val] == 1) {
list[val] = 0
} else {
list[val] = 1
}
this.setData({
multiButtonIndex: list
})
setTimeout(() => {
this.data.multiButtonIndex.indexOf(val)
}, 0);
},
onChangeTrueOrFalse(e) {
let val = e.currentTarget.dataset.value
this.setData({
TFButtonIndex: val
})
},
对于多选的button点击事件,这里是用对象的形式表示,键为0、1、2等等,值为0或1。
nextQuestionOrCommit() {
if (Object.keys(this.data.multiButtonIndex).length == 0 && this.data.buttonIndex == -1 && this.data.TFButtonIndex == -1) {
wx.showToast({
title: '必须选择选项后才可以进行下一题',
icon: 'none',
duration: 2000
})
return
}
if (this.data.buttonIndex != -1) {
let list = this.data.answerList
let obj = {}
obj.id = this.data.questionList[this.data.step - 1].id
obj.answer = String.fromCharCode(65 + Number(this.data.buttonIndex))
obj.seq = this.data.step
list.push(obj)
this.setData({
answerList: list
})
} else if (this.data.multiButtonIndex.length > 0) {
let list = this.data.answerList
let obj = {}
obj.id = this.data.questionList[this.data.step - 1].id
obj.seq = this.data.step
let str = ''
for (let i in this.data.multiButtonIndex) {
let word = String.fromCharCode(65 + Number(i))
str = str + word
}
obj.answer = str
list.push(obj)
this.setData({
answerList: list
})
} else if (this.data.TFButtonIndex != -1) {
let list = this.data.answerList
let obj = {}
obj.id = this.data.questionList[this.data.step - 1].id
obj.answer = Number(this.data.TFButtonIndex)
obj.seq = this.data.step
list.push(obj)
this.setData({
answerList: list
})
}
if (this.data.step < this.data._maxStep) {
this.setData({
buttonIndex: -1,
multiButtonIndex: [],
TFButtonIndex: -1,
step: this.data.step + 1
})
} else {
this.saveAnswer()
this.setData({
progressFlag: 3
})
}
// console.log(this.data.answerList);
},
这里先判断是否选择了选项,没选择择提示。
之后判断哪个index变化了,变了的index则将数据储存在一个list中,用于传递给后端。
如果step>maxstep(step为当前页面步数,每次下一步则+1,maxstep通过后端给的list.length得到),则进行提交api操作。
3.结果页
结果页一般是通过后端返回的数据渲染。
这里就不多赘述。
4.结语
希望在看完这边不太成熟的文章的观众们说一下谢谢,如果有问题可以直接在评论区发表评论。