vue+element实现单选多选 答题的打分demo(从0-1全过程)
答题: vue+element答题demo 包含单选 多选 判断、打分页面
一、创建项目
电脑要安装node.js
下载 | Node.js 中文网http://nodejs.cn/download/配置npm包 (推荐使用淘宝镜像)让你能够高速下载 包
npm install -g cnpm --registry=https://registry.npm.taobao.org
安装vue@cli 脚手架
npm install -g @vue/cli
安装成功后检查版本
vue --version
在需要创建的目录中 打开命令行窗口 执行下列代码创建
vue create answer
创建过程中通过空格键选择要使用的模块,我们在这里选择Babel、Router、Vuex、选择好后回车。
也可以快速创建 等待项目创建成功后 命令行执行 vue ui 来增加插件
二、局部安装 element-ui
npm i element-ui -S
按需引入
我们可以只引入需要的组件,以达到减小项目体积的目的。
首先,安装 babel-plugin-component:
npm install babel-plugin-component -D
根目录中设置babel.config.js
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: [
[
'component',
{
libraryName: 'element-ui',
styleLibraryName: 'theme-chalk'
}
],
'@babel/plugin-syntax-dynamic-import'
]
}
新建plugins 文件夹
element.js: 里面按需使用需要的文件
import Vue from 'vue'
import {
Button,
Form,
FormItem,
Radio,
RadioGroup,
RadioButton,
CheckboxGroup,
Checkbox,
Card,
MessageBox,
Message
} from 'element-ui'
Vue.use(Button)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(RadioGroup)
Vue.use(Radio)
Vue.use(RadioButton)
Vue.use(Card)
Vue.use(CheckboxGroup)
Vue.use(Checkbox)
Vue.prototype.$queding = MessageBox.confirm
Vue.prototype.$Msg = Message
再引入的main.js 中这样可以简化入口文件
import Vue from 'vue'
import App from './App.vue'
import './plugins/element'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
别忘了引入样式
public/index.html
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
三、配置路由
demo 的体验流程是
组件划分目录:
- Nav 导航页
- AnwerCar 单选答题页
- Reslut 单选结果页
- Multi 多选答题页
- MultiRes 多选结果页
- TFitem 判断答题页
- TFRes 判断结果页
router/index.js: 使用路由懒加载优化性能
import Vue from 'vue'
import VueRouter from 'vue-router'
const Answer = () => import('../components/AnswerCar')
const Result = () => import('../components/Result')
const Multi = () => import('../components/Multi')
const MultiRes = () => import('../components/MultiRes')
const TFItem = () => import('../components/TFItem')
const TFRes = () => import('../components/TFRes')
const Nav = () => import('../components/Nav')
Vue.use(VueRouter)
const routes = [
{
path: '/',
redirect:'/nav'
},
{
path: '/nav',
component:Nav,
},
{
path: '/answer',
component:Answer,
},
{
path: '/multi',
component:Multi
},
{
path: '/multiRes',
component:MultiRes
},
{
path: '/result',
component:Result
},
{
path: '/tfitem',
component:TFItem
},
{
path: '/tfres',
component:TFRes
},
]
const router = new VueRouter({
routes
})
export default router
Vuex 准备数据
在正常开发中 前端只需要向后端请求题目数据 收集用户的答案 返回给后端 再接收答题结果就可以了 我们的小demo 来简化一下
小demo的数据流程
- 在Vuex.state中预定义题目数据
- 答题页面使用配合element 表单组件 渲染数据
- 用户点击提交按钮时收集用户的答案转存到vuex中 并且跳转到对应的结果页
- 跳转到结果页 element 表单组件 渲染数据(比第2步多了 正确答案 答案解析)
- 在结果页从Vuex中得到用户提交的答案与正确答案进行比较 判断对错并且打分 单选 判断直接判断是否和正确答案相等 多选题多一些逻辑判断
- 展示总分 和 每一道题的得分情况
1 单选为例
export default new Vuex.Store({
state: {
quesBank:[
{
title:'某建设工程施工招标,甲公司中标后将其转包给不具有相应资质等级的乙公司,乙施工过程不符合规定的质量标准,给建设单位造成损失。关于向建设单位承担赔偿责任的说法,正确的是( )。',
selectA:'甲、乙承担连带赔偿责任',
selectB:'建设单位与甲有合同关系,应由乙承担赔偿责任',
selectC:'乙为实际施工人,应由乙承担赔偿责任',
selectD:'甲和乙承担按份赔偿责任',
correct:'A',
explain:'转包的法律责任,承包单位将承包的工程转包或违法分包的,对因转包工程或违法分包工程不符 合规定的质量标准造成的损失,该承包单位与接受转包或违法分包的单位承担连带赔偿责任。'
},
{...}
],
}
}
2.element 表单组件 渲染数据
通过映射便捷的使用vuex的数据
import {mapState,mapMutations} from 'vuex'
export default {
name: "AnswerCar",
data(){
return {
ruleForm: {
resource: [], //每一道题用户输入的值
},
}
},
computed:{
...mapState(['quesBank'])
}
}
<el-form>
<el-form-item label="" v-for="(t,i) in quesBank" :key="i">
<P style="margin-top: 10px;margin-bottom: 0px;line-height: 2">{{i + 1}}.{{t.title}}</P>
<el-radio-group v-model="ruleForm.resource[i]">
<el-radio label="A">A.{{t.selectA}}</el-radio><br>
<el-radio label="B">B.{{t.selectB}}</el-radio><br>
<el-radio label="C">C.{{t.selectC}}</el-radio><br>
<el-radio label="D">D.{{t.selectD}}</el-radio>
</el-radio-group>
</el-form-item>
<el-button type="primary" class="subBtn" @click="present">提交试卷</el-button>
</el-form>
3.储存用户的答案转到详情页
Vuex mutations 定义方法 在点击按钮是触发
// vuex 中
mutations: {
updateUserSelect(state,setOP){
state.UserSource = setOP
},
// 组件中
methods:{
...mapMutations(['updateUserSelect']),
async present(){
const yuan = this.ruleForm.resource.filter(x => x!== null)
if(yuan.length < this.ruleForm.resource.length || yuan.length < 1){
const confirmResult = await this.$queding(
'检测到您有未作答的题目, 是否继续提交?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).catch(err => err)
if (confirmResult !== 'confirm'){
return this.$Msg.info('已取消提交')
}
this.showAns()
}
this.showAns()
},
showAns(){
this.updateUserSelect(this.ruleForm.resource)
this.$router.push('/result')
}
}
先判断收集用户的答案数据数组 有没有 比题目的数组要少 少的话提示下用户
调用Vuex中方法 更新数据 并且跳转到 答题页
4.渲染结果页数据
比答题页多渲染 正确答案 答案解析 用户选择的答案 此题的得分情况 和总分
<div>
<el-card>您的得分是:{{score}}</el-card>
<el-form>
<el-form-item label="" v-for="(t,i) in quesBank" :key="i">
<P style="margin-top: 10px;margin-bottom: 0px;line-height: 2">{{i + 1}}.{{t.title}}</P>
<el-radio-group v-model="ruleForm.rightC[i]" style="user-select: none;pointer-events:none">
<el-radio label="A" >A.{{t.selectA}}</el-radio><br>
<el-radio label="B" >B.{{t.selectB}}</el-radio><br>
<el-radio label="C" >C.{{t.selectC}}</el-radio><br>
<el-radio label="D" >D.{{t.selectD}}</el-radio>
</el-radio-group>
<div>正确答案:{{ruleForm.rightC[i]}}</div>
<div :class="[ruleForm.rightC[i] === UserSource[i] ? 'bluee' : 'redd']">
<span style="display: inline-block;margin-right: 40px">您的选择:{{UserSource[i]}}</span>
得分{{ruleForm.rightC[i] === UserSource[i] ? ' 1' : ' 0'}}
</div>
<div>答案解析:{{t.explain}}</div>
</el-form-item>
</el-form>
</div>
5、6.进行比较打分
正确答案 答案解析 元数据里直接拿来用
用户选择的答案 上一步已经在vuex里更新了 直接拿过来用
此题的答题情况 用三元表达式判断 并添加不同样式
总分遍历数据相互比较 一样的话就+1
<script>
import {mapState} from 'vuex'
export default {
name: "Result",
data(){
return {
ruleForm: {
resource: [], //每一道题用户输入的值
// 每一道题的正确答案
rightC:[]
}
}
},
created() {
this.ruleForm.rightC = this.quesBank.map(x => x.correct)
},
computed:{
...mapState(['quesBank','UserSource']),
score(){
let total = 0
this.ruleForm.rightC.forEach(
(x,i) => {
if (x === this.UserSource[i]){
total ++
}
}
)
return total
}
},
}
</script>
<style scoped>
.bluee{
color: #409EFF;
}
.redd{
color: #f35;
}
</style>
判断题和单选题做法一致
多选题和单选题有不同的地方在于:
渲染的时候组件不同 并且收集的单项数据要为一个数组
<el-form>
<el-form-item v-for="(t,i) in multiBank" :key="i">
<P style="margin-top: 10px;margin-bottom: 0px;line-height: 2">{{i + 1}}.{{t.title}}</P>
<el-checkbox-group v-model="FormDate[i]" :max="4">
<el-checkbox label="A">A.{{t.selectA}}</el-checkbox>
<br>
<el-checkbox label="B">B.{{t.selectB}}</el-checkbox>
<br>
<el-checkbox label="C">C.{{t.selectC}}</el-checkbox>
<br>
<el-checkbox label="D">D.{{t.selectD}}</el-checkbox>
<br>
<el-checkbox label="E">E.{{t.selectE}}</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-button type="primary" class="subBtn" @click="present">提交试卷</el-button>
</el-form>
<script>
export default {
name: "Multi",
data(){
return {
FormDate:[]
}
},
created() {
// 因为多选框绑定要是一个数组 所以根据题目的长度遍历出一个空数组
this.multiBank.forEach(x => this.FormDate.push([]))
},
}
</script>
在进行答案验证的时候要比单选验证复杂一些
我的处理逻辑是
- 把标准答案和用户选择的答案 由数组转化成字符串 便于在页面当中展示
- 把多选题目的数据元数据copy一份 进行操作后 渲染到页面上 目的主要是为每一项增加两个属性 ses:本题所得分数 dec:本道题目的做题描述
- 分离出来各种情况来添加
- 全部答对(比较第一步拆分的此项字符换完全相等) ses:2 dec 答题完全正确,本题获得2分
- 选了错误项 用用户选择的每一项去查找在正确答案里有没有 正确答案里没有就返回 -1 就说明用户由选择了 错选项 ses:0 dec选择了错选项,本题不得分
- 用户此项数据length < 1 说明此题没有作答 ses:0 dec:没有作答,本题不得分
- 因为是else if 排除判断的 剩下一种情况就是 选择了正确答案但项目长度不够 多选题的得分规则 漏选每一项得0.5分 ses:a3.length*0.5 dec:漏选正确项,本题获a3.length * 0.5得分
- 总分使用数组方法 map reduce 求和即可
<script>
import {mapState} from 'vuex'
export default {
name: "MultiRes",
data(){
return{
// 用于展示选定勾选的正确答案
rightC:[],
// 用于展示文本的正确答案
rightT:[],
// 用于展示用户选择的正确答案
UserMultiSTXT:[],
total:[],
// 拷贝一份数据在这里修改
NewData:[]
}
},
created() {
this.rightC = this.multiBank.map(x => x.correct)
this.rightT = this.rightC.map(x => x.join(''))
this.UserMultiSTXT = this.UserMultiS.map(x => x.join(''))
this.NewData = this.multiBank
for(let i=0;i<this.multiBank.length;i++){
const pass1 = this.rightT[i] === this.UserMultiSTXT[i]
const a1 = Array.from(this.UserMultiS[i])
const a2 = Array.from(this.rightC[i])
let a3 = []
for(let s=0;s<a1.length;s++){
a3.push(a2.indexOf(a1[s]))
}
if (pass1){
this.NewData[i].ses =2
this.NewData[i].dec = `<span style="color: #409EFF">答题完全正确,本题获得2分</span>`
}else if(a3.includes(-1)){
this.NewData[i].ses = 0
this.NewData[i].dec = `<span style="color: #ff3355">选择了错选项,本题不得分</span>`
}else if(a3.length<1){
this.NewData[i].ses = 0
this.NewData[i].dec = `<span style="color: #ff3355">没有作答,本题不得分</span>`
}else {
this.NewData[i].ses = a3.length * 0.5
this.NewData[i].dec = `<span style="color: orange">漏选正确项,本题获${a3.length * 0.5}得分</span>`
}
}
this.total = this.NewData.map(x => x.ses).reduce((a,b) => a+b,0)
},
computed:{
...mapState(['multiBank','UserMultiS']),
},
}
</script>
源代码下载:
答题: vue+element答题demo 包含单选 多选 判断、打分页面https://gitee.com/wu-songgang/answer
更多推荐
所有评论(0)