Browse Source

refactor: 抽离学生做题时每道题目的组件

ChenYangfan 6 years ago
parent
commit
392e09f423

+ 15 - 2
src/server/question.js

@@ -28,7 +28,6 @@ export function getOneQuestion ({ questionId }) {
 }
 
 // 创建题目
-// todo keyPoints tags knowledgeId
 export function createQuestion ({ stem = '', analysis, type, options = [], answer, knowledgeId }) {
   const data = {
     stem,
@@ -75,9 +74,23 @@ export function getCreatedQuestions ({ key = '', page = 1, size = 10, sort = ''
     .then(res => res.data)
 }
 
-// todo use it!
+// 获取创建的题目的所有ID,用于标志哪些题目可以编辑
 export function getCreatedQuestionIds () {
   return axios
     .get(useQuestionServer('/created/ids'))
     .then(res => res.data)
 }
+
+// 收藏题目
+export function starQuestion ({ questionId, collect }) {
+  const data = { collect }
+  return axios
+    .post(useQuestionServer(`/${questionId}/collect`), data)
+}
+
+// 评价题目
+export function rateQuestion ({ questionId, rate }) {
+  const data = { rate }
+  return axios
+    .post(useQuestionServer(`/${questionId}/rate`), data)
+}

+ 2 - 2
src/server/quiz.js

@@ -69,9 +69,9 @@ export function deleteQuiz ({ quizId }) {
 }
 
 // 提交答案
-export function submitQuizStudentAnswer ({ quizId, answers = {} }) {
+export function submitQuizStudentAnswer ({ quizId, answers = {}, records = {} }) {
   return axios
-    .post(useQuizServer(`/${quizId}/student-answer`), { answers })
+    .post(useQuizServer(`/${quizId}/student-answer`), { answers, records })
 }
 
 // 老师获取某一测试学生的作答列表

+ 1 - 1
src/views/Home.vue

@@ -5,7 +5,7 @@
         SEEC 课堂助手
       </h1>
       <router-link :to="{name: userInfo.role === 'TEACHER' ? 'teacher' : 'student'}">
-        <c-button v-if="userInfo.name" icon="face" block theme="green" class="mb-32">已登录用户:{{userInfo.name}}</c-button>
+        <c-button v-if="userInfo.name" icon="face" block theme="green" class="mb-16">已登录用户:{{userInfo.name}}</c-button>
       </router-link>
       <!--      <c-button @click="handleLogin" standout block theme="blue" class="mb-12">去登录</c-button>-->
       <!--      <c-button block theme="blue" @click="handleRegister">去注册</c-button>-->

+ 17 - 56
src/views/quiz/StudentDoQuiz.vue

@@ -10,30 +10,9 @@
         <div>上次得分:{{!isEmpty(answersInfo.score) ? answersInfo.score : '无提交历史'}}</div>
         <div>最近提交:{{!isEmpty(answersInfo.submitAt) ? momentFromNow(answersInfo.submitAt) : '无提交历史'}}</div>
       </div>
-      <div class="quiz-list-item" :key="question.id" v-for="(question, index) in questions"
-           :class="question.studentAnswer ? question.studentAnswer.correct === false ? 'wrong' : '' : ''">
-        <div class="quiz-list-item__index">{{ index + 1 }}.</div>
-        <div>
-          <markdown-section class="quiz-list-item-title" :raw="question.stem"/>
-          <div v-if="question.type === QuestionKind.choice && question.studentAnswer">
-            <label class="option" :key="index" v-for="(item, index) in question.options">
-              <input v-model="question.studentAnswer.answer" type="radio" :value="index"/>
-              <b class="mr-8">{{ index }}.</b>
-              <markdown-section style="display: inline" :raw="item"/>
-            </label>
-          </div>
-          <div v-else-if="question.type === QuestionKind.trueOrFalse && question.studentAnswer">
-            <label class="option">
-              <input v-model="question.studentAnswer.answer" type="radio" :value="true"/>
-              <c-icon>check</c-icon>
-            </label>
-            <label class="option">
-              <input v-model="question.studentAnswer.answer" type="radio" :value="false"/>
-              <c-icon>close</c-icon>
-            </label>
-          </div>
-        </div>
-      </div>
+      <template v-for="(question, index) in questions">
+        <do-quiz-question-card @choice="handleChoice" :key="question.id" :question-prop="question" :index="index"></do-quiz-question-card>
+      </template>
       <div class="mt-24">
         <c-button class="mb-16" theme="green" icon="check">提交答案</c-button>
       </div>
@@ -44,17 +23,19 @@
 import BackTitle from '@/components/Basic/BackTitle'
 import { createDefaultQuiz } from '@/views/quiz/utils/default'
 import QuestionKind from '@/enums/question-kind'
-import MarkdownSection from '@/views/quiz/components/MarkdownSection'
 import CButton from '@/components/Basic/CButton'
-import CIcon from '@/components/Basic/CIcon'
 import { getOneQuiz, getOneQuizStudentAnswer, submitQuizStudentAnswer } from '@/server/quiz'
 import { getQuestionsByQuiz } from '@/server/question'
 import momentFromNow from '@/utils/moment-from-now'
 import { isEmpty } from '@/utils/types'
+import DoQuizQuestionCard from '@/views/quiz/components/DoQuizQuestionCard'
 
+/**
+ * 起止时间算法见同级目录下的readme
+ */
 export default {
   name: 'StudentDoQuiz',
-  components: { CIcon, CButton, MarkdownSection, BackTitle },
+  components: { DoQuizQuestionCard, CButton, BackTitle },
   data () {
     return {
       quiz: createDefaultQuiz(),
@@ -65,6 +46,12 @@ export default {
         score: null,
         submitAt: null
       },
+
+      // 做题起止时间相关,时间为了方便加减采用毫秒表示
+      lastDate: new Date().valueOf(),
+      choiceDate: 0,
+      questionPointer: -1,
+
       QuestionKind
     }
   },
@@ -127,6 +114,9 @@ export default {
         this.$router.go(-1)
       }).catch(err => { this.$setErrorMessage(err.response.data.msg) })
     },
+    handleChoice ({ questionId }) {
+      console.log(questionId)
+    },
     momentFromNow,
     isEmpty
   }
@@ -154,33 +144,4 @@ export default {
 .red {
   color: $theme-red;
 }
-
-.quiz-list-item {
-  padding: 24px 8px;
-  display: flex;
-
-  &__index {
-    margin-right: 16px;
-  }
-
-  .option {
-    display: flex;
-    align-items: center;
-    margin-top: 8px;
-
-    input {
-      margin-right: 12px;
-    }
-  }
-}
-
-.wrong {
-  .quiz-list-item__index {
-    color: $theme-red;
-  }
-
-  .quiz-list-item-title {
-    color: $theme-red;
-  }
-}
 </style>

+ 91 - 0
src/views/quiz/components/DoQuizQuestionCard.vue

@@ -0,0 +1,91 @@
+<template>
+  <div class="quiz-list-item"
+       :class="question.studentAnswer ? question.studentAnswer.correct === false ? 'wrong' : '' : ''">
+    <div class="quiz-list-item__index">{{ index + 1 }}.</div>
+    <div>
+      <markdown-section class="quiz-list-item-title" :raw="question.stem"/>
+      <div v-if="question.type === QuestionKind.choice && question.studentAnswer">
+        <label class="option" :key="index" v-for="(item, index) in question.options">
+          <input @click.stop="emitChoice" v-model="question.studentAnswer.answer" type="radio" :value="index"/>
+          <b class="mr-8">{{ index }}.</b>
+          <markdown-section style="display: inline" :raw="item"/>
+        </label>
+      </div>
+      <div v-else-if="question.type === QuestionKind.trueOrFalse && question.studentAnswer">
+        <label class="option">
+          <input @click.stop="emitChoice" v-model="question.studentAnswer.answer" type="radio" :value="true"/>
+          <c-icon>check</c-icon>
+        </label>
+        <label class="option">
+          <input @click.stop="emitChoice" v-model="question.studentAnswer.answer" type="radio" :value="false"/>
+          <c-icon>close</c-icon>
+        </label>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import CIcon from '@/components/Basic/CIcon'
+import MarkdownSection from '@/views/quiz/components/MarkdownSection'
+import QuestionKind from '@/enums/question-kind'
+
+// fixme: 通过几层reference传值可能会造成一点困惑 - 咕咕咕
+export default {
+  components: { CIcon, MarkdownSection },
+  props: {
+    questionProp: Object,
+    index: Number
+  },
+  created () {
+    this.question = this.questionProp
+    this.$watch('questionProp', newValue => {
+      this.question = newValue
+    })
+  },
+  data () {
+    return {
+      question: null,
+      QuestionKind
+    }
+  },
+  methods: {
+    emitChoice () {
+      this.$emit('choice', { questionId: this.question.id })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import "~@/style/theme.scss";
+
+.quiz-list-item {
+  padding: 24px 8px;
+  display: flex;
+
+  &__index {
+    margin-right: 16px;
+  }
+
+  .option {
+    display: flex;
+    align-items: center;
+    margin-top: 8px;
+
+    input {
+      margin-right: 12px;
+    }
+  }
+}
+
+.wrong {
+  .quiz-list-item__index {
+    color: $theme-red;
+  }
+
+  .quiz-list-item-title {
+    color: $theme-red;
+  }
+}
+</style>

+ 6 - 0
src/views/quiz/readme.md

@@ -0,0 +1,6 @@
+# 做题时做题时间记录算法描述
+
+1. 在初始化页面的时候,记录`lastDate`为当前时间,当前题目指针`qustionPointer`为 -1(不指向任何题目)
+2. 当点击任意一道题目的答案时:`questionPointer`指向题目id,`choiceDate`设置为点击时的时间,之后每次点击本题目的选项都会更新`choiceDate`
+3. 如果点击了其他题目的答案时:`records`记录推入上道题目的`id`和起止时间(如果`id`在`records`中存在,则将两者时间差加到结束时间上),`questionPointer`指向新题目`id`,`lastDate`设置为上一个`choiceDate`,`choiceDate`设置为点击时的时间
+4. 提交时推入最后一道题的记录