一、功能需求
搭建一款消防安全培训答题小程序,大体上实现功能如下:
1.重要消防相关信息发布提醒;
2.培训课程库播放,文档的,加视频的;
3.题库、考试单选、多选、判断三类题 ;
4.考试成绩查询、输出表单 ;
5.单次培训:限时内完成,签到(手签名),限时内完成考试;
二、项目结构
使用微信开发者工具创建一个新的小程序项目,项目结构大致如下:
arduino
pages
├── index // 首页,显示重要信息提醒
├── course // 培训课程库页面
├── exam // 考试页面
├── result // 考试成绩查询页面
├── signin // 单次培训签到页面
三、关键代码
实现一个基本的消防安全培训答题小程序,包含重要信息发布、培训课程库、题库考试、成绩查询和单次培训签到等功能。以下是一个消防安全培训答题小程序的实现思路及部分代码:
app.json
json
{
"pages": [
"pages/index/index",
"pages/course/course",
"pages/exam/exam",
"pages/result/result",
"pages/signin/signin"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "消防安全培训答题小程序",
"navigationBarTextStyle": "black"
}
}
pages/index/index.wxml
ini
<view class="container">
<view class="notice" wx:for="{{notices}}" wx:key="*this">{{item}}</view>
<button bindtap="goToCourse">进入培训课程库</button>
<button bindtap="goToSignin">参加单次培训</button>
</view>
pages/index/index.js
css
Page({
data: {
notices: ["近期将开展消防安全培训,请及时参加!"]
},
goToCourse() {
wx.navigateTo({
url: '/pages/course/course'
});
},
goToSignin() {
wx.navigateTo({
url: '/pages/signin/signin'
});
}
});
pages/course/course.wxml
xml
<view class="container">
<view class="doc-item" wx:for="{{documents}}" wx:key="*this">
<text>{{item.title}}</text>
<button bindtap="openDoc" data-url="{{item.url}}">查看文档</button>
</view>
<video src="{{videoUrl}}" controls></video>
</view>
pages/course/course.js
php
Page({
data: {
documents: [
{ title: "消防知识手册", url: "https://example.com/fire_manual.pdf" },
{ title: "消防应急预案", url: "https://example.com/fire_plan.pdf" }
],
videoUrl: "https://example.com/fire_video.mp4"
},
openDoc(e) {
const url = e.currentTarget.dataset.url;
wx.downloadFile({
url: url,
success: function (res) {
const filePath = res.tempFilePath;
wx.openDocument({
filePath: filePath,
success: function (res) {
console.log('打开文档成功');
}
});
}
});
}
});
pages/exam/exam.wxml
xml
<view class="container">
<view wx:for="{{questions}}" wx:key="index">
<view>{{item.question}}</view>
<view wx:if="{{item.type === 'single'}}" wx:for="{{item.options}}" wx:key="*this">
<radio-group bindchange="onSingleAnswerChange" data-index="{{index}}">
<radio value="{{item}}">{{item}}</radio>
</radio-group>
</view>
<view wx:if="{{item.type === 'multiple'}}" wx:for="{{item.options}}" wx:key="*this">
<checkbox-group bindchange="onMultipleAnswerChange" data-index="{{index}}">
<checkbox value="{{item}}">{{item}}</checkbox>
</checkbox-group>
</view>
<view wx:if="{{item.type === 'judge'}}">
<radio-group bindchange="onJudgeAnswerChange" data-index="{{index}}">
<radio value="true">正确</radio>
<radio value="false">错误</radio>
</radio-group>
</view>
</view>
<button bindtap="submitExam">提交考试</button>
</view>
pages/exam/exam.js
ini
Page({
data: {
questions: [
{
question: "以下哪种灭火器适用于扑灭电器火灾?",
type: "single",
options: ["泡沫灭火器", "二氧化碳灭火器", "水基型灭火器"],
answer: "二氧化碳灭火器"
},
{
question: "消防设施包括以下哪些?",
type: "multiple",
options: ["灭火器", "消火栓", "应急照明"],
answer: ["灭火器", "消火栓", "应急照明"]
},
{
question: "火灾发生时,应尽快乘坐电梯逃生。",
type: "judge",
answer: "false"
}
],
userAnswers: []
},
onSingleAnswerChange(e) {
const index = e.currentTarget.dataset.index;
const answer = e.detail.value;
const userAnswers = this.data.userAnswers;
userAnswers[index] = answer;
this.setData({
userAnswers
});
},
onMultipleAnswerChange(e) {
const index = e.currentTarget.dataset.index;
const answer = e.detail.value;
const userAnswers = this.data.userAnswers;
userAnswers[index] = answer;
this.setData({
userAnswers
});
},
onJudgeAnswerChange(e) {
const index = e.currentTarget.dataset.index;
const answer = e.detail.value;
const userAnswers = this.data.userAnswers;
userAnswers[index] = answer;
this.setData({
userAnswers
});
},
submitExam() {
const { questions, userAnswers } = this.data;
let score = 0;
for (let i = 0; i < questions.length; i++) {
const question = questions[i];
const userAnswer = userAnswers[i];
if (Array.isArray(question.answer)) {
if (userAnswer.sort().toString() === question.answer.sort().toString()) {
score++;
}
} else {
if (userAnswer === question.answer) {
score++;
}
}
}
wx.setStorageSync('examScore', score);
wx.navigateTo({
url: '/pages/result/result'
});
}
});
pages/result/result.wxml
xml
<view class="container">
<view>您的考试成绩是:{{score}} 分</view>
<button bindtap="exportResult">导出成绩表单</button>
</view>
pages/result/result.js
javascript
Page({
data: {
score: 0
},
onLoad() {
const score = wx.getStorageSync('examScore');
this.setData({
score
});
},
exportResult() {
// 这里可以实现导出表单的逻辑,如生成 PDF 等
wx.showToast({
title: '导出功能待实现',
icon: 'none'
});
}
});
pages/signin/signin.wxml
ini
<view class="container">
<canvas id="signCanvas" style="width: 300px; height: 200px;"></canvas>
<button bindtap="saveSignature">保存签名</button>
<button bindtap="startExam">开始考试</button>
</view>
pages/signin/signin.js
javascript
Page({
data: {
signature: ''
},
onLoad() {
const ctx = wx.createCanvasContext('signCanvas');
this.setData({
ctx
});
},
saveSignature() {
const ctx = this.data.ctx;
ctx.draw(false, () => {
wx.canvasToTempFilePath({
canvasId: 'signCanvas',
success: (res) => {
const signature = res.tempFilePath;
this.setData({
signature
});
wx.showToast({
title: '签名保存成功',
icon: 'success'
});
}
});
});
},
startExam() {
if (this.data.signature) {
wx.navigateTo({
url: '/pages/exam/exam'
});
} else {
wx.showToast({
title: '请先完成签名',
icon: 'none'
});
}
}
});