最近两个做了一个展示试卷信息,选项等,并在前端页面打分 把分数与选项传进数据库,并且可以点击回显。
页面功能:我的试卷是代表着用户已做完的试卷,点击会弹出组件展示已做完试卷的信息(测试数据就放了一个,他里面还集成了下拉加载)
在首页点击继续考试或者开始考试弹出做题组件
接下来上代码
(后台返回的测试数据)
首页html代码
<template>
<div class="net">
<div v-if="isItem">//isitem 用来首页与试卷详情的显示切换
<div v-if="isGo">// isGo 用来首页与考试页面的显示切换
<el-card class="box-card" :body-style="{ padding: '10px' }">
<div slot="header" class="clearfix">
<span style="font-size: 20px;">我的试卷:</span>
</div>
<div>
<mescroll-uni ref="mescrollRef" @init="mescrollInit" :down="downOption" @up="upCallback"
:fixed="false">
<div v-show="!isMyShow" @click="itemClick(i)" v-for="i in myList" >
<el-card class="box-card-item">
<div slot="header" class="clearfix-header">
<span>{{ i.Title }}</span><span>测试结果:<span class="xu">{{ i.Result
}}</span></span>
</div>
<div class="date">得分:<span class="xu">{{ i.Score}}</span></div>
<div class="date"> <span class="bodyname">考试人:<span class="xu">{{ i.F_CreatorUserId}}</span></span>
<div class="date">交卷时间:<span class="xu">{{ i.F_LastModifyTime
}}</span></div>
</div>
</el-card>
</div>
</mescroll-uni>
</div>
</el-card>
</div>
// examination组件考试组件
<examination @isGoShow="setFn" v-else :paperItem="paperItem" :newPaper="newPaper" :form="form" :config="config"
:id="id" @setText="getPaperrList"></examination>
<el-button class="parserbtn" v-if="isGo" type="primary" round @click="goExamination()"
style="margin-bottom: 15px;">{{ text ? '开始考试'
: '继续考试' }}</el-button>
</div>
// completedItem详情组件
<completedItem v-else @isItem="isItem = !isItem" :completedItem="completedItem" :formItem="formItem">
</completedItem>
</div>
</template>
script部分
需要注意的地方因为有个业务功能,需要保存试题,所以如果点击开始考试但是没有做完就得变成开始考试且继续做那张试卷
然后代码中有很多在处理格式,因为接口是我们公司低代码平台生成的,所以格式返回的都是json字符串,所以多了很多处理格式的地方,包括提交post接口也要转化成json字符串,所以很麻烦,不过你们应该可以跟后端沟通
<script>
import { getPaperrList, getPaperrItem, getPaperrNewItem, getPaperrItemMore } from "@/api/apply/paper"
import examination from "./examination.vue"
import completedItem from "./completedItem.vue"
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
export default {
mixins: [MescrollMixin],
props: ['config'],
components: { examination, completedItem },
data() {
return {
name: 'dynamicPaper',
isGo: true,
isMyShow: false,
isItem: true,
myList: [],
todoList: [],
paperItem: [],
completedItem: {},
form: {
res: [],
},
formItem: {
res: [],
},
downOption: {
use: false,
isLock: false
},
datas: {
currentPage: 1,
menuId: '397890880037468677',
pageSize: 10,
queryJson: "",
sidx: "",
sort: "desc",
superQueryJson: ""
},
newdatas: {
data: {
Title: "试卷1",
F_CreatorUserId: "",
F_CreatorTime: "",
F_LastModifyTime: "",
tableField110: [],
id: "",
flowId: ""
},
id: ''
},
newPaper: {},
id: '',
text: true
}
},
created() {
},
// 监听未完成考试数组
watch: {
todoList: {
handler(newVal, oldVal) {
if (!newVal.length) {
this.text = true
} else {
this.text = false
}
},
immediate: true
}
},
methods: {
// 检测未完成试卷
getweiList() {
let obj = { ...this.datas }
obj.pageSize = 999
getPaperrList(obj, this.config.flowId).then(({ data }) => {
this.todoList = data.list.filter(item => { return item.Score==0 })
})
},
// 已完成试卷点击详情
itemClick(i) {
this.myList = []
getPaperrItem(this.config.flowId, i.F_Id).then(({ data }) => {
this.completedItem = JSON.parse(data.data)
this.completedItem.tableField110.forEach((item, ind) => {
// 处理选项格式
this.completedItem.tableField110[ind].Options = eval(item.Options)
})
this.completedItem.tableField110.forEach((it, i) => {
if (this.completedItem.tableField110[i].QuestionType == 2) {
// 处理多选题正确答案格式
this.completedItem.tableField110[i].Answer = eval(this.completedItem.tableField110[i].Answer).sort().toString()
// 处理考生答案选项格式
if(!it.ExamineeAnswer) return
let arr = it.ExamineeAnswer.split(',')
arr.forEach((item, index) => {
arr[index] = parseInt(arr[index])
})
this.formItem.res[i] = arr
} else {
this.formItem.res[i] = parseInt(it.ExamineeAnswer)
}
});
this.isItem = !this.isItem
document.documentElement.scrollTop = 0
})
},
// 继续/开始考试
goExamination() {
this.myList = []
// 继续考试
if (this.todoList.length) {
this.getPaperrItem(this.config.flowId, this.todoList[0].id)
} else {
// 开始考试
this.newdatas.data = JSON.stringify(this.newdatas.data)
let timestamp = Date.parse(new Date()) / 1000;
getPaperrNewItem(this.config.flowId, timestamp, this.newdatas).then((res) => {
this.getPaperrItem(this.config.flowId, res.data.Id)
})
}
document.documentElement.scrollTop = 0
this.isGo = !this.isGo
},
// 获取试卷详情请求
getPaperrItem(flowId, dataId) {
getPaperrItem(flowId, dataId).then(({ data }) => {
console.log(data);
this.id = data.id
this.newPaper = JSON.parse(data.data)
this.paperItem = JSON.parse(data.data).tableField110
this.paperItem.forEach((item, ind) => {
// 处理选项格式
this.paperItem[ind].Options = eval(item.Options)
if (item.QuestionType == 2) {
// 处理多选题答案格式 json转化数组
this.paperItem[ind].Answer = JSON.parse(this.paperItem[ind].Answer)
}
// 处理判断题答案格式
if (item.QuestionType == 3) {
this.paperItem[ind].Answer = this.paperItem[ind].Answer.slice(0, this.paperItem[ind].Answer.length - 1).slice(1)
}
})
this.crFormres(this.paperItem, this.form.res)
}).catch(err => {
this.$refs.uToast.show({
title: err,
type: 'error',
})
})
},
// 处理传入子组件格式
crFormres(item, res) {
item.forEach((it, i) => {
if (item[i].QuestionType == 2) {
res[i] = []
} else {
res[i] = ''
}
console.log('处理了');
});
},
setFn() {
this.isGo = true
this.getweiList()
},
// 下拉加载
upCallback(page) {
this.getweiList()
this.datas.currentPage = page.num
getPaperrList(this.datas, this.config.flowId).then(({ data }) => {
this.myList.push(...data.list.filter(item => { return item.Score>0 }))
// this.todoList.push(...data.list.filter(item => { return !item.Result }))
this.mescroll.endSuccess(data.list.length);
console.log(this.myList, '123', this.todoList);
}).catch(() => {
this.mescroll.endErr();
})
}
},
}
</script>
css部分
<style lang="scss">
.net {
.box-card {
width: 380px;
margin: 15px auto;
border-radius: 5px;
}
.clearfix-header {
display: flex;
justify-content: center;
justify-content: space-between;
}
.bodyname {
margin-right: 60px;
}
.box-card-item {
margin-bottom: 10px;
.date {
margin-top: 10px;
}
}
// .bigbody {
// display: flex;
// justify-content: center;
// justify-content: space-between;
// padding: 10px;
// }
.parserbtn {
margin: 0 38%;
}
// .shi{
// }
.xu{
color: #909399;
}
}
</style>
examination组件(开始/继续考试)
<template>
<div class="net">
<el-button type="info" class="navbtn" @click="$emit('isGoShow')" round><i
class="icon-ym icon-ym-report-icon-preview-pagePre" style="margin-right: 10px;"></i>返回</el-button>
<el-form :model="form" ref="uForm">
<el-card class="box-card" v-for="(o, index) in paperItem" :key="index">
<div slot="header" class="clearfix">
<span>{{ o.QuestionStem }}</span>({{ o.Point }}分)
</div>
<!-- 单选 -->
<el-radio-group v-model="form.res[index]" v-if="o.QuestionType === '1'">
<div class="sitem" v-for="(i, ind) in o.Options" :key="ind"><el-radio :label="ind + 1">{{ i
}}</el-radio></div>
</el-radio-group>
<!-- 多选 -->
<el-checkbox-group v-model="form.res[index]" v-if="o.QuestionType === '2'">
<div class="sitem" v-for="(i, ind) in o.Options" :key="ind"><el-checkbox :label="ind + 1">{{ i
}}</el-checkbox></div>
</el-checkbox-group>
<!-- <el-radio-group v-model="form.res[index]" v-if="o.QuestionType === '3'">
<div class="sitem"><el-radio :label="1">对</el-radio></div>
<div class="sitem"><el-radio :label="2">错</el-radio></div>
</el-radio-group> -->
</el-card>
</el-form>
<button @click="fn" style="margin: 10%;">提交</button>
<!-- 弹出层 -->
<el-dialog title="答题完成" :visible.sync="dialogVisible" width="80%" style="margin-top: 80px; border-radius: 5%;">
<span>成绩为{{ grades }}!下次争取更进一步,自动跳转~</span>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="isDialog">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { setPaperrItem } from "@/api/apply/paper"
export default {
props: ['paperItem', 'form', 'newPaper', "config", 'id'],
data() {
return {
forms: {},
grades: 0,
dialogVisible: false,
datas: {
data: {
tableField110: [],
Title: "",
F_CreatorUserId: "",
F_CreatorTime: "",
F_LastModifyTime: null,
Score: 0,
Result: "合格",
id: "",
flowId: "",
ExamineeAnswer:''
},
id: ""
}
};
},
created() {
this.forms = this.form
},
methods: {
fn() {
// 非空校验
console.log(this.forms.res,'99');
let flag = this.forms.res.every(item => !!item)
if (!flag) {
uni.showToast({
title: '还有题未完成哦',
icon: 'error'
});
return
} else {
// 计算分数
this.paperItem.forEach((item, ind) => {
if (item.QuestionType == 2) {
console.log(item);
if (item.Answer.sort().toString() == this.forms.res[ind].sort().toString()) this.grades += item.Point
} else {
if (item.Answer == this.forms.res[ind]) this.grades += item.Point
}
})
console.log(this.datas, this.newPaper, '11');
this.datas.data.Title = this.newPaper.Title
this.datas.data.F_CreatorUserId = this.newPaper.F_CreatorUserId
this.datas.data.id = this.id
this.datas.id = this.id
this.datas.data.F_CreatorTime = this.newPaper.F_CreatorTime
this.datas.data.Result = this.newPaper.Result
this.datas.data.tableField110 = this.newPaper.tableField110
this.datas.data.Score = this.grades
// this.datas.data = JSON.stringify(this.datas.data)
// console.log(this.config.flowId,this.datas.id,this.datas,'99');
this.forms.res.forEach((item,ind) => {
if(item instanceof Array) {this.forms.res[ind] = item.toString()}
this.datas.data.tableField110.ExamineeAnswer = this.forms.res[ind]
})
this.datas.data = JSON.stringify(this.datas.data)
setPaperrItem(this.config.flowId, this.datas.id, this.datas).then(res => {
console.log(res);
})
// this.$emit('setText')
this.dialogVisible = true
setTimeout(this.isDialog, 3000)
}
},
isDialog() {
this.dialogVisible = false
this.$emit('isGoShow')
}
},
}
</script>
<style lang="scss">
.navbtn {
margin-left: 15px;
margin-top: 15px;
font-size: 18px;
padding-left: 10px;
}
.net {
.box-card {
width: 380px;
margin: 15px auto;
border-radius: 3%;
}
}
.sitem {
margin-bottom: 8px;
}
</style>
completedItem组件(试卷详情页面)
<template>
<div class="net">
<el-button type="info" class="navbtn" @click="$emit('isItem')" round><i
class="icon-ym icon-ym-report-icon-preview-pagePre" style="margin-right: 10px;"></i>返回</el-button>
<el-card class="box-card" :body-style="{ padding: '10px' }">
<div>
<div slot="header" class="clearfix-header">
<span>{{ completedItem.Title }}</span> <span>测试结果:<span class="xu">{{ completedItem.Result }}</span> </span>
</div>
<div class="magin-b">
<div class="magin-t">得分:<span class="xu">{{ completedItem.Score }}</span></div>
<div class="bodyname magin-t">考试人:<span class="xu">{{ completedItem.F_CreatorUserId }}</span></div>
<div class="date magin-t">交卷时间:<span class="xu">{{ completedItem.F_LastModifyTime }}</span></div>
</div>
<el-card class="box-card-s" v-for="(o, index) in completedItem.tableField110" :key="index">
<div slot="header" class="clearfix">
<span>{{ o.QuestionStem }}</span>({{ o.Point }}分)
</div>
<!-- 单选 -->
<el-radio-group disabled v-model="formItem.res[index]" v-if="o.QuestionType === '1'">
<div class="sitem" v-for="(i, ind) in o.Options" :key="ind"><el-radio
:label="ind + 1">{{ i
}}</el-radio></div>
</el-radio-group>
<!-- 多选 -->
<el-checkbox-group disabled v-model="formItem.res[index]" v-if="o.QuestionType === '2'">
<div class="sitem" v-for="(i, ind) in o.Options" :key="ind"><el-checkbox
:label="ind + 1">{{ i
}}</el-checkbox></div>
</el-checkbox-group>
<!-- 判断 -->
<!-- <el-radio-group v-if="o.QuestionType === '3'">
<div class="sitem"><el-radio :label="1">对</el-radio></div>
<div class="sitem"><el-radio :label="2">错</el-radio></div>
</el-radio-group> -->
<div class="tet">
正确答案:<span class="xu">{{ o.Answer }}</span>
</div>
</el-card>
</div>
</el-card>
</div>
</template>
<script>
export default {
props: ['completedItem','formItem'],
created() {
console.log(this.completedItem,this.formItem, '90');
}
}
</script>
<style lang="scss">
.tet {
font-size: 16px;
margin-top: 8px;
}
.magin-t{
margin-top: 10px;
}
.magin-b{
margin-bottom: 20px;
}
.box-card {
width: 380px;
margin: 15px auto;
border-radius: 5px;
padding: 10px;
.box-card-s{
margin-top: 7px;
}
}
.clearfix-header{
display: flex;
justify-content: space-between;
}
.xu{
color: #909399;
}
</style>
这个页面难点我总结了一下
需要转化格式接受展示和上传提交都要转化格式(个人接口需求问题)
element-ui多选框单选框的v-model值需要进行处理,组合多选框的值需要是个数组
提交表单答案对比其实只是第一感觉难,实际几行代码就解决了,单选题对比相等就相加分值,多选题数组排序方法后再转化为字符串对比是否相等
优点:因为我这个排版都是比较灵活的 后台想传多少选项就可以显示多少选项 且优势数据都在前端处理过了 可以省去后台很多功夫 他们只需要接收数据就行了。
因为我端口是用低代码生成的 需要处理数据 复用性没那么高 仅供阅读复制修改或者提供思路 如果想直接不用脑子复制粘贴的不太适合
然后需求是一步一步提出的,比如点击详情是做完后修改的,所以封装方面做的没那么优美哈。