Then the last blog has completed the check-in function , This is to complete the classroom test function .

One , Requirement description

1, Multiple choice questions in the background , Upload test questions from subjective question list

2, Client access to subject information

3, Format and load the title information

4, Client answers , Each subjective question can upload an answer picture

5, The client answers the question and submits it to the server

Two , Front page

Submit a lot of data using forms, of course , Not much code .wxml The code is as follows :


<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'> choice question </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'> Short answer questions </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=" Write down your answers here ..." 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'> Submit </button>

</view>

</form>

</view>

wcss The code is as follows :


/* Style of each topic block */

.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;

}

/* Single choice questions radio-group style */

.radio-group{

margin: 2%;

display: flex;

flex-direction: column;

}

/* Subjective question answering area */

.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);

}

/* Subjective question picture upload */

.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;

/* Automatically hide more than one line of text */

overflow:hidden;

/* Add ellipsis after text hiding */

text-overflow:ellipsis;

/* Force no line breaks */

white-space:nowrap;

}

.btn-commit{

background: royalblue;

color: whitesmoke;

}

Three , Background code

1, initial data


data: {

// Information list of multiple choice questions

onlineTestChooseItems:null,

// Class test questions series

onlineTestSeries:null,

// Subject information list

onlineTestSelfQuestionItems:null,

// Series of simulated test questions

simulateTestSeries: null,

// Subjective question answer picture path list

answerImgPaths:[],

},

2, Get topic information


/**

* Get the information of classroom test answers

* id by scroll-view menu Has been loaded index

* testType Is the test type , It could be a classroom test , Simulation test or something , Refer to the data in the server

*/

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==' Classroom tests ') 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: ' Loading error ',

duration: 2000

});

});

}).catch(err => {

wx.hideToast();

console.log(err);

wx.showToast({

title: ' Loading error ',

duration: 2000

});

});

},

3, Upload pictures of subjective questions


Upload subjective question picture first , Then upload the good ones url Write it down , Finally, save it to the corresponding subjective topic . Check the document and find that the uploaded image needs to use wx.api Medium wx.chooseImage(Object
object),object The parameters of are shown in the table below :

attribute type Default value Is it required explain
count number 9 no Maximum number of pictures you can select
sizeType Array.<string> ['original', 'compressed'] no The size of the selected picture
sourceType Array.<string> ['album', 'camera'] no Choose the source of the picture
success function   no Interface calls a successful callback function
fail function   no Interface calls failed callback functions
complete function   no The callback function at the end of the interface call ( Call successful , Failure will be carried out )
So we get the following upload code :


// Take photos or pictures for the subjective questions

chooseAnswerImage: function(e) {

wx.chooseImage({

count: 1, // default 9

sizeType: ['original'], // Specifies the original image

sourceType: ['album', 'camera'], // You can specify whether the source is an album or a camera , By default, there are both

success: function(res) {

// Returns a list of local file paths for the selected photo ,tempFilePath Can be used as img Tagged src Property display picture

// If the answer picture has been uploaded, delete the original picture first

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

if (currentAnswerImgPathsItem != null) {

// afferent string Is a single file deletion , afferent array Is batch deletion

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: ' Image upload failed ',

duration: 2000

})

})

} else {

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

}

}

});

},

uploadAnswerImg: function(pathId, pathImg) {

// Upload selected pictures

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

wx.showToast({

title: ' Uploading ',

icon: 'loading',

duration: 10000

});

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

wx.hideToast();

wx.showToast({

title: ' Image uploaded successfully ',

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: ' Image upload failed ',

duration: 2000

})

});

},

4, Submit answer data


The answer is divided into multiple choice questions and subjective questions , The subjective question also has the corresponding picture , because bmob in pointer Type cannot be added directly , Therefore, a new clause containing pointer Field data , You should first add other information, and then add the corresponding pointer Type data insertion . Here is the code to upload a topic :


/**

* Upload single question information

* value It's topic information

* itemType It's the topic type

*/

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);

// Save first answer And successfully uploaded image information

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

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

// Save associated objects

// Set user associated objects

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

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

res1.set('userId', pointerUserId);

if (itemType == 'choose') {

// Set multiple choice question related objects

var chooseItemIdPointer = Bmob.Pointer('choose_item'); // Associated fields

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

res1.set('chooseItemId', pointerIdChooseItemId);

// Save after setting

res1.save();

} else {

// Set up the object of the subjective question

var subjectItemIdPointer = Bmob.Pointer('subjective_item'); // Associated fields

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

res1.set('subjectiveItemId', pointerIdSubjectItemId);

res1.save();

}

return true;

});

}).catch(err => {

wx.showToast({

title: ' Upload failed ',

duration: 2000

});

return false;

});

},


The next step is to traverse all the answer information in the form , Upload by type , However, when judging the upload results, make sure that each topic has been uploaded , If a topic is not uploaded successfully, it will prompt that it has not been submitted successfully . Here is the code to upload all the answer data :


/**

* Upload all answers

* value It's the information submitted by the form

*/

uploadAnswersInfo: function(value) {

// Show load loading

wx.showToast({

title: ' Submitting ...',

icon: 'loading',

duration: 1000

});

// Upload Title result list

var allUploadSuccess = [];

// Multiple choice index

var chooseItemIndex = 0;

// Subjective question index

var subjectiveItemIndex = 0;

// ergodic submit Data submitted

for (var keyname in value)

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

//console.log(value.keyname);

// Judge whether it is a single choice question

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++;

}

}

// Regularly monitor all returned results

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: ' The first ' + (i + 1) + ' Multiple choice question upload failed ',

duration: 1000

});

else {

wx.showToast({

title: ' The first ' + (i + 1 - that.data.onlineTestChooseItems.length) + ' Subjective question upload failed ',

duration: 1000

});

}

}

}

// Clear timer

clearInterval(checkResult);

wx.hideToast();

// All the questions were submitted successfully

if (allUploadSuccessFlag) {

// Upload and submit records

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

query.set("type", ' Classroom tests ');

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();

// Show load logo

wx.showToast({

title: ' Submitted successfully ',

duration: 3000

});

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

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

}

} else {

wx.showToast({

title: ' Submitting ...',

icon: 'loading',

duration: 1000

});

}

}, 1000);

},

Four , effect

1, Server topic information

Multiple choice question information



Subjective question information



2, Get topic information loading





3, Submit results



Then the classroom test and the simulation test function are the same , Just change the data , Because classroom questions should be pushed in real time , and bmob Real time push is charged , This is done in the end , The next section first realizes the foreground of the exercise module .