소스 검색

feat: add update quiz and problem feat

WuXinyu 6 년 전
부모
커밋
51480f26db

+ 5 - 1
src/components/Editor.vue

@@ -43,9 +43,13 @@ export default {
     }
   },
   watch: {
-    initContent(val) {
+    initContent: {
+      immediate: true,
+      handler(val) {
         // eslint-disable-next-line no-unused-expressions
         this.editor?.setContent(markdownToHTML(val))
+        this.$emit('update-markdown', val)
+      }
     }
   },
   mounted() {

+ 15 - 4
src/router/index.js

@@ -40,6 +40,11 @@ const routes = [{
   path: '/quiz-detail/:quizId',
   name: 'quiz-detail',
   component: () => import('../views/quiz/QuizDetail.vue')
+}, {
+  meta: { roles: ['teacher'] },
+  path: '/quiz-detail/:quizId/edit',
+  name: 'edit-quiz',
+  component: () => import('../views/quiz/EditQuiz.vue')
 }, {
   meta: { roles: ['student'] },
   path: '/quiz/:slideId',
@@ -55,11 +60,21 @@ const routes = [{
   path: '/questions/:questionId',
   name: 'question-detail',
   component: () => import('../views/quiz/QuestionDetail.vue')
+}, {
+  meta: { roles: ['teacher'] },
+  path: '/questions/:questionId/edit',
+  name: 'edit-question',
+  component: () => import('../views/quiz/EditQuestion.vue')
 }, {
   meta: { roles: ['teacher'] },
   path: '/create-question',
   name: 'create-question',
   component: () => import('../views/quiz/CreateQuestion.vue')
+}, {
+  meta: { roles: ['student'] },
+  path: '/student/quiz/:quizId/do-quiz',
+  name: 'student-do-quiz',
+  component: () => import(/* webpackChunkName: "student" */ '../views/student/components/StudentDoQuiz.vue')
 }, {
   meta: { roles: ['teacher'] },
   path: '/teacher',
@@ -132,10 +147,6 @@ const routes = [{
       path: ':quizId',
       name: 'student-quiz-detail',
       component: () => import(/* webpackChunkName: "student" */ '../views/student/components/StudentQuizDetail.vue')
-    }, {
-      path: ':quizId/do-quiz',
-      name: 'student-do-quiz',
-      component: () => import(/* webpackChunkName: "student" */ '../views/student/components/StudentDoQuiz.vue')
     }]
   }, {
     path: 'courses',

+ 1 - 1
src/server/question.js

@@ -27,7 +27,7 @@ export function createQuestion(questionBody) {
 
 export function updateQuestion({ questionId, questionBody }) {
   return axios
-    .post(useQuestionServer(`/${questionId}`), questionBody)
+    .put(useQuestionServer(`/${questionId}`), questionBody)
     .then(res => res.data)
 }
 

+ 1 - 1
src/server/quiz.js

@@ -41,7 +41,7 @@ export function updateQuiz({
 }) {
   const data = { id, name, quizTime, slideId, questions }
   return axios
-    .post(useQuizServer(`/${id}`), data)
+    .put(useQuizServer(`/${id}`), data)
     .then(res => res.data)
 }
 

+ 5 - 6
src/views/quiz/CreateQuestion.vue

@@ -2,8 +2,7 @@
   <div class="create-question">
     <back-title>创建题目</back-title>
     <question-kind-bar v-model="currentKind" />
-    <choice-question-editor v-if="currentKind === QuestionKind.choice" @submit="create($event, QuestionKind.choice)" />
-    <true-false-question-editor v-else @submit="create($event, QuestionKind.trueOrFalse)" />
+    <question-editor-factory :kind="currentKind" @submit="create($event, currentKind)" />
   </div>
 </template>
 
@@ -11,13 +10,13 @@
 import BackTitle from '@/components/Basic/BackTitle'
 import QuestionKindBar from '@/components/QuestionKindBar'
 import QuestionKind from '@/server/enums/question-kind'
-import ChoiceQuestionEditor from '@/views/quiz/components/ChoiceQuestionEditor'
-import TrueFalseQuestionEditor from '@/views/quiz/components/TrueFalseQuestionEditor'
 import { createQuestion } from '@/server/question'
+import QuestionEditorFactory from '@/views/quiz/components/QuestionEditorFactory'
+
 export default {
-  components: { TrueFalseQuestionEditor, ChoiceQuestionEditor, QuestionKindBar, BackTitle },
+  components: { QuestionEditorFactory, QuestionKindBar, BackTitle },
   data() {
-    return { currentKind: QuestionKind.choice, QuestionKind }
+    return { currentKind: QuestionKind.choice }
   },
   methods: {
     create(questionBody, kind) {

+ 7 - 49
src/views/quiz/CreateQuiz.vue

@@ -1,69 +1,27 @@
 <template>
   <div>
-    <back-title>测试详情</back-title>
-    <form @submit.prevent="handleSubmit">
-      <label>
-        测试名称:
-        <input v-model="quiz.name" required placeholder="请输入测试名称" />
-      </label>
-      <label>
-        测试进行时课件的状态(测试时间):
-        <select v-model="quiz.quizTime" required >
-          <option value="">请选择测试时间</option>
-          <option :key="index" v-for="(item, index) in SlideStateTranslation" :value="index">{{ item }}</option>
-        </select>
-      </label>
-      <div class="mb-16" :key="question.id" v-for="(question, index) in selectedQuestions">
-        <div>{{ index + 1 }}.</div>
-        <a @click="moveIndexQuestionUp(index)">向上移动</a>
-        <a @click="moveIndexQuestionUp(index + 1)">向下移动</a>
-        <question-detail-factory :question="question" />
-      </div>
-      <question-table v-model="selectedQuestions" selectable />
-      <button>创建</button>
-    </form>
+    <back-title>创建测试</back-title>
+    <quiz-editor @submit="handleSubmit" />
   </div>
 </template>
 
 <script>
-import { naming as QuestionKindNaming } from '@/server/enums/question-kind'
-import { QuizStateNaming } from '@/server/enums/quiz-state'
-import SlideStateTranslation from '@/server/enums/slide-state-translation'
 import BackTitle from '@/components/Basic/BackTitle'
-import QuestionTable from '@/views/quiz/components/QuestionTable'
-import QuestionDetailFactory from '@/views/quiz/components/QuestionDetailFactory'
 import { createQuiz } from '@/server/quiz'
+import QuizEditor from '@/views/quiz/components/QuizEditor'
 
 export default {
   name: 'CreateQuiz',
-  components: { QuestionDetailFactory, QuestionTable, BackTitle },
-  data() {
-    return {
-      quiz: { name: '', quizTime: '', slideId: this.$route.params.slideId },
-      selectedQuestions: [],
-
-      QuestionKindNaming,
-      QuizStateNaming,
-      SlideStateTranslation
-    }
-  },
+  components: { QuizEditor, BackTitle },
   methods: {
-    handleSubmit() {
-      createQuiz({ ...this.quiz, questions: this.selectedQuestions.map(item => item.id) })
+    handleSubmit(quizData) {
+      const { slideId } = this.$route.params
+      createQuiz({ ...quizData, slideId })
         .then(() => this.$setSuccessMessage('创建成功'))
         .then(() => this.$router.go(-1))
         .catch(err =>
           this.$setErrorMessage(err.response.data.msg)
         )
-    },
-    moveIndexQuestionUp(index) {
-      if (index < this.selectedQuestions.length && index >= 1) {
-        const nextQuestions = [...this.selectedQuestions]
-        const before = nextQuestions[index - 1]
-        nextQuestions[index - 1] = nextQuestions[index]
-        nextQuestions[index] = before
-        this.selectedQuestions = nextQuestions
-      }
     }
   }
 }

+ 50 - 0
src/views/quiz/EditQuestion.vue

@@ -0,0 +1,50 @@
+<template>
+  <div class="edit-question">
+    <back-title>修改题目</back-title>
+    <question-editor-factory :kind="question.kind" :init-value="question" @submit="edit($event)" />
+  </div>
+</template>
+
+<script>
+import BackTitle from '@/components/Basic/BackTitle'
+import QuestionKind from '@/server/enums/question-kind'
+import { getQuestionById, updateQuestion } from '@/server/question'
+import QuestionEditorFactory from '@/views/quiz/components/QuestionEditorFactory'
+
+export default {
+  components: { QuestionEditorFactory, BackTitle },
+  data() {
+    return {
+      currentKind: QuestionKind.choice,
+      question: {}
+    }
+  },
+  mounted() {
+    this.fetchQuestionDetail()
+  },
+  methods: {
+    fetchQuestionDetail() {
+      const { questionId } = this.$route.params
+      getQuestionById(questionId)
+        .then(res => (this.question = res))
+        .catch(err => this.$setErrorMessage(err.response.data.msg))
+    },
+    edit(questionBody) {
+      updateQuestion({ questionId: questionBody.id, questionBody })
+        .then(() => this.$setSuccessMessage('修改成功'))
+        .then(() => this.$router.go(-1))
+        .catch(err =>
+          this.$setErrorMessage(err.response.data.msg)
+        )
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+  .edit-question {
+    margin: 0 auto;
+    max-width: 1100px;
+    padding: 64px 24px;
+  }
+</style>

+ 47 - 0
src/views/quiz/EditQuiz.vue

@@ -0,0 +1,47 @@
+<template>
+  <div>
+    <back-title>修改测试</back-title>
+    <quiz-editor :init-quiz="initQuiz" @submit="handleSubmit" />
+  </div>
+</template>
+
+<script>
+import BackTitle from '@/components/Basic/BackTitle'
+import { getQuizById, updateQuiz } from '@/server/quiz'
+import QuizEditor from '@/views/quiz/components/QuizEditor'
+
+export default {
+  name: 'EditQuiz',
+  components: { QuizEditor, BackTitle },
+  data() {
+    return {
+      initQuiz: {}
+    }
+  },
+  mounted() {
+    this.fetchQuizDetail()
+  },
+  methods: {
+    fetchQuizDetail() {
+      const { quizId } = this.$route.params
+      getQuizById(quizId)
+        .then(res => (this.initQuiz = res))
+        .catch(err =>
+          this.$setErrorMessage(err.response.data.msg)
+        )
+    },
+    handleSubmit(quizData) {
+      updateQuiz({ ...quizData, slideId: this.initQuiz?.slide?.id })
+        .then(() => this.$setSuccessMessage('创建成功'))
+        .then(() => this.$router.go(-1))
+        .catch(err =>
+          this.$setErrorMessage(err.response.data.msg)
+        )
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 3 - 1
src/views/quiz/QuizView.vue

@@ -5,7 +5,9 @@
       <div>{{ item.name }}</div>
       <div>测试时间:{{ SlideStateTranslation[item.quizTime] }}</div>
       <div>测试状态:{{ QuizStateNaming[item.state] }}</div>
-      <router-link :to="`/student/quiz/${item.id}`"><button>详情</button></router-link>
+      <router-link :to="{ name: 'student-quiz-detail', params: { quizId: item.id }}">
+        <button>详情</button>
+      </router-link>
     </div>
   </tab-side-component>
 </template>

+ 14 - 2
src/views/quiz/TeacherQuizSetting.vue

@@ -1,13 +1,20 @@
 <template>
   <div class="teacher-quiz-setting">
     <back-title>具体课件的测试列表</back-title>
-    <router-link :to="`${$route.path}/create-quiz`"><button>新增测试</button></router-link>
+    <router-link :to="{ name:'teacher-quiz-create', params: { slideId: this.slideId }}">
+      <button>新增测试</button>
+    </router-link>
     <router-link to="/questions"><button>题库管理</button></router-link>
     <div v-for="item in quizList" :key="item.id">
       <div>{{ item.name }}</div>
       <div>测试时间:{{ SlideStateTranslation[item.quizTime] }}</div>
       <div>测试状态:{{ QuizStateNaming[item.state] }}</div>
-      <router-link :to="`/quiz-detail/${item.id}`"><button>详情</button></router-link>
+      <router-link :to="{ name: 'quiz-detail', params: { quizId: item.id }}">
+        <button>详情</button>
+      </router-link>
+      <router-link :to="{ name: 'edit-quiz', params: { quizId: item.id }}">
+        <button>修改</button>
+      </router-link>
     </div>
   </div>
 </template>
@@ -28,6 +35,11 @@ export default {
       QuizStateNaming
     }
   },
+  computed: {
+    slideId() {
+      return this.$route.params.slideId
+    }
+  },
   mounted() {
     this.fetchQuiz()
   },

+ 12 - 2
src/views/quiz/components/ChoiceQuestionEditor.vue

@@ -41,7 +41,7 @@
     </editor-section>
     <editor-section title="解析">
       <editor
-        :init-content="value.analysis"
+        :init-content="initValue.analysis"
         @update-markdown="value.analysis = $event"
       />
     </editor-section>
@@ -80,7 +80,17 @@ export default {
   watch: {
     value: {
       deep: true,
-      handler: nextValue => this.$emit('input', nextValue)
+      handler(nextValue) {
+        return this.$emit('input', nextValue)
+      }
+    },
+    initValue: {
+      deep: true,
+      immediate: true,
+      handler(nextInitValue) {
+        this.value.options = nextInitValue.options
+        this.value.answer = nextInitValue.answer
+      }
     }
   },
   methods: {

+ 3 - 2
src/views/quiz/components/QuestionCard.vue

@@ -9,7 +9,8 @@
         <a v-if="selectable" class="action" style="cursor: pointer" @click="$emit('toggle-select', questionId)">
           {{ selected ? "取消选择" : "选择" }}
         </a>
-        <router-link class="action" style="margin-left: 16px;" :to="`/questions/${questionId}`">详情</router-link>
+        <router-link class="action" style="margin-left: 16px;" :to="{ name: 'edit-question', params: { questionId }}">修改</router-link>
+        <router-link class="action" style="margin-left: 16px;" :to="{ name: 'question-detail', params: { questionId }}">详情</router-link>
       </span>
     </div>
     <markdown-section :raw="stem" />
@@ -29,7 +30,7 @@ export default {
       default: ''
     },
     questionId: {
-      type: Number,
+      type: String,
       required: true
     },
     kind: {

+ 37 - 0
src/views/quiz/components/QuestionEditorFactory.vue

@@ -0,0 +1,37 @@
+<template>
+  <choice-question-editor v-if="kind === QuestionKind.choice" :initValue="initValue" @submit="handleSubmit" />
+  <true-false-question-editor v-else-if="kind === QuestionKind.trueOrFalse" :initValue="initValue" @submit="handleSubmit" />
+</template>
+
+<script>
+import QuestionKind from '@/server/enums/question-kind'
+import ChoiceQuestionEditor from '@/views/quiz/components/ChoiceQuestionEditor'
+import TrueFalseQuestionEditor from '@/views/quiz/components/TrueFalseQuestionEditor'
+
+export default {
+  name: 'QuestionEditorFactory',
+  components: { TrueFalseQuestionEditor, ChoiceQuestionEditor },
+  props: {
+    initValue: {
+      type: Object,
+      default: () => null
+    },
+    kind: {
+      type: String,
+      default: ''
+    }
+  },
+  data() {
+    return { QuestionKind }
+  },
+  methods: {
+    handleSubmit(event) {
+      this.$emit('submit', { ...this.initValue, ...event })
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 79 - 0
src/views/quiz/components/QuizEditor.vue

@@ -0,0 +1,79 @@
+<template>
+  <form @submit.prevent="handleSubmit">
+    <label>
+      测试名称:
+      <input v-model="quiz.name" required placeholder="请输入测试名称" />
+    </label>
+    <label>
+      测试进行时课件的状态(测试时间):
+      <select v-model="quiz.quizTime" required >
+        <option value="">请选择测试时间</option>
+        <option :key="index" v-for="(item, index) in SlideStateTranslation" :value="index">{{ item }}</option>
+      </select>
+    </label>
+    <div class="mb-16" :key="question.id" v-for="(question, index) in selectedQuestions">
+      <div>{{ index + 1 }}.</div>
+      <a @click="moveIndexQuestionUp(index)">向上移动</a>
+      <a @click="moveIndexQuestionUp(index + 1)">向下移动</a>
+      <question-detail-factory :question="question" />
+    </div>
+    <question-table v-model="selectedQuestions" selectable />
+    <button>提交</button>
+  </form>
+</template>
+
+<script>
+import { naming as QuestionKindNaming } from '@/server/enums/question-kind'
+import { QuizStateNaming } from '@/server/enums/quiz-state'
+import SlideStateTranslation from '@/server/enums/slide-state-translation'
+import QuestionTable from '@/views/quiz/components/QuestionTable'
+import QuestionDetailFactory from '@/views/quiz/components/QuestionDetailFactory'
+
+export default {
+  name: 'QuizEditor',
+  components: { QuestionDetailFactory, QuestionTable },
+  props: {
+    initQuiz: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  data() {
+    return {
+      quiz: { name: '', quizTime: '' },
+      selectedQuestions: [],
+
+      QuestionKindNaming,
+      QuizStateNaming,
+      SlideStateTranslation
+    }
+  },
+  watch: {
+    initQuiz: {
+      immediate: true,
+      handler({ id, name, quizTime, questions = [] } = {}) {
+        this.quiz = { id, name, quizTime }
+        this.selectedQuestions = questions
+      }
+    }
+  },
+  methods: {
+    handleSubmit() {
+      this.$emit('submit', { ...this.quiz, questions: this.selectedQuestions.map(item => item.id) })
+    },
+    moveIndexQuestionUp(index) {
+      if (index < this.selectedQuestions.length && index >= 1) {
+        const nextQuestions = [...this.selectedQuestions]
+        const before = nextQuestions[index - 1]
+        nextQuestions[index - 1] = nextQuestions[index]
+        nextQuestions[index] = before
+        this.selectedQuestions = nextQuestions
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 7 - 0
src/views/quiz/components/TrueFalseQuestionEditor.vue

@@ -56,6 +56,13 @@ export default {
     value: {
       deep: true,
       handler: nextValue => this.$emit('input', nextValue)
+    },
+    initValue: {
+      deep: true,
+      immediate: true,
+      handler(nextInitValue) {
+        this.value.answer = nextInitValue.answer
+      }
     }
   },
   methods: {