Просмотр исходного кода

试卷添加页面 ExamPaperAdd.vue 开发

reghao 1 год назад
Родитель
Сommit
01797033fd

+ 23 - 4
src/api/exam.js

@@ -1,16 +1,19 @@
-import {delete0, get, post} from '@/utils/request'
+import { delete0, get, post } from '@/utils/request'
 
 const examApi = {
   getExamSubject: '/api/content/exam/subject',
   getExamSubjectKV: '/api/content/exam/subject/kv',
   getExamQuestion: '/api/content/exam/question',
   getExamQuestion1: '/api/content/exam/question/1',
+  getExamQuestionType: '/api/content/exam/question/type',
   postExamQuestion: '/api/content/exam/question',
-  postExamPaper: '/api/content/exam/paper',
+  postExamPaper: '/api/content/exam/paper1',
+  getExamPapers: '/api/content/exam/paper1',
   getExamResult: '/api/content/exam/result',
-  getExamPapers: '/api/content/exam/paper',
   getExamPaperScore: '/api/content/exam/paper/score',
-  getExam: '/api/content/exam'
+  getExam: '/api/content/exam',
+  getPaper: '/api/content/exam/paper',
+  postPaper: '/api/content/exam/paper'
 }
 
 export function getExamSubject() {
@@ -25,6 +28,14 @@ export function getExamQuestion() {
   return get(examApi.getExamQuestion1)
 }
 
+export function getExamQuestionType() {
+  return get(examApi.getExamQuestionType)
+}
+
+export function getExamQuestionPage(param) {
+  return get(examApi.getExamQuestion1, param)
+}
+
 export function deleteExamQuestion(questionId) {
   return delete0(examApi.getExamQuestion + '/' + questionId)
 }
@@ -64,3 +75,11 @@ export function getExamResult(examId) {
 export function getExamPaperScore(examId) {
   return get(examApi.getExamPaperScore + '/' + examId)
 }
+
+export function postPaper(data) {
+  return post(examApi.postPaper, data)
+}
+
+export function getPaper(examId) {
+  return get(examApi.getPaper + '/' + examId)
+}

+ 8 - 1
src/router/admin.js

@@ -16,6 +16,7 @@ const ExamSubject = () => import('views/exam/ExamSubject')
 const ExamQuestion = () => import('views/exam/ExamQuestion')
 const ExamQuestionAdd = () => import('views/exam/ExamQuestionAdd')
 const ExamPaper = () => import('views/exam/ExamPaper')
+const ExamPaperAdd = () => import('views/exam/ExamPaperAdd')
 const ExamMarker = () => import('views/exam/ExamMarker')
 const ExamResults = () => import('views/exam/ExamResults')
 
@@ -32,7 +33,7 @@ export default {
       meta: { needAuth: true }
     },
     {
-      path: '/exam/paper/:examId',
+      path: '/exam/paper1/:examId',
       name: 'ExamCard',
       component: ExamCard,
       meta: { needAuth: true }
@@ -73,6 +74,12 @@ export default {
       component: ExamPaper,
       meta: { needAuth: true }
     },
+    {
+      path: '/exam/paper/add',
+      name: 'ExamPaperAdd',
+      component: ExamPaperAdd,
+      meta: { needAuth: true }
+    },
     {
       path: '/exam/marker',
       name: 'ExamMarker',

+ 3 - 3
src/utils/util.js

@@ -76,18 +76,18 @@ function delCookie(name) {
   setCookie(name, null, -1)
 }
 
-const validFormAndInvoke = (formEl, success, message = '信息有误', fail = function () {
+const validFormAndInvoke = (formEl, success, message = '信息有误', fail = function() {
 }) => {
   if (!formEl) {
     return
   }
   formEl.validate(valid => {
-    if (valid) {// form valid succeed
+    if (valid) { // form valid succeed
       // do success function
       success()
       // reset fields
       formEl.resetFields()
-    } else {// form valid fail
+    } else { // form valid fail
       Vue.prototype.$notify({
         title: 'Tips',
         message: message,

+ 1 - 1
src/views/exam/ExamCard.vue

@@ -322,7 +322,7 @@ export default {
     }
   },
   created() {
-    this.getExamInfo()
+    // this.getExamInfo()
     // 页面数据加载的等待状态栏
     this.loading = this.$loading({
       body: true,

+ 644 - 0
src/views/exam/ExamPaperAdd.vue

@@ -0,0 +1,644 @@
+<template>
+  <el-container>
+    <el-header height="220">
+      <el-steps :active="curStep" simple>
+        <el-step title="组卷配置" icon="el-icon-edit" />
+        <el-step title="考试权限" icon="el-icon-lock" />
+        <el-step title="补充配置" icon="el-icon-setting" />
+      </el-steps>
+
+      <el-button v-show="curStep !== 1" style="margin-top: 10px" @click="curStep--">
+        上一步
+      </el-button>
+      <el-button v-show="curStep !== 3" style="float:right;margin-top: 10px" type="primary" @click="curStep++">
+        下一步
+      </el-button>
+      <el-button v-show="curStep === 3" style="float:right;margin-top: 10px" type="primary" @click="createExamPaper">
+        创建试卷
+      </el-button>
+    </el-header>
+    <el-main>
+      <!--设置试题信息-->
+      <el-card v-show="curStep === 1">
+        <el-radio-group
+          v-model="makeModel"
+          size="medium"
+          @change="makeModelChange"
+        >
+          <el-radio :label="1" border>自由组卷</el-radio>
+          <el-radio :label="2" border>题库组卷</el-radio>
+        </el-radio-group>
+        <span v-show="makeModel === 1" style="float: right;color: red;font-weight: bold">
+          {{ '试卷总分:' + sumTotalScore }}
+        </span>
+        <!-- 自由组卷内容-->
+        <div v-show="makeModel === 1" style="margin-top: 25px">
+          <el-button icon="el-icon-plus" size="mini" @click="showAddDialog">添加试题</el-button>
+          <el-table :data="addExamQuestion1" border style="margin-top: 10px">
+            <el-table-column
+              type="index"
+              label="序号"
+              width="50"
+            />
+            <el-table-column label="题目内容" align="center">
+              <template slot-scope="scope">
+                {{ scope.row.content.substr(0, 20) }}
+              </template>
+            </el-table-column>
+            <el-table-column
+              align="center"
+              label="题目类型"
+              prop="type"
+            />
+            <el-table-column label="单题分数" align="center">
+              <template slot-scope="scope">
+                <el-input-number v-model="scope.row.score" :min="1" :max="20" style="margin-left: 5px" />
+              </template>
+            </el-table-column>
+            <el-table-column label="操作" width="80" align="center">
+              <template slot-scope="scope">
+                <el-button
+                  type="danger"
+                  icon="el-icon-delete"
+                  circle
+                  @click="delQuestion(scope.row.questionId)"
+                />
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+        <!-- 题库组卷内容-->
+        <div v-show="makeModel === 2" style="margin-top: 25px">
+          <el-button icon="el-icon-plus" size="mini" @click="addBank">添加题库</el-button>
+          <!--存放题目的信息-->
+          <el-table :data="addExamQuestion2" border style="margin-top: 10px">
+            <el-table-column label="题库" width="155" align="center">
+              <template slot-scope="scope">
+                <el-select
+                  v-model="scope.row.bankName"
+                  clearable
+                  placeholder="请选择题库"
+                  style="margin-left: 5px"
+                >
+                  <el-option
+                    v-for="item in allSubject"
+                    :key="item.questionBank.bankId"
+                    :label="item.questionBank.bankName"
+                    :value="item.questionBank.bankName"
+                  />
+                </el-select>
+              </template>
+            </el-table-column>
+
+            <el-table-column label="单选题分数" align="center">
+              <template slot-scope="scope">
+                <el-input v-model="scope.row.singleScore" style="margin-left: 5px" />
+              </template>
+            </el-table-column>
+
+            <el-table-column label="多选题分数" align="center">
+              <template slot-scope="scope">
+                <el-input v-model="scope.row.multipleScore" style="margin-left: 5px" />
+              </template>
+            </el-table-column>
+
+            <el-table-column label="判断题分数" align="center">
+              <template slot-scope="scope">
+                <el-input v-model="scope.row.judgeScore" style="margin-left: 5px" />
+              </template>
+            </el-table-column>
+
+            <el-table-column label="简答题分数" align="center">
+              <template slot-scope="scope">
+                <el-input v-model="scope.row.shortScore" style="margin-left: 5px" />
+              </template>
+            </el-table-column>
+
+            <el-table-column label="操作" width="80" align="center">
+              <template slot-scope="scope">
+                <el-button type="danger" icon="el-icon-delete" circle @click="delBank(scope.row.bankId)" />
+              </template>
+            </el-table-column>
+
+          </el-table>
+        </div>
+      </el-card>
+
+      <!--设置考试权限-->
+      <el-card v-show="curStep === 2">
+        <el-radio-group v-model="examAuthority" size="medium">
+          <el-radio :label="1" border>完全公开</el-radio>
+          <el-radio :label="2" border>需要密码</el-radio>
+        </el-radio-group>
+
+        <el-alert
+          style="margin-top: 15px"
+          :title="examAuthority === 1? '开放的,任何人都可以进行考试!' : '半开放的,知道密码的人员才可以考试!'"
+          type="warning"
+        />
+
+        <el-input
+          v-show="examAuthority === 2"
+          v-model="examPassword"
+          style="margin-top: 15px;width: 20%"
+          type="password"
+          show-password
+          placeholder="输入考试密码"
+        />
+      </el-card>
+
+      <!--设置考试信息-->
+      <el-card v-show="curStep === 3">
+        <el-form ref="examInfoForm" :model="examInfo" :rules="examInfoRules" label-width="100px">
+          <el-form-item label="考试名称" prop="examName">
+            <el-input v-model="examInfo.examName" />
+          </el-form-item>
+          <el-form-item label="考试描述" prop="examDesc">
+            <el-input v-model="examInfo.examDesc" />
+          </el-form-item>
+          <el-form-item v-show="makeModel === 1" label="总分数" prop="totalScore">
+            <el-input-number :value="sumTotalScore" :disabled="true" />
+          </el-form-item>
+          <el-form-item label="及格分数" prop="passScore">
+            <el-input-number v-model="examInfo.passScore" :min="1" :max="parseInt(sumTotalScore)" />
+          </el-form-item>
+          <el-form-item label="考试时长(分钟)" prop="examDuration">
+            <el-input-number v-model="examInfo.examDuration" :min="1" :max="120" />
+          </el-form-item>
+          <el-form-item label="考试开始时间" prop="startTime">
+            <el-date-picker
+              v-model="examInfo.startTime"
+              style="margin-left: 5px"
+              type="datetime"
+              placeholder="考试开始时间"
+            />
+          </el-form-item>
+          <el-form-item label="考试结束时间" prop="endTime">
+            <el-date-picker
+              v-model="examInfo.endTime"
+              style="margin-left: 5px"
+              type="datetime"
+              placeholder="考试结束时间"
+            />
+          </el-form-item>
+        </el-form>
+      </el-card>
+    </el-main>
+    <el-dialog title="添加试题" :visible.sync="showQuestionDialog" width="80%" center>
+      <el-row>
+        <el-select
+          v-model="queryInfo.questionBank"
+          clearable
+          placeholder="请选择科目"
+          style="margin-left: 5px"
+          @change="subjectChange"
+        >
+          <el-option
+            v-for="item in allSubject"
+            :key="item.key"
+            :label="item.value"
+            :value="item.key"
+          />
+        </el-select>
+        <el-select v-model="queryInfo.questionType" clearable placeholder="请选择试题类型" @change="typeChange">
+          <el-option
+            v-for="item in allType"
+            :key="item.key"
+            :label="item.value"
+            :value="item.key"
+          />
+        </el-select>
+        <el-input
+          v-model="queryInfo.questionContent"
+          placeholder="题目内容"
+          style="margin-left: 5px;width: 220px"
+          prefix-icon="el-icon-search"
+          @blur="getQuestionInfo"
+        />
+        <el-button type="primary" style="float: right" @click="addQuToFree">确认{{ selectedTable.length }}项</el-button>
+      </el-row>
+      <el-table
+        ref="questionTable"
+        v-loading="loading"
+        highlight-current-row
+        :border="true"
+        :data="dataList"
+        tooltip-effect="dark"
+        style="width: 100%;margin-top: 25px;"
+        @selection-change="handleTableSectionChange"
+      >
+        <el-table-column
+          align="center"
+          type="selection"
+          width="55"
+        />
+        <el-table-column
+          align="center"
+          label="题目类型"
+          prop="type"
+        />
+        <el-table-column align="center" label="题目内容">
+          <template slot-scope="scope">
+            <span class="quContent">{{ scope.row.content.substr(0, 20) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column
+          align="center"
+          label="难度"
+          prop="level"
+        />
+        <el-table-column
+          align="center"
+          prop="subject"
+          label="所属科目"
+        />
+        <el-table-column
+          align="center"
+          prop="createBy"
+          label="创建人"
+        />
+
+        <el-table-column
+          align="center"
+          label="创建时间"
+        >
+          <template slot-scope="scope">
+            {{ scope.row.createAt }}
+          </template>
+        </el-table-column>
+      </el-table>
+      <!--分页-->
+      <el-pagination
+        style="margin-top: 25px"
+        layout="total, sizes, prev, pager, next, jumper"
+        :page-sizes="[10, 20, 50]"
+        :page-size="pageSize"
+        :current-page="currentPage"
+        :total="totalSize"
+        @current-change="handleCurrentChange"
+        @prev-click="handleCurrentChange"
+        @next-click="handleCurrentChange"
+      />
+    </el-dialog>
+  </el-container>
+</template>
+
+<script>
+import { getExamQuestionPage, getExamQuestionType, getExamSubjectKV, postPaper } from '@/api/exam'
+import { validFormAndInvoke } from '@/utils/util'
+
+export default {
+  name: 'ExamPaperAdd',
+  props: ['tagInfo'],
+  data() {
+    return {
+      currentPage: 1,
+      pageSize: 20,
+      totalSize: 0,
+      dataList: [],
+      // 查询题目的参数
+      queryInfo: {
+        'subjectId': null,
+        'type': null,
+        'level': null,
+        'pageNumber': 1,
+        'pageSize': 10
+      },
+      // 所有题库信息
+      allSubject: [],
+      allType: [],
+      // 当前的步骤
+      curStep: 1,
+      // 组卷模式
+      makeModel: 1,
+      // 添加考试题目信息(makeModel = 1的时候)
+      addExamQuestion1: [],
+      // 添加考试题目信息(makeModel = 2 的时候)
+      addExamQuestion2: [],
+      // 所有题目的对话框
+      showQuestionDialog: false,
+      // 对话框中题目表格的加载
+      loading: true,
+      // 所有题目的信息
+      questionInfo: [],
+      // 所有题目的对话框中表格被选中
+      selectedTable: [],
+      // 所有题目总数
+      total: 0,
+      // 考试权限(1公开, 2密码)
+      examAuthority: 1,
+      // 考试密码(权限为2时的密码)
+      examPassword: '',
+      // 补充的考试信息
+      examInfo: {
+        examId: null,
+        examDesc: null,
+        passScore: 0,
+        examDuration: 0,
+        startTime: null,
+        endTime: null
+      },
+      // 补充的考试信息的表单验证
+      examInfoRules: {
+        examName: [
+          {
+            required: true,
+            message: '请输入考试名称',
+            trigger: 'blur'
+          }
+        ],
+        passScore: [
+          {
+            required: true,
+            message: '请输入通过分数',
+            trigger: 'blur'
+          }
+        ],
+        examDuration: [
+          {
+            required: true,
+            message: '请输入考试时长',
+            trigger: 'blur'
+          }
+        ]
+      }
+    }
+  },
+  computed: {
+    // 计算总分
+    sumTotalScore() {
+      if (this.makeModel === 1) {
+        let score = 0
+        this.addExamQuestion2.forEach(item => {
+          score += parseInt(item.score)
+        })
+        return score
+      }
+    }
+  },
+  created() {
+    document.title = '组卷管理'
+    // 一创建就改变头部的面包屑
+    this.$emit('giveChildChangeBreakInfo', '添加考试', '添加考试')
+    // this.createTagsInParent()
+    this.getSubjects()
+    this.getQuestionTypes()
+  },
+  methods: {
+    // 向父组件中添加头部的tags标签
+    createTagsInParent() {
+      let flag = false
+      this.tagInfo.map(item => {
+        // 如果tags全部符合
+        if (item.name === '添加考试' && item.url === this.$route.path) {
+          flag = true
+        } else if (item.name === '添加考试' && item.url !== this.$route.path) {
+          this.$emit('updateTagInfo', '添加考试', this.$route.path)
+          flag = true
+        }
+      })
+      if (!flag) this.$emit('giveChildAddTag', '添加考试', this.$route.path)
+    },
+    // 获取所有的题库信息
+    getSubjects() {
+      getExamSubjectKV().then((resp) => {
+        if (resp.code === 0) {
+          this.allSubject = resp.data
+        } else {
+          this.$notify({
+            title: 'Tips',
+            message: resp.message,
+            type: 'error',
+            duration: 2000
+          })
+        }
+      })
+    },
+    getQuestionTypes() {
+      getExamQuestionType().then((resp) => {
+        if (resp.code === 0) {
+          this.allType = resp.data
+        } else {
+          this.$notify({
+            title: 'Tips',
+            message: resp.message,
+            type: 'error',
+            duration: 2000
+          })
+        }
+      })
+    },
+    // 删除当前需要去除的题库
+    delBank(bankId) {
+      this.addExamQuestion2.forEach((item, index) => {
+        if (item.bankId === bankId) this.addExamQuestion2.splice(index, 1)
+      })
+    },
+    // 添加题库组卷中的题库
+    addBank() {
+      this.addExamQuestion2.push({
+        'bankName': '',
+        'singleScore': 1,
+        'multipleScore': 1,
+        'judgeScore': 1,
+        'shortScore': 1
+      })
+    },
+    // 自由组卷时添加试题
+    showAddDialog() {
+      this.showQuestionDialog = true
+      this.queryInfo.subjectId = null
+      this.queryInfo.type = null
+      this.queryInfo.level = null
+      this.queryInfo.pageNumber = this.currentPage
+      this.queryInfo.pageSize = this.pageSize
+      this.getQuestionInfo()
+    },
+    // 自由组卷时删除试题
+    delQuestion(questionId) {
+      this.addExamQuestion1.forEach((item, index) => {
+        if (item.questionId === questionId) this.addExamQuestion1.splice(index, 1)
+      })
+    },
+    // 题目类型变化
+    typeChange(val) {
+      this.queryInfo.type = val
+      this.queryInfo.pageNumber = this.currentPage
+      this.queryInfo.pageSize = this.pageSize
+      this.getQuestionInfo()
+    },
+    // 题库变化
+    subjectChange(val) {
+      this.queryInfo.subjectId = val
+      this.queryInfo.pageNumber = this.currentPage
+      this.queryInfo.pageSize = this.pageSize
+      this.getQuestionInfo()
+    },
+    // 获取题目信息
+    getQuestionInfo() {
+      this.dataList = []
+      getExamQuestionPage(this.queryInfo).then(resp => {
+        if (resp.code === 0) {
+          this.dataList = resp.data.list
+          this.totalSize = resp.data.totalSize
+          this.loading = false
+        } else {
+          this.$notify({
+            title: '提示',
+            message: resp.msg,
+            type: 'warning',
+            duration: 3000
+          })
+        }
+      }).catch(error => {
+        this.$notify({
+          title: '提示',
+          message: error.message,
+          type: 'error',
+          duration: 3000
+        })
+      })
+    },
+    // 处理表格被选中
+    handleTableSectionChange(val) {
+      this.selectedTable = val
+    },
+    // 分页页面大小改变
+    handleSizeChange(val) {
+      this.queryInfo.pageSize = val
+      this.getQuestionInfo()
+    },
+    // 分页插件的页数
+    handleCurrentChange(val) {
+      this.queryInfo.pageNo = val
+      this.getQuestionInfo()
+    },
+    // 自由组卷中选中的题目添加进去
+    addQuToFree() {
+      this.selectedTable.forEach(item => {
+        if (!this.addExamQuestion1.some(i2 => {
+          return i2.questionId === item.id
+        })) { // 不存在有当前题目
+          this.addExamQuestion1.push({
+            'questionId': item.questionId,
+            'content': item.content,
+            'type': item.type,
+            'score': 1
+          })
+        }
+      })
+      this.showQuestionDialog = false
+    },
+    // 组卷模式变化
+    makeModelChange() {
+      this.$confirm('此操作将丢失当前组卷数据, 是否继续?', 'Tips', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.makeModel === 1 ? this.addExamQuestion1 = [] : this.addExamQuestion2 = []
+      }).catch(() => {
+      })
+    },
+    // 创建试卷
+    createExamPaper() {
+      validFormAndInvoke(this.$refs['examInfoForm'], () => {
+        if ((this.makeModel === 1 && this.addExamQuestion1.length === 0) ||
+          (this.makeModel === 2 && this.addExamQuestion2.length === 0)) {
+          this.$message.error('试卷还没有添加试题')
+          return false
+        }
+
+        // 构造数据对象(考试信息)
+        const exam = this.examInfo
+        exam.totalScore = this.sumTotalScore
+        exam.status = 1
+        // 权限id设置
+        exam.type = this.examAuthority
+        if (this.examAuthority === 2) { // 考试密码
+          if (this.examPassword === '') { // 当前用户选择了需要密码权限,但是密码为空
+            this.$message.error('当前权限为需要密码,但是密码为空')
+            return false
+          }
+          exam.password = this.examPassword
+        }
+
+        if (this.makeModel === 1 && !this.addExamQuestion1.some(item => item.bankId === '')) {
+          // 题目id数组
+          const questionIds = []
+          // 题目成绩数组
+          const scores = []
+          this.addExamQuestion1.forEach(item => {
+            questionIds.push(item.questionId)
+            scores.push(item.score)
+          })
+          exam.questionIds = questionIds.join(',')
+          exam.scores = scores.join(',')
+          this.addExamPaper(exam)
+        } else if (this.makeModel === 2) {
+          const bankNames = []
+          this.addExamQuestion2.forEach(item => bankNames.push(item.bankName))
+          exam.bankNames = bankNames.join(',')
+          exam.singleScore = this.addExamQuestion2[0].singleScore
+          exam.multipleScore = this.addExamQuestion2[0].multipleScore
+          exam.judgeScore = this.addExamQuestion2[0].judgeScore
+          exam.shortScore = this.addExamQuestion2[0].shortScore
+          this.addExamPaper(exam)
+        } else {
+          this.$message.error('请检查考试规则设置是否完整')
+        }
+      }, '请检查考试规则设置是否完整')
+    },
+    addExamPaper(examInfo) {
+      postPaper(examInfo).then(resp => {
+        if (resp.code === 0) {
+          this.$notify({
+            title: '提示',
+            message: '试卷已创建',
+            type: 'warning',
+            duration: 3000
+          })
+          this.$router.push('/exam/paper')
+        } else {
+          this.$notify({
+            title: '提示',
+            message: resp.msg,
+            type: 'warning',
+            duration: 3000
+          })
+        }
+      }).catch(error => {
+        this.$notify({
+          title: '提示',
+          message: error.message,
+          type: 'error',
+          duration: 3000
+        })
+      })
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+
+.el-container {
+  width: 100%;
+  height: 100%;
+}
+
+.el-container {
+  animation: leftMoveIn .7s ease-in;
+}
+
+@keyframes leftMoveIn {
+  0% {
+    transform: translateX(-100%);
+    opacity: 0;
+  }
+  100% {
+    transform: translateX(0%);
+    opacity: 1;
+  }
+}
+</style>

+ 0 - 3
src/views/exam/ExamQuestion.vue

@@ -402,11 +402,8 @@
     >
 
       <el-form ref="removeForm" :model="removeForm" :rules="removeFormRules">
-
         <el-form-item label="题库名称" label-width="120px" prop="subjectId">
-
           <el-select v-model="removeForm.subjectId" multiple placeholder="请选择题库">
-
             <el-option
               v-for="item in allSubject"
               :key="item.key"

+ 1 - 1
src/views/exam/ExamQuestionAdd.vue

@@ -440,7 +440,7 @@ export default {
       this.addQuForm.options[0] = {
         id: this.addQuForm.options.length,
         correct: true,
-        content: this.answerContent,
+        content: this.answerContent8,
         pos: this.addQuForm.options.length + 1,
         images: [],
         analysis: ''