vue2 + ElementUI表单嵌套数组校验,实现在线考试功能

1、最终效果:

考题是从后端接口获取循环遍历所得

动态表单校验:

2、代码示例:

<template>
  <div>
    <div class="detailContent">
      <div class="stateLabel">
        <div class="title">【{{ submitForm.typeName }}】 {{ submitForm.theme }}</div>
        <div class="right">
          <div class="examDate">{{ submitForm.startTime }} 至 {{ submitForm.endTime }}
            <el-button size="mini" type="primary" @click="goBack">返回</el-button>
          </div>
          <div class="examUser">{{ submitForm.createUser }} {{ submitForm.initiateUnitName }}</div>
        </div>
      </div>
      <el-divider></el-divider>
      <div class="center" v-loading="loading">
        <div class="examContent">
          <div style="padding: 0 50px">
            <el-form class="submit-form" :model="submitForm" ref="submitForm" :rules="submitRules"
              :disabled="status != '1' && status != '5'">
              <div v-for="(item, index) in submitForm.questionList" :key="index">
                <div class="examTopic">
                  <div class="topicTitle">{{ index + 1 }} 、{{ item.name }}</div>
                  <div class="score">
                    <div class="examType">{{ findTopicType(item.type) }} {{ item.score }} 分</div><br>
                  </div>
                </div>
                <div class="answer">
                  <p>答案:</p>
                  <!-- 单选题 -->
                  <div class="answerList" v-if="item.type == 1">
                    <el-form-item :prop="'questionList.' + index + '.answer'" :rules="submitRules.answer">
                      <el-radio-group v-model="item.answer">
                        <el-radio v-for="info in item.options" style="margin-left:10px" :label="info.id">{{
                          info.option }}</el-radio>
                      </el-radio-group>
                    </el-form-item>
                  </div>
                  <!-- 多选题 -->
                  <div class="answerList" v-else-if="item.type == 2">
                    <el-form-item :prop="'questionList.' + index + '.answer'" :rules="submitRules.answer">
                      <el-checkbox-group v-model="item.answer">
                        <el-checkbox v-for="(info, index) in item.options" :key="index" :label="info.id">{{ info.option
                        }}</el-checkbox>
                      </el-checkbox-group>
                    </el-form-item>
                  </div>
                  <!-- 论述题 -->
                  <div class="answerList" v-else-if="item.type == 3">
                    <el-form-item :prop="'questionList.' + index + '.answer'" :rules="submitRules.answer">
                      <el-input class="textarea" type="textarea" v-model="item.answer" clearable show-word-limit
                        :autosize="{ minRows: 2, maxRows: 4 }" maxlength="800" placeholder="请输入"></el-input>
                    </el-form-item>
                  </div>
                  <!-- 简答题 -->
                  <div class="answerList" v-else-if="item.type == 4">
                    <el-form-item :prop="'questionList.' + index + '.answer'" :rules="submitRules.answer">
                      <el-input class="textarea" type="textarea" v-model="item.answer" clearable show-word-limit
                        :autosize="{ minRows: 2, maxRows: 4 }" maxlength="800" placeholder="请输入"></el-input>
                    </el-form-item>
                  </div>
                </div>
              </div>
            </el-form>
          </div>
        </div>
        <div class="requirement">
          <el-card class="box-card">
            <div slot="header" class="clearfix">
              <span>考试要求:</span>
            </div>
            <div>
              {{ submitForm.examAsk }}
            </div>
          </el-card>
          <div v-if="status == 4 || status == 2" class="bigCircle">
            {{ submitForm.score }} 分
          </div>
        </div>
      </div>

      <div>
        <div style="padding: 0 50px" v-if="status == 1 || status == 5">
          <div slot="footer" class="dialog-footer">
            <el-button size="mini" class="cancel" @click="submit('1')">保 存</el-button>
            <el-button size="mini" type="primary" @click="submit('2')">交 卷</el-button>
          </div>
        </div>
        <div style="padding: 0 50px" v-else>
          <div slot="footer" class="dialog-footer">
            <el-button size="mini" class="cancel" @click="goBack">返 回</el-button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { dictMixin } from "@/mixins/dictMinxin";
import baseUpload from '@/components/upload/baseUpload.vue'
import { Loading } from 'element-ui'
import fileListVue from "@/components/common/fileList.vue";
export default {
  mixins: [dictMixin],
  components: {
    fileListVue,
    baseUpload
  },
  data() {
    return {
      dateStatus: '',
      loading: false,
      totalData: 0,
      pageNo: 1,
      pageSize: 10,
      examId: '',
      isDisabled: false,
      submitForm: {},
      status: '', // 1 参加考试 2 详情 3 列表详情  4 打分
      searchForm: {
        examId: '',
        userName: '',
        pageNo: 1,
        pageSize: 10,
      },
      personId: '',
      submitRules: {
        answer: [{ required: true, message: "请输入答案", trigger: "change" }],
      },
    };
  },
  mounted() {
    if (this.$route.query) {
      let { examId, status } = this.$route.query
      this.status = status // 状态 1 参加考试 2 详情 
      this.examId = examId // 试卷id
      // status 1 参加考试 2 详情 
      if (status == '1') {
        this.getDetail(); // 获取考题
      } else {
        this.getMyExamList() // 获取我的试卷
      }
    }
  },
  methods: {
    findTopicType(val) {
      let str = ""
      if (val == 1) {
        str = '单选题'
      } else if (val == 2) {
        str = '多选题'
      } else if (val == 3) {
        str = '论述题'
      } else if (val == 4) {
        str = '简答题'
      }
      return str
    },

    goBack() {
      window.history.go(-1);
    },
    submit(status) {
      this.$refs["submitForm"].validate((valid) => {
        if (valid) {
          this.$confirm("请确认是否提交!", "提示", {
            confirmButtonText: "确定",
            cancelButtonText: "取消",
            type: "warning",
          }).then(() => {
            let params = { ...this.submitForm }
            if (status == 1) { //保存
              params.isComplete = 1
            } else if (status == 2) { // 交卷
              params.isComplete = 2
            }
            // this.$api.onlineExam.handPaper(params).then((res) => {
            //   if (res.code == 200) {
            //     this.$message({
            //       type: 'success',
            //       message: "操作成功!"
            //     })
            //   } else {
            //     this.$message({
            //       type: "error",
            //       message: res.message + "!",
            //     });
            //   }
            //   this.$router.push("/onlineExam");
            // })
          })
        }
      })
    },
    async getDetail() {
      this.loading = true;
      await this.$api.onlineExam.getExamInfo(this.examId).then((res) => {
        this.loading = false;
        if (res.code == 200) {
          this.submitForm = res.data
          this.submitForm.questionList.map(item => {
            if (item.type == 2) {
              item.answer = []
            }
          })
        }
      })
    },
    async getMyExamList() {
      this.loading = true;
      let params = {
        examId: this.examId,
        personId: this.personId
      }
      await this.$api.onlineExam.paperDetail(params).then((res) => {
        this.loading = false;
        if (res.code == 200) {
          // this.submitForm = res.data
          this.submitForm = {
            createTime: "2024-01-19 14:51:52",
            createUser: "admin",
            endTime: "2024-01-23 00:00:00",
            examAsk: "认真认真认真", // 考试要求
            id: 15,
            initiateUnitName: "国网四川省电力公司",
            isComplete: null,
            questionList: [{// 题目列表
              answer: 47, // 答案
              answerList: null,
              examId: 15,
              id: 26,
              name: "在Vue中,被用来响应地更新HTML属性的指令是",
              options: [{
                answer: null,
                id: 44,
                option: "v-on",
                questionId: 26,
              },
              {
                answer: null,
                id: 45,
                option: "v-if",
                questionId: 26,
              }, {
                answer: null,
                id: 46,
                option: "v-bind",
                questionId: 26,
              }, {
                answer: null,
                id: 47,
                option: "v-model",
                questionId: 26,
              }],
              partScore: null,
              score: 10,
              totalScore: 10,
              type: 1, // 题目类型 1 单选 2 多选 3 论述 4 简答
            }, {
              answer: null, // 答案
              answerList: [48, 49, 50],
              examId: 15,
              id: 27,
              name: "在Vue中,被用来响应地更新HTML属性的指令是",
              options: [{
                answer: null,
                id: 48,
                option: "push()",
                questionId: 27,
              },
              {
                answer: null,
                id: 49,
                option: "pop()",
                questionId: 27,
              }, {
                answer: null,
                id: 50,
                option: "shift()",
                questionId: 27,
              }],
              partScore: null,
              score: 10,
              totalScore: 10,
              type: 2, // 题目类型 1 单选 2 多选 3 论述 4 简答
            }, {
              answer: null, // 答案
              answerList: null,
              examId: 15,
              id: 28,
              name: "请说下封装 vue 组件的过程",
              options: null,
              partScore: null,
              score: 50,
              totalScore: 50,
              type: 3, // 题目类型 1 单选 2 多选 3 论述 4 简答
            }, {
              answer: null, // 答案
              answerList: null,
              examId: 15,
              id: 29,
              name: "请说下 vue的生命周期",
              options: null,
              partScore: null,
              score: 30,
              totalScore: 30,
              type: 4, // 题目类型 1 单选 2 多选 3 论述 4 简答
            }
            ],
            questionNum: 4,
            receivingUnitName: "国网四川省电力公司",
            score: 100,
            startTime: "2024-01-19 00:00:00",
            status: 1,
            theme: "测试考试",
            totalScore: 100,
            type: "2",
            typeName: "定期考试",
          }
          this.submitForm.questionList.map(item => {
            if (item.type == 2) {
              item.answer = []
              item.answerList.map(i => {
                item.answer.push(i)
              })
            } else if (item.type == 1) {
              item.answer = item.answer
            }
          })
        }
      })
    },
  },
};
</script>
<style lang="less" scoped>
.examDate {}

/deep/ .el-form-item__content {
  max-width: none !important;
}

/deep/ .el-date-editor.el-input__inner {
  width: 100%;
}

/deep/.el-radio__input.is-checked .el-radio__inner {
  border-color: #18a8a4;
  background: #18a8a4;
}

/deep/ .el-radio__input.is-checked+.el-radio__label {
  color: #18a8a4;
}

/deep/ .el-tree .el-tree-node .el-checkbox .el-checkbox__inner {
  display: inline-block;
}

.title {
  border-left: 3px solid rgba(24, 168, 164, 1);
  opacity: 1;
  font-size: 20px;
  font-family: PingFangSC, PingFangSC-Medium;
  font-weight: 600;
  text-align: left;
  color: #333c4f;
  line-height: 20px;
  padding-left: 11px;
  margin: 20px 0;
  width: auto;
  margin-left: 25px;
}

.detailContent {
  background: #fff;
  width: calc(100% - 40px);
  height: auto;
  overflow: hidden;
  padding: 20px;
}

.center {
  display: flex;
  justify-content: space-evenly;

  .examContent {
    width: 60%;

    .examTopic {
      display: flex;
      align-items: center;

      .topicTitle {
        width: 75%;
        background-color: aliceblue;
        padding: 6px;
      }

      .score {
        margin-left: 10px;
        margin-top: 10px;
        width: 20%;
        display: flex;
        flex-direction: column;
        flex-wrap: nowrap;
        align-content: flex-start;
        align-items: flex-start;

        .scoreExam {
          display: flex;
          flex-direction: row;
          flex-wrap: nowrap;
          justify-content: flex-start;
          align-items: center;
        }
      }

      .examType {
        // padding: 5px;
      }

    }

    .answer {
      padding: 10px;

      p {
        display: inline-block;
        padding: 10px;
      }

      .answerList {
        width: 500px;
        display: inline-block;
        padding: 10px;
      }
    }

    .textarea {
      width: 100%;
    }
  }

  .requirement {
    width: 40%;

    .box-card {
      width: 100%;
    }

    .table-card {
      margin-top: 20px;
      width: 100%;
    }
  }
}

.cancel {
  margin-left: 450px;
}

.stateLabel {
  display: flex;
  justify-content: space-between;

  .right {
    font-family: PingFangSC, PingFangSC-Medium;
    text-align: left;
    color: #41444a;
    line-height: 28px;
  }

}

.bigCircle {
  margin-top: 200px;
  width: 200px;
  height: 200px;
  background-color: #18A8A4;
  // border: 1px solid #18A8A4;
  border-radius: 50%;
  font-size: 28px;
  color: #fff;
  display: flex;
  justify-content: center;
  align-items: center;
}
</style>

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于springboot+vue+elementui开发的在线考试系统【前端源码+后端源码+数据库】.zip 系统基于spring boot + vue开发,前端框架elementui,在若依/Ruoyi(Vue前后端分离版本)整体系统框架上开发了考试系统相关。 整个系统包括试题管理、考试组织、网上考试、以及资料管理四部分,资料管理只是作为图片、文件共享使用 请先阅读doc目录下《考试系统介绍》,内部有较为详尽功能说明,后续文档将说明数据表生成、源程序运行及部署。 内置功能: 用户管理:用户是系统操作者,该功能主要完成系统用户配置。 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。 岗位管理:配置系统用户所属担任职务。 菜单管理:配置系统菜单,操作权限,按钮权限标识等。 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。 字典管理:对系统中经常使用的一些较为固定的数据进行维护。 参数管理:对系统动态配置常用参数。 通知公告:系统通知公告信息发布维护。 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 登录日志:系统登录日志记录查询包含登录异常。 在线用户:当前系统中活跃用户状态监控。 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。 代码生成:前后端代码的生成(java、html、xml、sql)支持CRUD下载 。 系统接口:根据业务代码自动生成相关的api接口文档。 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。 缓存监控:对系统的缓存信息查询,命令统计等。 在线构建器:拖动表单元素生成相应的HTML代码。 连接池监视:监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈
好的,关于elementui自定义循环表单验证,您可以参考以下步骤: 1. 在需要验证的表单项中设置ref属性,例如: ``` <el-form ref="myForm"> <el-form-item v-for="(item,index) in formList" :key="index" :label="item.label"> <el-input v-model="item.value" /> </el-form-item> </el-form> ``` 2. 在需要添加验证规则的组件中设置prop属性,例如: ``` <el-form-item v-for="(item,index) in formList" :key="index" :label="item.label"> <el-input v-model="item.value" :placeholder="item.placeholder" :prop="'formList.'+index+'.value'" /> </el-form-item> ``` 其中prop的值被设置成"formList.index.value",这样可以对对应的表单项进行验证。 3. 在data中定义表单数据和验证规则,例如: ``` data() { return { formList: [ { label: '姓名', value: '', placeholder: '请输入姓名' }, { label: '年龄', value: '', placeholder: '请输入年龄' }, { label: '邮箱', value: '', placeholder: '请输入邮箱' } ], rules: { 'formList.0.value': [ { required: true, message: '请输入姓名', trigger: 'blur' } ], 'formList.1.value': [ { required: true, message: '请输入年龄', trigger: 'blur' }, { type: 'number', message: '年龄必须是数字值', trigger: 'blur' } ], 'formList.2.value': [ { required: true, message: '请输入邮箱', trigger: 'blur' }, { type: 'email', message: '邮箱格式不正确', trigger: ['blur', 'change'] } ] } } } ``` 在上述代码中,rules中定义了每个表单项对应的验证规则,并通过prop指定了对应的表单值。 4. 在提交表单时触发验证,例如: ``` submitForm() { this.$refs['myForm'].validate(valid => { if (valid) { // 表单验证通过 console.log('表单验证通过!'); } else { // 表单验证失败 console.log('表单验证失败!'); return false; } }); } ``` 在submitForm函数中,通过this.$refs['myForm'].validate()方法触发表单验证,将验证结果返回给回调函数valid。如果valid为true,则表单验证通过,否则表单验证失败。 以上就是elementui自定义循环表单验证的基本流程,您可以根据需求进行调整和优化。希望对您有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值