1. 程式人生 > >微信小程式(看文件寫例項七)微信小程式課堂寶APP實現線上課堂測試

微信小程式(看文件寫例項七)微信小程式課堂寶APP實現線上課堂測試

接著上篇博文已經完成簽到功能,這篇來完成課堂測試功能。

一、需求描述

1、在後臺選擇題、主觀題表中上傳測試題

2、客戶端獲取題目資訊

3、把題目資訊格式化載入顯示

4、客戶端答題,主觀題每題能上傳一張答題圖片

5、客戶端答題結束提交到伺服器

二、前臺頁面

提交大量資料當然用表單了,程式碼並不多。wxml程式碼如下:

<view class='section' hidden='{{selectedScrollItemsHiddenSign[1]}}'>

<form bindsubmit="formSubmit">

<view class='section item-block' style='text-align: center;font-size=20px;'>{{onlineTestSeries}}</view>

<view class='section item-block'>選擇題</view>

<view class='section item-block' wx:for="{{onlineTestChooseItems}}" wx:for-item="i" wx:key="*unique">

<view class='choose-item-title'>{{i.number_id}}、{{i.title}}</view>

<radio-group class="radio-group" name="choose-radio-group{{i.number_id}}">

<label class="radio" wx:for="{{i.chooseItems}}" wx:key="*unique">

<radio value="{{item.name}}"/>

{{item.value}}

</label>

</radio-group>

</view>

<view class='section item-block'>簡答題</view>

<view class='section item-block' wx:for="{{onlineTestSelfQuestionItems}}" wx:key="*unique">

<view class='choose-item-title'>{{item.number_id}}、{{item.title}}</view>

<textarea class="answer-mainbody" name="answer-textarea{{item.number_id}}" placeholder="在這裡寫下您的答案..." type="textarea"/>

<view class="answer-img" name="answer-img{{item.number_id}}" id="{{index}}" bindtap='chooseAnswerImage'></view>

<text class='answer-img-text'>{{answerImgPaths[index].path}}</text>

</view>

<view class='section item-block'>

<button formType="submit" class='btn-commit'>提交</button>

</view>

</form>

</view>

wcss程式碼如下:

/* 每個題目塊樣式 */

.item-block{

margin: 2%;

background: rgb(232, 233, 232);

color: royalblue;

font-size: 15px;

padding: 4%;

border-radius: 3%;

width: 96%;

display: flex;

flex-direction: column;

}

/* 單選題radio-group樣式 */

.radio-group{

margin: 2%;

display: flex;

flex-direction: column;

}

/* 主觀題答題區 */

.answer-mainbody{

height: 150px;

margin: 2%;

padding: 6px;

font-family: monospace;

white-space: pre-wrap;

background: whitesmoke;

border-radius: 3%;

border: 1px solid rgb(214, 214, 214);

}

/* 主觀題圖片上傳 */

.answer-img{

margin: 1%;

background: url(http://i.pengxun.cn//content/images/imgpost2/01/post-big-pic-01.png) no-repeat 0 -286px;

background-size: 100%;

height: 26px;

width: 26px;

}

.answer-img-text{

height: 25px;

padding: 6px;

/* 超出一行文字自動隱藏 */

overflow:hidden;

/* 文字隱藏後新增省略號 */

text-overflow:ellipsis;

/* 強制不換行 */

white-space:nowrap;

}

.btn-commit{

background: royalblue;

color: whitesmoke;

}

三、後臺程式碼

1、初始data

data: {

// 選擇題題目資訊列表

onlineTestChooseItems:null,

//課堂測試試題系列

onlineTestSeries:null,

// 主觀題題目資訊列表

onlineTestSelfQuestionItems:null,

// 模擬測試試題系列

simulateTestSeries: null,

// 主觀題答題圖片路徑列表

answerImgPaths:[],

},

2、獲取題目資訊

/**

* 獲得課堂測試答題資訊

* id為scroll-view menu中已經載入完成的index

* testType為測試型別,可以是課堂測試、模擬測試或者其他,以伺服器中的資料為參照

*/

getPracticeItemsInfo:function(id,testType){

let query_choose = Bmob.Query('choose_item');

query_choose.order('number_id');

query_choose.equalTo("type", "==", testType);

query_choose.find().then(res => {

if(res.length>0){

if (testType=='課堂測試') that.setData({ onlineTestSeries:res[0].series});

else that.setData({ simulateTestSeries: res[0].series });

}

var choose_items = new Array();

for (var i = 0; i < res.length; i++) {

choose_items[i] = {

objectId: res[i].objectId,

number_id: res[i].number_id,

title: res[i].title,

chooseItems: [

{ name: 'a', value: res[i].choose_item_a },

{ name: 'b', value: res[i].choose_item_b },

{ name: 'c', value: res[i].choose_item_c },

{ name: 'd', value: res[i].choose_item_d }

]

}

}

let query_subjective = Bmob.Query('subjective_item');

query_subjective.order('number_id');

query_choose.equalTo("type", "==", testType);

query_subjective.find().then(res_subjective => {

var subjective_items = new Array();

for (var i = 0; i < res_subjective.length; i++) {

subjective_items[i] = {

objectId: res_subjective[i].objectId,

number_id: res_subjective[i].number_id,

title: res_subjective[i].title

};

}

that.data.selectedScrollItemsLoadingComplete[id] = true;

that.setData({ onlineTestChooseItems: choose_items, onlineTestSelfQuestionItems: subjective_items, selectedScrollItemsLoadingComplete: that.data.selectedScrollItemsLoadingComplete });

wx.hideToast();

}).catch(err => {

wx.hideToast();

console.log(err.msg);

wx.showToast({

title: '加載出錯',

duration: 2000

});

});

}).catch(err => {

wx.hideToast();

console.log(err);

wx.showToast({

title: '加載出錯',

duration: 2000

});

});

},

3、上傳主觀題圖片

先上傳主觀題圖片,然後把上傳好的url記錄下來,最後儲存到對應的主觀題目即可。檢視文件發現上傳圖片要用wx.api中的wx.chooseImage(Object object),object的引數如下表:

屬性 型別 預設值 是否必填 說明
count number 9 最多可以選擇的圖片張數
sizeType Array.<string> ['original', 'compressed'] 所選的圖片的尺寸
sourceType Array.<string> ['album', 'camera'] 選擇圖片的來源
success function   介面呼叫成功的回撥函式
fail function   介面呼叫失敗的回撥函式
complete function   介面呼叫結束的回撥函式(呼叫成功、失敗都會執行)

於是得到如下上傳程式碼:

// 主觀題選擇答題拍照或者圖片

chooseAnswerImage: function(e) {

wx.chooseImage({

count: 1, // 預設9

sizeType: ['original'], // 指定是原圖

sourceType: ['album', 'camera'], // 可以指定來源是相簿還是相機,預設二者都有

success: function(res) {

// 返回選定照片的本地檔案路徑列表,tempFilePath可以作為img標籤的src屬性顯示圖片

// 如果該題已經上傳答題圖片則先刪除原先的圖片

var currentAnswerImgPathsItem = that.data.answerImgPaths[e.currentTarget.id];

if (currentAnswerImgPathsItem != null) {

// 傳入string是單個檔案刪除,傳入array是批量刪除

var del = Bmob.File();

del.destroy([currentAnswerImgPathsItem.file.url]).then(res1 => {

that.uploadAnswerImg(e.currentTarget.id, res.tempFilePaths[0]);

}).catch(err => {

console.log(err);

wx.showToast({

title: '圖片上傳失敗',

duration: 2000

})

})

} else {

that.uploadAnswerImg(e.currentTarget.id, res.tempFilePaths[0]);

}

}

});

},

uploadAnswerImg: function(pathId, pathImg) {

// 上傳選中的圖片

var file = Bmob.File('subjectImg.jpg', pathImg);

wx.showToast({

title: '正在上傳',

icon: 'loading',

duration: 10000

});

file.save().then(res => {

wx.hideToast();

wx.showToast({

title: '圖片上傳成功',

duration: 2000

})

that.data.answerImgPaths[pathId] = {

path: pathImg,

file: res[0]

};

that.setData({

answerImgPaths: that.data.answerImgPaths

});

}).catch(err => {

console.log(err);

wx.showToast({

title: '圖片上傳失敗',

duration: 2000

})

});

},

4、提交答題資料

提交答案分選擇題和主觀題,主觀題還有對應的圖片,由於bmob中pointer型別不能直接新增,因此在新增一條含有pointer欄位的資料時,要先增其他的資訊成功後在回撥函式中再把對應的pointer型別資料插入。下面是上傳一個題目的程式碼:

/**

* 上傳單個題資訊

* value是題目資訊

* itemType是題目型別

*/

uploadAItemAnswersInfo: function(value, itemType) {

var query = Bmob.Query('choose_item_submit');

query.set("answer", value.answer);

if (value.file != null)

query.set("subjectiveImg", value.file);

// 先儲存answer和上傳成功的圖片資訊

query.save().then(res => {

query.get(res.objectId).then(res1 => {

// 儲存關聯物件

// 設定使用者關聯物件

var userIdPointer = Bmob.Pointer('_User');

var pointerUserId = userIdPointer.set(value.userId);

res1.set('userId', pointerUserId);

if (itemType == 'choose') {

// 設定選擇題關聯物件

var chooseItemIdPointer = Bmob.Pointer('choose_item'); //關聯欄位

var pointerIdChooseItemId = chooseItemIdPointer.set(value.chooseItemId);

res1.set('chooseItemId', pointerIdChooseItemId);

// 設定後儲存

res1.save();

} else {

// 設定主觀題關聯物件

var subjectItemIdPointer = Bmob.Pointer('subjective_item'); //關聯欄位

var pointerIdSubjectItemId = subjectItemIdPointer.set(value.subjectItemId);

res1.set('subjectiveItemId', pointerIdSubjectItemId);

res1.save();

}

return true;

});

}).catch(err => {

wx.showToast({

title: '上傳失敗',

duration: 2000

});

return false;

});

},

接下來是隻要把表單中的所有答題資訊遍歷,按型別上傳即可,不過再上傳結果判定時要確保每個題目都已經上傳,有一個題目上傳不成功就提示沒有提交成功。下面是上傳所有答題資料的程式碼:

/**

* 上傳所有答案資訊

* value是表單提交上來的資訊

*/

uploadAnswersInfo: function(value) {

// 顯示載入loading

wx.showToast({

title: '提交中...',

icon: 'loading',

duration: 1000

});

// 上傳題目結果列表

var allUploadSuccess = [];

// 選擇題索引

var chooseItemIndex = 0;

// 主觀題索引

var subjectiveItemIndex = 0;

// 遍歷submit提交的資料

for (var keyname in value)

if (value[keyname] != "") {

//console.log(value.keyname);

// 判斷是否是單選題

var data = {};

if (keyname.indexOf('choose-radio-group') != -1) {

data = {

userId: app.globalData.currentUser.objectId,

chooseItemId: that.data.onlineTestChooseItems[chooseItemIndex].objectId,

file: null,

answer: value[keyname]

}

allUploadSuccess[chooseItemIndex + subjectiveItemIndex] = that.uploadAItemAnswersInfo(data, 'choose');

chooseItemIndex++;

} else {

var fileName = null;

if (that.data.answerImgPaths[subjectiveItemIndex] != null) fileName = that.data.answerImgPaths[subjectiveItemIndex].file;

data = {

userId: app.globalData.currentUser.objectId,

subjectItemId: that.data.onlineTestSelfQuestionItems[subjectiveItemIndex].objectId,

file: fileName,

answer: value[keyname]

}

allUploadSuccess[chooseItemIndex + subjectiveItemIndex] = that.uploadAItemAnswersInfo(data, 'subjective');

subjectiveItemIndex++;

}

}

// 定時監聽所有返回結果

var checkResult = setInterval(function() {

if (allUploadSuccess.length == that.data.onlineTestChooseItems.length + that.data.onlineTestSelfQuestionItems.length) {

var allUploadSuccessFlag = true;

for (var i = 0; i < allUploadSuccess.length; i++) {

if (allUploadSuccess[i] == false) {

allUploadSuccessFlag = false;

if (i > that.data.onlineTestChooseItems.length)

wx.showToast({

title: '第' + (i + 1) + '選擇題上傳失敗',

duration: 1000

});

else {

wx.showToast({

title: '第' + (i + 1 - that.data.onlineTestChooseItems.length) + '主觀題上傳失敗',

duration: 1000

});

}

}

}

// 清除定時器

clearInterval(checkResult);

wx.hideToast();

// 所有題目都成功提交

if (allUploadSuccessFlag) {

// 上傳提交記錄

var query = Bmob.Query('submit_record');

query.set("type", '課堂測試');

query.set('series', that.data.onlineTestSeries);

query.save().then(res => {

query.get(res.objectId).then(res1 => {

var pointer = Bmob.Pointer('_User');

var poiID = pointer.set(app.globalData.currentUser.objectId);

res1.set('userId', poiID);

res1.save();

// 顯示載入logo

wx.showToast({

title: '提交成功',

duration: 3000

});

}).catch(err => {});

}).catch(err => {});

}

} else {

wx.showToast({

title: '提交中...',

icon: 'loading',

duration: 1000

});

}

}, 1000);

},

四、效果

1、伺服器題目資訊

選擇題資訊

主觀題資訊

2、獲取題目資訊載入

3、提交結果

然後課堂測試和模擬測試功能一樣,就是換個資料,因為課堂提問要實時推送,而bmob實時推送是收費的,這在後面做最後的完成,下節先實現練習模組的前臺。