vue+element实现单选多选 答题的打分demo(从0-1全过程)

一、创建项目

电脑要安装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 的体验流程是 

组件划分目录:

  1. Nav                导航页
  2. AnwerCar      单选答题页
  3. Reslut            单选结果页
  4. Multi               多选答题页
  5. MultiRes         多选结果页
  6. TFitem            判断答题页
  7. 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的数据流程

  1. 在Vuex.state中预定义题目数据
  2. 答题页面使用配合element 表单组件 渲染数据
  3. 用户点击提交按钮时收集用户的答案转存到vuex中 并且跳转到对应的结果页
  4. 跳转到结果页 element 表单组件 渲染数据(比第2步多了 正确答案 答案解析) 
  5. 在结果页从Vuex中得到用户提交的答案与正确答案进行比较 判断对错并且打分 单选 判断直接判断是否和正确答案相等 多选题多一些逻辑判断
  6. 展示总分 和 每一道题的得分情况

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>

在进行答案验证的时候要比单选验证复杂一些

我的处理逻辑是

  1. 把标准答案和用户选择的答案 由数组转化成字符串 便于在页面当中展示
  2. 把多选题目的数据元数据copy一份 进行操作后 渲染到页面上 目的主要是为每一项增加两个属性 ses:本题所得分数   dec:本道题目的做题描述
  3. 分离出来各种情况来添加
  4. 全部答对(比较第一步拆分的此项字符换完全相等)  ses:2     dec 答题完全正确,本题获得2分
  5. 选了错误项 用用户选择的每一项去查找在正确答案里有没有 正确答案里没有就返回 -1 就说明用户由选择了 错选项  ses:0     dec选择了错选项,本题不得分
  6. 用户此项数据length < 1 说明此题没有作答 ses:0     dec:没有作答,本题不得分
  7. 因为是else if 排除判断的 剩下一种情况就是 选择了正确答案但项目长度不够 多选题的得分规则 漏选每一项得0.5分  ses:a3.length*0.5  dec:漏选正确项,本题获a3.length * 0.5得分
  8. 总分使用数组方法 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

猜你喜欢

转载自blog.csdn.net/benlalagang/article/details/125927798
今日推荐