上线项目演示

微信搜索[放马来答]或扫以下二维码体验:

项目源码

项目源码

其他版本

Vue答题App实战教程

Hello小程序

1.注册微信小程序

点击立即注册,选择微信小程序,按照要求填写信息

2.登录小程序并完善信息

填写小程序信息,完善信息。

3.下载小程序开发工具

完善信息后点击文档,工具,下载,选择稳定版的对应平台的安装包下载,下载完后点击安装即可



4.建立小程序项目

扫码登录,选择小程序,并点击加号,填写相关信息,APPID位置于下方截图所示。

5.小程序代码结构介绍

如下图所示的四个文件,主要用于注册和配置微信小程序,其包含的是全局配置信息。

  • app.js:用于注册微信小程序应用。

  • app.json:小程序的全局配置,比如网络请求的超时时间,以及窗口的属性

  • app.wxss:小程序全局样式

  • project.config.json:包含了小程序的整体配置信息,即使是换了开发设备,亦或是换了项目,只要将该文件保留,每个开发者的个性化设置就都将保留。

如下图所示,还有两个目录,

  • pages:每一个子文件夹代表了小程序的一个页面,比如index,和logs分别代表了两个页面。每个页面又由四个文件组成:

    index.js:处理页面逻辑和数据交互。

    index.json:对应页面的配置信息。

    index.wxml:展示页面的内容和元素。

    index.wxss:设置用wxml展示元素的样式。

  • utils:存放的是一些工具代码,实现代码复用的目的。

6.小程序helloworld

开发试题分类页面

新增home页面

pages目录下新建home目录,并添加4个文件,如图所示:

其中:

home.js

// pages/home/home.js
Page({
data: { },
onLoad: function (options) { },
toTestPage: function(e){
let testId = e.currentTarget.dataset['testid'];
wx.navigateTo({
url: '../test/test?testId='+testId
})
}
})

home.wxml

<!--pages/home/home.wxml-->
<view class="page">
<view class="page-title">请选择试题:</view>
<view class="flex-box">
<view class="flex-item"><view class="item bc_green" bindtap="toTestPage" data-testId="18">IT知识</view></view>
<view class="flex-item"><view class="item bc_red" bindtap="toTestPage" data-testId="15">游戏知识</view></view>
<view class="flex-item"><view class="item bc_yellow" bindtap="toTestPage" data-testId="21">体育知识</view></view>
<view class="flex-item"><view class="item bc_blue" bindtap="toTestPage" data-testId="27">动物知识</view></view>
<view class="flex-item item-last"><view class="item bc_green" bindtap="toTestPage" data-testId="0">综合知识</view></view>
</view>
</view>

home.json

{
"navigationBarTitleText": "试题分类",
"usingComponents": {}
}

home.wxss

/* pages/home/home.wxss */
.page-title {
padding-top: 20rpx;
padding-left: 40rpx;
font-size: 16px;
}
.flex-box {
display: flex;
align-items:center;
flex-wrap: wrap;
justify-content: space-between;
padding: 20rpx;
box-sizing:border-box;
}
.flex-item {
width: 50%;
height: 200rpx;
padding: 20rpx;
box-sizing:border-box;
}
.item {
width:100%;
height:100%;
border-radius:8rpx;
display: flex;
align-items:center;
justify-content: center;
color: #fff;
}
.item-last {
flex: 1;
}

修改app.json,注意:pages/home/home一定要放到pages数组的最前,以成为首页。

{
"pages": [
"pages/home/home",
"pages/index/index",
"pages/logs/logs",
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle": "black"
},
"style": "v2",
"sitemapLocation": "sitemap.json"
}

修改app.wxss,定义全局样式

/**app.wxss**/
.container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 200rpx 0;
box-sizing: border-box;
} .bc_green{
background-color: #09BB07;
}
.bc_red{
background-color: #F76260;
}
.bc_blue{
background-color: #10AEFF;
}
.bc_yellow{
background-color: #FFBE00;
}
.bc_gray{
background-color: #C9C9C9;
}

运行结果

开发试题展示功能

新增test页面

pages目录下新建test目录,并添加4个文件,如图所示:

修改test.js

// pages/test/test.js
Page({ /**
* 页面的初始数据
*/
data: {
test_id:0
}, /**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.setData({test_id:options.testId})
}
})

修改test.wxml

<!--pages/test/test.wxml-->
<text>我是{{test_id}}</text>

运行结果

在试题分类页点击某一分类,跳转到试题页,试题页显示分类id

Mock试题数据

项目目录下新增data目录,data目录新增json.js,存放我们的试题模拟数据。

var json = {
"18": [
{
"question": "This mobile OS held the largest market share in 2012.?",
"option": {
"A": "iOS",
"B": "Android",
"C": "BlackBerry",
"D": "Symbian"
},
"true": "A",
"scores": 10,
"checked": false
},
{
"question": "This mobile OS held the largest market share in 2012.?",
"option": {
"A": "iOS",
"B": "Android",
"C": "BlackBerry",
"D": "Symbian"
},
"true": "A",
"scores": 10,
"checked": false
}
],
"0": [
{
"question": "This mobile OS held the largest market share in 2012.?",
"option": {
"A": "iOS",
"B": "Android",
"C": "BlackBerry",
"D": "Symbian"
},
"true": "A",
"scores": 10,
"checked": false
},
{
"question": "This mobile OS held the largest market share in 2012.?",
"option": {
"A": "iOS",
"B": "Android",
"C": "BlackBerry",
"D": "Symbian"
},
"true": "A",
"scores": 10,
"checked": false
}
]
} module.exports = {
questionList: json
}

修改app.js

var jsonList = require('data/json.js');

App({
...
},
globalData: {
questionList: jsonList.questionList, // 拿到答题数据
// questionList:{},
quizCategory:{
"0": "综合知识",
"18": "IT知识",
"21": "体育知识",
"15": "游戏知识",
"27":"动物知识",
}
}
})

修改test.js

// import he from "he";
var app = getApp(); Page({
data: {
bIsReady: false, // 页面是否准备就绪
index: 0, // 题目序列
chooseValue: [], // 选择的答案序列
}, shuffle(array) {
return array.sort(() => Math.random() - 0.5);
},
/**
* 生成一个从 start 到 end 的连续数组
* @param start
* @param end
*/
generateArray: function (start, end) {
return Array.from(new Array(end + 1).keys()).slice(start)
}, onLoad: function (options) {
wx.setNavigationBarTitle({ title: options.testId }) // 动态设置导航条标题
this.setData({
questionList: app.globalData.questionList[options.testId], // 拿到答题数据
testId: options.testId // 课程ID
})
let countArr = this.generateArray(0, this.data.questionList.length - 1); // 生成题序
this.setData({
bIsReady: true,
shuffleIndex: this.shuffle(countArr).slice(0, countArr.length) // 生成随机题序 [2,0,3] 并截取num道题
})
},
/*
* 单选事件
*/
radioChange: function (e) {
console.log('checkbox发生change事件,携带value值为:', e.detail.value)
this.data.chooseValue[this.data.index] = e.detail.value;
console.log(this.data.chooseValue);
},
/*
* 退出答题 按钮
*/
outTest: function () {
wx.showModal({
title: '提示',
content: '你真的要退出答题吗?',
success(res) {
if (res.confirm) {
console.log('用户点击确定')
wx.switchTab({
url: '../home/home'
})
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
},
/*
* 下一题/提交 按钮
*/
nextSubmit: function () {
// 如果没有选择
if (this.data.chooseValue[this.data.index] == undefined) {
wx.showToast({
title: '请选择至少一个答案!',
icon: 'none',
duration: 2000,
success: function () {
return;
}
})
return;
} // 判断是不是最后一题
if (this.data.index < this.data.shuffleIndex.length - 1) {
// 渲染下一题
this.setData({
index: this.data.index + 1
})
} else {
console.log('ok')
}
}
})

test.wxml

<!--pages/test/test.wxml-->
<view wx:if="{{bIsReady}}" class="page">
<!--标题-->
<view class='page__hd'>
<view class="page__title">
{{index+1}}、{{questionList[shuffleIndex[index]].question}}
({{questionList[shuffleIndex[index]].scores}}分)
</view>
</view>
<!--内容-->
<view class="page__bd"> <radio-group class="radio-group" bindchange="radioChange">
<label class="radio my-choosebox" wx:for="{{questionList[shuffleIndex[index]].option}}" wx:for-index="key" wx:for-item="value">
<radio value="{{key}}" checked="{{questionList[shuffleIndex[index]].checked}}"/>{{key}}、{{value}}
</label>
</radio-group> </view>
<!--按钮-->
<view class='page_ft'>
<view class='mybutton'>
<button bindtap='nextSubmit' wx:if="{{index == questionList.length-1}}">提交</button>
<button bindtap='nextSubmit' wx:else>下一题</button>
<text bindtap='outTest' class="toindex-btn">退出答题</text>
</view>
</view>
</view>

test.wxss

/* pages/test/test.wxss */
.page {
padding: 20rpx;
}
.page__bd {
padding: 20rpx;
}
.my-choosebox {
display: block;
margin-bottom: 20rpx;
}
.toindex-btn {
margin-top: 20rpx;
display:inline-block;
line-height:2.3;
font-size:13px;
padding:0 1.34em;
color:#576b95;
text-decoration:underline;
float: right;
}

项目运行结果:

请求真实数据

修改test.js

  getQuestions(testId) {
// 显示标题栏加载效果
wx.showNavigationBarLoading();
wx.request({
// url: 'https://opentdb.com/api.php?amount=10&difficulty=easy&type=multiple&category=' + testId,
url: 'https://opentdb.com/api.php?amount=10&difficulty=easy&type=multiple&category=' + testId,
method: "GET",
success: res => {
if (res.data.response_code === 0) {
this.setData({
questionList: this.parseQuestion(res.data.results), // 拿到答题数据
testId: testId // 课程ID
})
console.log(this.data.questionList);
app.globalData.questionList[testId] = this.data.questionList
let count = this.generateArray(0, this.data.questionList.length - 1); // 生成题序 this.setData({
bIsReady: true,
shuffleIndex: this.shuffle(count).slice(0, 10) // 生成随机题序 [2,0,3] 并截取num道题
})
} else {
;
}
// 停止加载效果
wx.stopPullDownRefresh();
wx.hideNavigationBarLoading();
},
fail: err => {
// 停止加载效果
wx.stopPullDownRefresh();
wx.hideNavigationBarLoading();
}
}); }, onLoad: function (options) {
this.getQuestions(options.testId)
console.log(options); wx.setNavigationBarTitle({ title: app.globalData.quizCategory[options.testId] }) // 动态设置导航条标题
},

解析返回的数据:

   // 主题列表数据模型
parseQuestion(aList) {
let aTopicList = [];
if (!aList || (aList && !Array.isArray(aList))) {
aList = [];
} aTopicList = aList.map(oItem => { const answers = [oItem.correct_answer, oItem.incorrect_answers].flat()
let optionArr = ['A', 'B', 'C', 'D']
let options = {}
let optionArrShuffle = this.shuffle(optionArr)
for (let i = 0; i < answers.length; i++) {
options[optionArr[i]] = String(answers[i]);
}
const ordered_options = {};
Object.keys(options).sort().forEach(function (key) {
ordered_options[key] = options[key];
});
return {
"question": String(oItem.question), // id
"scores": 10,
"checked": false,
"option": ordered_options,
"true": optionArr[0]
};
});
return aTopicList; },

这里解析的原因是,接口返回的json数据和我们自己设计的数据格式略有不同,我们要转换成自己的数据格式:

接口返回的数据格式:

{
"response_code": 0,
"results": [{
"category": "Science: Computers",
"type": "multiple",
"difficulty": "easy",
"question": "The numbering system with a radix of 16 is more commonly referred to as ",
"correct_answer": "Hexidecimal",
"incorrect_answers": ["Binary", "Duodecimal", "Octal"]
}, {
"category": "Science: Computers",
"type": "multiple",
"difficulty": "easy",
"question": "This mobile OS held the largest market share in 2012.",
"correct_answer": "iOS",
"incorrect_answers": ["Android", "BlackBerry", "Symbian"]
}, {
"category": "Science: Computers",
"type": "multiple",
"difficulty": "easy",
"question": "How many values can a single byte represent?",
"correct_answer": "256",
"incorrect_answers": ["8", "1", "1024"]
}, {
"category": "Science: Computers",
"type": "multiple",
"difficulty": "easy",
"question": "In computing, what does MIDI stand for?",
"correct_answer": "Musical Instrument Digital Interface",
"incorrect_answers": ["Musical Interface of Digital Instruments", "Modular Interface of Digital Instruments", "Musical Instrument Data Interface"]
}, {
"category": "Science: Computers",
"type": "multiple",
"difficulty": "easy",
"question": "In computing, what does LAN stand for?",
"correct_answer": "Local Area Network",
"incorrect_answers": ["Long Antenna Node", "Light Access Node", "Land Address Navigation"]
}]
}

我们自己的数据格式:

var json = {
"18": [
{
"question": "This mobile OS held the largest market share in 2012.?",
"option": {
"A": "iOS",
"B": "Android",
"C": "BlackBerry",
"D": "Symbian"
},
"true": "A",
"scores": 10,
"checked": false
},
{
"question": "This mobile OS held the largest market share in 2012.?",
"option": {
"A": "iOS",
"B": "Android",
"C": "BlackBerry",
"D": "Symbian"
},
"true": "A",
"scores": 10,
"checked": false
}
],
"0": [
{
"question": "This mobile OS held the largest market share in 2012.?",
"option": {
"A": "iOS",
"B": "Android",
"C": "BlackBerry",
"D": "Symbian"
},
"true": "A",
"scores": 10,
"checked": false
},
{
"question": "This mobile OS held the largest market share in 2012.?",
"option": {
"A": "iOS",
"B": "Android",
"C": "BlackBerry",
"D": "Symbian"
},
"true": "A",
"scores": 10,
"checked": false
}
]
}

注意:

开发期间:不校验合法域名,web-view.....这里不要勾选。

引入第三方库

细心的朋友可能会发现,有些题目中有乱码,如下图所示'

有一个很好的第三方库He可以处理这个问题。

我们需要使用npm导入一个第三方库处理这个问题,大家会学习到在小程序开发中如何使用npm引入第三方库。

项目根目录下新建package.json文件

{
"name": "wechatanswer-master",
"version": "1.0.0",
"description": "答题类微信小程序",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://gitee.com/kamiba/my_quiz_wechat_app.git"
},
"author": "",
"license": "ISC",
"dependencies": {
"he": "^1.2.0"
}
}

2、执行npm install --production xxx,这个xxx就是你想使用的npm 包。此时在当前文件夹下会生成一个node_modules文件夹。PS:此处请务必使用--production选项,可以减少安装一些业务无关的 npm 包,从而减少整个小程序包的大小。

npm install --production he

3、在微信开发者工具-->工具-->构建npm,此时会生成一个miniprogram_npm文件夹。

4、构建完成后就可以使用 npm 包了。首先把使用npm模块勾起来,然后在js文件中引入即可。

然后修改test.js

import he from "he";

  // 主题列表数据模型
parseQuestion(aList) {
let aTopicList = [];
if (!aList || (aList && !Array.isArray(aList))) {
aList = [];
} aTopicList = aList.map(oItem => { const answers = [oItem.correct_answer, oItem.incorrect_answers].flat()
let optionArr = ['A', 'B', 'C', 'D']
let options = {}
let optionArrShuffle = this.shuffle(optionArr)
for (let i = 0; i < answers.length; i++) {
options[optionArr[i]] = he.decode(String(answers[i]));
}
const ordered_options = {};
Object.keys(options).sort().forEach(function (key) {
ordered_options[key] = options[key];
});
return {
"question": he.decode(String(oItem.question)), // id
"scores": 10,
"checked": false,
"option": ordered_options,
"true": optionArr[0]
};
});
return aTopicList; },

上面和以前有三处修改

import he from "he";
options[optionArr[i]] = he.decode(String(answers[i]));
"question": he.decode(String(oItem.question)),

计算用户得分,记录错题

修改test.js

Page({
data: {
bIsReady: false, // 页面是否准备就绪
index: 0, // 题目序列
chooseValue: [], // 选择的答案序列
totalScore: 100, // 总分
wrong: 0, // 错误的题目数量
wrongListSort: [], // 错误的题目集合
},
 /*
* 下一题/提交 按钮
*/
nextSubmit: function () {
// 如果没有选择
if (this.data.chooseValue[this.data.index] == undefined) {
wx.showToast({
title: '请选择至少一个答案!',
icon: 'none',
duration: 2000,
success: function () {
return;
}
})
return;
}
// 判断答案是否正确
this.chooseError(); // 判断是不是最后一题
if (this.data.index < this.data.shuffleIndex.length - 1) {
// 渲染下一题
this.setData({
index: this.data.index + 1
})
} else {
let wrongListSort = JSON.stringify(this.data.wrongListSort);
let chooseValue = JSON.stringify(this.data.chooseValue);
let shuffleIndex = JSON.stringify(this.data.shuffleIndex);
console.log('wrongListSort:' + wrongListSort)
wx.navigateTo({
url: '../results/results?totalScore=' + this.data.totalScore + '&shuffleIndex=' + shuffleIndex + '&chooseValue=' + chooseValue + '&wrongListSort=' + wrongListSort + '&testId=' + this.data.testId
})
// 设置缓存 var logs = wx.getStorageSync('logs') || [] let logsList = { "date": Date.now(), "testId": app.globalData.quizCategory[this.data.testId], "score": this.data.totalScore }
logs.unshift(logsList);
wx.setStorageSync('logs', logs);
}
},
/*
* 错题处理
*/
chooseError: function () {
var trueValue = this.data.questionList[this.data.shuffleIndex[this.data.index]]['true'];
var chooseVal = this.data.chooseValue[this.data.index];
console.log('选择了' + chooseVal + '答案是' + trueValue);
if (chooseVal.toString() != trueValue.toString()) {
console.log('错了');
this.data.wrong++;
this.data.wrongListSort.push(this.data.index);
this.setData({
totalScore: this.data.totalScore - this.data.questionList[this.data.shuffleIndex[this.data.index]]['scores'] // 扣分操作
})
}
},

实现结果展示页面

pages目录下新增page---results

results.js

// pages/results/results.js
var app = getApp();
Page({
data: {
totalScore: null, // 分数
shuffleIndex: [], // 错误的题数-乱序
wrongListSort: [], // 错误的题数-正序
chooseValue: [], // 选择的答案
remark: ["好极了!你很棒棒哦", "哎哟不错哦", "别灰心,继续努力哦!"], // 评语
modalShow: false
},
onLoad: function (options) {
console.log(options);
wx.setNavigationBarTitle({ title: app.globalData.quizCategory[options.testId] }) // 动态设置导航条标题 let shuffleIndex = JSON.parse(options.shuffleIndex);
let wrongListSort = JSON.parse(options.wrongListSort);
let chooseValue = JSON.parse(options.chooseValue);
this.setData({
totalScore: options.totalScore != "" ? options.totalScore : "无",
shuffleIndex: shuffleIndex,
wrongListSort: wrongListSort,
chooseValue: chooseValue,
questionList: app.globalData.questionList[options.testId], // 拿到答题数据
testId: options.testId // 课程ID
})
console.log(this.data.chooseValue);
},
// 查看错题
toView: function () {
// 显示弹窗
this.setData({
modalShow: true
})
},
// 返回首页
toIndex: function () {
wx.switchTab({
url: '../home/home'
})
}
})

results.json

{
"navigationBarTitleText": "WeChatTest",
"usingComponents": {
"wrong-modal":"/components/wrongModal/wrongModal"
}
}

results.wxml

<view class="page">
<!--标题-->
<view class='page-head'>
<view class="page-title">
答题结束!您的得分为:
</view>
<!--分数-->
<view class='page-score'>
<text class="score-num">{{totalScore}}</text>
<text class="score-text">分</text>
</view>
<text class="score-remark">{{totalScore==100?remark[0]:(totalScore>=80?remark[1]:remark[2])}}</text> <!-- 评价 -->
</view>
<!--查询错误-->
<view class='page-footer'>
<view class="wrong-view" wx:if="{{wrongListSort.length > 0}}">
<text>错误的题数:</text>
<text wx:for="{{wrongListSort}}">[{{item-0+1}}]</text> 题
</view>
<view class="wrong-btns">
<button type="default" bindtap="toView" hover-class="other-button-hover" class="wrong-btn" wx:if="{{wrongListSort.length > 0}}"> 点击查看 </button>
<button type="default" bindtap="toIndex" hover-class="other-button-hover" class="wrong-btn"> 返回首页 </button>
</view>
</view>
<wrong-modal modalShow="{{modalShow}}" shuffleIndex="{{shuffleIndex}}" wrongListSort="{{wrongListSort}}" chooseValue="{{chooseValue}}" questionList="{{questionList}}" testId="{{testId}}"
></wrong-modal>
</view>

results.wxss

/* pages/results/results.wxss */
.page {
padding: 20rpx;
}
.page-head {
text-align: center;
}
.page-title { }
.page-score {
display: flex;
justify-content: center;
align-items: flex-end;
padding-top:40rpx;
padding-bottom:40rpx;
}
.score-num {
font-size:100rpx;
}
.page-footer {
margin-top:60rpx;
text-align: center;
}
.wrong-btns {
display:flex;
align-items:center;
justify-content:center;
margin-top: 60rpx;
}
.wrong-btn {
margin-left:20rpx;
margin-right:20rpx;
height:70rpx;
line-height:70rpx;
font-size:14px;
}

实现错题查看页面

项目目录下新增components目录。components目录下新增wrongModal目录,wrongModal目录下新增page---wrongModal

wrongModal.js

// components/wrongModal/wrongModal.js
Component({
/**
* 组件的属性列表
*/
properties: {
// 是否显示
modalShow: {
type: Boolean,
value: false
},
// 题库
questionList: {
type: Array,
value: []
},
// 课程ID
testId: {
type: String,
value: '101-1'
},
// 题库乱序index
shuffleIndex: {
type: Array,
value: []
}, // 错题题数-正序
wrongListSort: {
type: Array,
value: []
},
// 选择的答案
chooseValue: {
type: Array,
value: []
}
},
/**
* 组件的初始数据
*/
data: {
index: 0 // wrongList的index
},
/**
* 组件所在页面的生命周期
*/
pageLifetimes: {
show: function () {
// 页面被展示
console.log('show')
console.log(this.data.questionList)
// console.log(this.data.wrongList)
},
hide: function () {
// 页面被隐藏
},
resize: function (size) {
// 页面尺寸变化
}
},
/**
* 组件的方法列表
*/
methods: {
// 下一题
next: function(){
if (this.data.index < this.data.wrongListSort.length - 1){
// 渲染下一题
this.setData({
index: this.data.index + 1
})
}
},
// 关闭弹窗
closeModal: function(){
this.setData({
modalShow: false
})
},
// 再来一次
again: function(){
wx.reLaunch({
url: '../test/test?testId=' + this.data.testId
})
},
// 返回首页
toIndex: function () {
wx.reLaunch({
url: '../home/home'
})
},
}
})

wrongModal.json

{
"component": true,
"usingComponents": {}
}

wrongModal.wxml

<!--components/wrongModal/wrongModal.wxml-->
<view class="modal-page" wx:if="{{modalShow}}">
<view class="modal-mask" bindtap="closeModal"></view>
<!-- 内容 -->
<view class="modal-content">
<view class="modal-title">
题目: {{questionList[shuffleIndex[wrongListSort[index]]].question}}
</view>
<view class="modal-body">
<radio-group class="radio-group" bindchange="radioChange">
<label class="radio my-choosebox" wx:for="{{questionList[shuffleIndex[wrongListSort[index]]].option}}" wx:for-index="key" wx:for-item="value">
<radio disabled="{{true}}" value="{{key}}" checked="{{questionList[shuffleIndex[wrongListSort[index]]].checked}}"/>{{key}}、{{value}}
</label>
</radio-group>
</view>
<!-- 答案解析 -->
<view class="modal-answer">
<text class="answer-text wrong-answer">
您的答案为 {{chooseValue[wrongListSort[index]]}}
</text>
<text class="answer-text true-answer">
正确答案为 {{questionList[shuffleIndex[wrongListSort[index]]]['true']}}
</text>
</view>
<!-- 操作按钮 -->
<view class="modal-button">
<view wx:if="{{index == wrongListSort.length-1}}" class="modal-btns">
<button bindtap='again' class="modal-btn">再来一次</button>
<button bindtap='toIndex' class="modal-btn">返回首页</button>
</view>
<button bindtap='next' wx:else class="modal-btn">下一题</button>
</view>
</view>
</view>

wrongModal.wxss

/* components/wrongModal/wrongModal.wxss */
.modal-mask {
position:fixed;
width:100%;
height:100%;
top:0;
left:0;
z-index:10;
background: #000;
opacity: 0.5;
overflow: hidden;
}
.modal-page {
display:flex;
align-items:center;
justify-content:center;
width:100%;
height:100%;
top:0;
position:absolute;
left:0;
}
.modal-content {
width: 80%;
min-height: 80%;
background: #fff;
border-radius: 8rpx;
z-index:11;
padding: 20rpx;
}
.modal-title {
font-size: 14px;
}
.modal-body {
padding: 20rpx;
}
.my-choosebox {
display: block;
margin-bottom: 20rpx;
font-size: 14px;
}
.modal-answer {
display: flex;
}
.answer-text {
font-size: 14px;
margin-right: 20rpx;
}
.modal-button {
display: flex;
align-items:center;
justify-content:center;
margin-top:60rpx;
}
.modal-btns {
display: flex;
align-items:center;
justify-content:center;
}
.modal-btn {
margin-left:20rpx;
margin-right:20rpx;
height:70rpx;
line-height:70rpx;
font-size:12px;
}

实现成绩记录页面

修改项目目录下的app.json,增加底部导航栏

{
"pages": [
"pages/home/home",
"pages/logs/logs",
"pages/test/test",
"pages/results/results",
"pages/index/index"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle": "black"
},
"tabBar": {
"color": "#666666",
"selectedColor": "#3cc51f",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "pages/home/home",
"iconPath": "image/icon_component.png",
"selectedIconPath": "image/icon_component_HL.png",
"text": "答题"
},
{
"pagePath": "pages/logs/logs",
"iconPath": "image/icon_API.png",
"selectedIconPath": "image/icon_API_HL.png",
"text": "记录"
}
]
},
"sitemapLocation": "sitemap.json"
}

项目目录下新增image文件夹,并添加以下文件,具体文件请下载源码获取:

修改pages目录下logs页面

logs.js

//logs.js
const util = require('../../utils/util.js') Page({
data: {
logs: [],
},
onShow: function() {
this.setData({
logs: this.formatLogs()
})
},
// 拿到缓存并格式化日期数据
formatLogs: function(){
let newList = [];
(wx.getStorageSync('logs') || []).forEach(log => {
if(log.date){
log['date'] = util.formatTime(new Date(log.date));
newList.push(log);
}
})
return newList;
}
})

logs.json

{
"navigationBarTitleText": "查看日志",
"usingComponents": {}
}

logs.wxml

<!--logs.wxml-->
<view class="page">
<view class="table" wx:if="{{logs.length>0}}">
<view class="tr bg-w">
<view class="th first">时间</view>
<view class="th">试题</view>
<view class="th ">得分</view>
</view>
<block wx:for="{{logs}}" wx:for-item="item">
<view class="tr">
<view class="td first">{{item.date}}</view>
<view class="td">{{item.testId}}</view>
<view class="td">{{item.score}}</view>
</view>
</block>
</view>
<view class="no-record" wx:else>
<image src="/image/wechat.png" class="no-image"></image>
<text class="no-text">没有数据哦~</text>
</view>
</view>

logs.wxss

.table {
border: 0px solid darkgray;
font-size: 12px;
}
.tr {
display: flex;
width: 100%;
justify-content: center;
height: 2rem;
align-items: center;
}
.td {
width:40%;
justify-content: center;
text-align: center;
}
.bg-w{
background: snow;
}
.th {
width: 40%;
justify-content: center;
background: #3366FF;
color: #fff;
display: flex;
height: 2rem;
align-items: center;
}
.first {
flex:1 0 auto;
}
.no-record {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.no-image {
width: 200rpx;
height: 200rpx;
margin-top: 200rpx;
margin-bottom: 40rpx;
}
.no-text {
font-size: 16px;
color: #ccc;
display: block;
}

修改test.js

  /*
* 下一题/提交 按钮
*/
nextSubmit: function () {
// 如果没有选择
.....
// 设置缓存 var logs = wx.getStorageSync('logs') || [] let logsList = { "date": Date.now(), "testId": app.globalData.quizCategory[this.data.testId], "score": this.data.totalScore }
logs.unshift(logsList);
wx.setStorageSync('logs', logs);
}
},

最终项目结构

小程序部署上线

租用服务器,申请域名、搭建Linux环境并配置https服务

发布体验版

这里的体验版发布阶段有点类似于我们日常的灰度发布前的内部灰度测试,可以指定白名单用户进行生产测试体验。发布的动作其实很简单,就是在微信的开发IDE中上面工具栏上点击上传按钮即可发布到微信服务器,提交后就可以在mp管理端查看到新的开发版本,可以发布二维码白名单用户扫码后进行体验。

上线审核

体验版本验证没问题后就可以发布,点击开发版本右边的提交审核按钮就可以发布到腾讯进行小程序审核,第一次发布审核时间会比较长,大约3-5个工作日左右,日后的升级版本审核就很快了,基本上可以做到半天就审核通过。

上线

审核通过后就会出现在审核版本的栏位,点击右边的发布即可,发布后监控一段后台服务情况,如果发现问题可以紧急回退版本,小程序mp管理端也提供了前端版本回退的功能。

总体来说小程序在版本管理、发布回退的体验方面做得还是很好的,能够满足基本需求,不需要额外的开发。

项目总结

很感谢您和豆约翰走到了这里,至此我们这个知识测验微信小程序,全部开发完毕。如果对于代码或小程序上线有任何疑问,可以加我微信[tiantiancode]一起讨论。

最后

如果您觉得豆约翰的文章对您有所帮助,另外不想错过豆约翰未来更多更好的技术实战教程,还请

从0开始,手把手教你开发并部署上线一个知识测验微信小程序的更多相关文章

  1. 【云开发】10分钟零基础学会做一个快递查询微信小程序,快速掌握微信小程序开发技能(轮播图、API请求)

    大家好,我叫小秃僧 这次分享的是10分钟零基础学会做一个快递查询微信小程序,快速掌握开发微信小程序技能. 这篇文章偏基础,特别适合还没有开发过微信小程序的童鞋,一些概念和逻辑我会讲细一点,尽可能用图说 ...

  2. 小程序语音红包开发中 汉字转拼音的问题 微信小程序红包开发遇到的坑

    公司最近在开发微信小程序的红包功能,语音红包需要用到文字转拼音的功能. 之前介绍过怎么将中文的汉字转为拼音的,具体看下面这篇文章. 微信语音红包小程序开发如何提高精准度 红包小程序语音识别精准度 微信 ...

  3. 两天快速开发一个自己的微信小程序

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "Songti SC" } p.p2 { margin: 0.0px 0. ...

  4. 快速开发一个自己的微信小程序

    一.写在前面 1.为什么要学小程序开发? 对于前端开发而言,微信小程序因为其简单快速.开发成本低.用户流量巨大等特点,也就成了前端开发工程师必会的一个技能. 2.先看看小程序效果 (1)欢迎页 (2) ...

  5. 如何开发一款堪比APP的微信小程序(腾讯内部团队分享)

    一夜之间,微信小程序刷爆了行业网站和朋友圈,小程序真的能如张小龙所说让用户"即用即走"吗? 其功能能和动辄几十兆安装文件的APP相比吗? 开发小程序,是不是意味着移动应用开发的一次 ...

  6. 1-微信小程序开发(安装软件和运行第一个微信小程序)

    https://developers.weixin.qq.com/miniprogram/dev/ 我的 打开 上传成功后

  7. 微信小程序开发环境搭建

    关注,QQ群,微信应用号社区 511389428 微信小程序可谓是今天最火的一个名词了,一经出现真是轰炸了整个开发人员,当然很多App开发人员有了一个担心,微信小程序的到来会不会给移动端App带来一个 ...

  8. 微信小程序开发心得

    微信小程序也已出来有一段时间了,最近写了几款微信小程序项目,今天来说说感受. 首先开发一款微信小程序,最主要的就是针对于公司来运营的,因为,在申请appid(微信小程序ID号)时候,需要填写相关的公司 ...

  9. 微信小程序开发心得--动画机制

    微信小程序也已出来有一段时间了,最近写了几款微信小程序项目,今天来说说感受.首先开发一款微信小程序,最主要的就是针对于公司来运营的,因为,在申请appid(微信小程序ID号)时候,需要填写相关的公司认 ...

随机推荐

  1. 使用vw进行移动端适配(nuxt项目)

    基于nuxt 2.0.0 一.安装postcss-px-to-viewport npm安装 npm install postcss-px-to-viewport --save-dev 或 yarn安装 ...

  2. Go语言圣经[中文版]

    近期整理了一篇Go语言圣经[中文版]在线版本,排版比较适合手机以及PC阅读. Go语言圣经[中文版本]

  3. 如何利用Excel设计一个唱票统计系统?

    具体操作如下: 首先需要一个如下的数据结构. 唱票数G列区域,不能手动输入候选人票数,这样很不方便,所以我们需要一个窗体控件,用点击鼠标的方法来实现唱票.在“开发工具-插入-数值调节钮”下图3处,然后 ...

  4. Web测试和app测试区别?

    EB测试和APP测试从流程上来说,没有区别.都需要经历测试计划方案,用例设计,测试执行,缺陷管理,测试报告等相关活动.从技术上来说,WEB测试和APP测试其测试类型也基本相似,都需要进行功能测试,性能 ...

  5. Java容器面试总结

    1.List,Set,Map三者的区别? List:用于存储一个有序元素的集合. Set:用于存储一组不重复的元素. Map:使用键值对存储.Map会维护与Key有关联的值.两个Key可以引用相同的对 ...

  6. Python在Linux下编译安装

    [准备环境] Linux centos [前言] 1 linux下默认带Python,带的是2.7版本的 ,如果需要升级版本,需要把系统的自带的Python改名或者卸载,再次安装你所需要的Python ...

  7. 有趣的程序分析之C

    1. 下面的函数被用来计算某个整数的平方,它能实现预期设计目标吗?如果不能,试回答存在什么问题: 1 2 3 4 int square( volatile int *ptr ) {     retur ...

  8. 01 . ELK Stack简介原理及部署应用

    简介 ELK并不是一款软件,是一整套解决方案,是由ElasticSearch,Logstash和Kibana三个开源工具组成:通常是配合使用,而且先后归于Elastic.co公司名下,简称ELK协议栈 ...

  9. 代码规范与计划(Beta阶段)

    这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 团队名称 WeChair 这个作业要求在哪里 Beta冲刺 这个作业的目标 代码规范与计划 作业正文 如下 其他参考文献 代码规 ...

  10. Axis WSDD文件参考文档

    本部分介绍的所有的WSDD元素的名称空间都是"http://xml.apache.org/axis/wsdd/". <deployment> 告诉Axis Engine ...