Kaynağa Gözat

feat: 创建、修改、删除题目

ChenYangfan 6 yıl önce
ebeveyn
işleme
b0b2061e4b

+ 1 - 1
src/App.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="app">
     <slide-up-transition>
-      <keep-alive :include="['EditQuiz', 'CreateQuiz']">
+      <keep-alive :include="['EditQuiz', 'CreateQuiz', 'Questions']">
         <router-view/>
       </keep-alive>
     </slide-up-transition>

+ 2 - 2
src/server/enums/question-kind.js

@@ -1,6 +1,6 @@
 export const kinds = {
-  choice: 'CHOICE',
-  trueOrFalse: 'TRUE_FALSE'
+  choice: 'choice',
+  trueOrFalse: 'true_false'
 }
 
 export const naming = {

+ 56 - 0
src/server/question.js

@@ -1,7 +1,9 @@
 import axios from '@/plugins/axios'
 import { useQuestionServer } from '@/config/server-info'
 import generateSearchParams from '@/server/utils/generate-search-params'
+import kinds from '@/server/enums/question-kind'
 
+// 教师获取题目列表
 export function getQuestionList ({ key = '', page = 1, size = 10, sort = '' }) {
   const params = generateSearchParams({ key, page, sort, size })
   return axios
@@ -11,8 +13,62 @@ export function getQuestionList ({ key = '', page = 1, size = 10, sort = '' }) {
     .then(res => res.data)
 }
 
+// 教师获取某一测试的题目
 export function getQuestionsOfQuiz ({ quizId }) {
   return axios
     .get(useQuestionServer('/quiz/' + quizId))
     .then(res => res.data)
 }
+
+// 获取某一题目的信息
+export function getOneQuestion ({ questionId }) {
+  return axios
+    .get(useQuestionServer('/') + questionId)
+    .then(res => res.data)
+}
+
+// 创建题目
+// todo keyPoints tags knowledgeId
+export function createQuestion ({ stem = '', analysis, type, options = [], answer }) {
+  const data = {
+    stem,
+    analysis,
+    type,
+    answer
+  }
+  if (type === kinds.choice) {
+    data.options = options
+  }
+  return axios
+    .post(useQuestionServer(), data)
+    .then(res => res.data)
+}
+
+// 修改题目
+export function modifyQuestion ({ questionId, stem = '', analysis, type, options = [], answer }) {
+  const data = {
+    stem,
+    analysis,
+    type,
+    answer
+  }
+  if (type === kinds.choice) {
+    data.options = options
+  }
+  return axios
+    .put(useQuestionServer('/' + questionId), data)
+}
+
+// 删除题目
+export function deleteQuestion ({ questionId }) {
+  return axios
+    .delete(useQuestionServer('/' + questionId))
+}
+
+// 获取创建的题目
+export function getCreatedQuestions ({ key = '', page = 1, size = 10, sort = '' }) {
+  const params = generateSearchParams({ key, page, sort, size })
+  return axios
+    .get(useQuestionServer('/created'), { params })
+    .then(res => res.data)
+}

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

@@ -11,6 +11,7 @@ import BackTitle from '@/components/Basic/BackTitle'
 import QuestionKindBar from '@/components/QuestionKindBar'
 import QuestionKind from '@/server/enums/question-kind'
 import QuestionEditorFactory from '@/views/quiz/components/QuestionEditorFactory'
+import { createQuestion } from '@/server/question'
 
 export default {
   name: 'CreateQuestion',
@@ -19,17 +20,13 @@ export default {
     return { currentKind: QuestionKind.choice }
   },
   mounted () {
-    this.$setErrorMessage('本页面后端 API 需要进行改进,暂不能使用')
   },
   methods: {
-    create (questionBody, kind) {
-      this.$setErrorMessage('本页面后端 API 需要进行改进,暂不能使用')
-      // createQuestion({ ...questionBody, kind })
-      //   .then(() => this.$setSuccessMessage('创建成功'))
-      //   .then(() => this.$router.go(-1))
-      //   .catch(err =>
-      //     this.$setErrorMessage(err.response.data.msg)
-      //   )
+    create (questionBody, type) {
+      createQuestion({ ...questionBody, type })
+        .then(() => this.$setSuccessMessage('创建题目成功'))
+        .then(() => this.$router.go(-1))
+        .catch(err => this.$setErrorMessage(err.response.data.msg))
     }
   }
 }

+ 36 - 23
src/views/quiz/EditQuestion.vue

@@ -1,7 +1,10 @@
 <template>
   <div class="edit-question">
-    <back-title>修改题目</back-title>
-    <question-editor-factory :kind="question.kind" :init-value="question" @submit="edit($event)" />
+    <back-title>
+      修改题目
+      <c-button theme="red" icon="close" text :left="12" @click="handleDeleteQuestion">删除题目</c-button>
+    </back-title>
+    <question-editor-factory :kind="question.type" :init-value="question" @submit="edit($event)"/>
   </div>
 </template>
 
@@ -10,43 +13,53 @@ import BackTitle from '@/components/Basic/BackTitle'
 import QuestionKind from '@/server/enums/question-kind'
 // import { getQuestionById, updateQuestion } from '@/server/question-backup'
 import QuestionEditorFactory from '@/views/quiz/components/QuestionEditorFactory'
+import { deleteQuestion, getOneQuestion, modifyQuestion } from '@/server/question'
+import CButton from '@/components/Basic/CButton'
 
 export default {
   name: 'EditQuestion',
-  components: { QuestionEditorFactory, BackTitle },
-  data() {
+  components: { CButton, QuestionEditorFactory, BackTitle },
+  data () {
     return {
       currentKind: QuestionKind.choice,
       question: {}
     }
   },
-  mounted() {
-    this.$setErrorMessage('这部分API需要等题库系统加入权限管理')
+  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))
+    fetchQuestionDetail () {
+      const { questionId } = this.$route.params
+      getOneQuestion({ 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)
-      //   )
+    edit (questionBody) {
+      modifyQuestion({ ...questionBody, questionId: questionBody.id })
+        .then(() => this.$setSuccessMessage('修改成功'))
+        .then(() => this.$router.go(-1))
+        .catch(err =>
+          this.$setErrorMessage(err.response.data.msg)
+        )
+    },
+    handleDeleteQuestion () {
+      const { questionId } = this.$route.params
+      deleteQuestion({ questionId })
+        .then(res => {
+          this.$setSuccessMessage('删除成功')
+          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;
-  }
+.edit-question {
+  margin: 0 auto;
+  max-width: 800px;
+  padding: 64px 24px;
+}
 </style>

+ 29 - 11
src/views/quiz/QuestionDetail.vue

@@ -1,7 +1,13 @@
 <template>
   <div class="question-detail">
-    <back-title>题目详情</back-title>
-    <question-detail-factory :question="question" />
+    <back-title>
+      题目详情
+      <c-button theme="red" icon="close" text :left="12" @click="handleDeleteQuestion">删除题目</c-button>
+    </back-title>
+    <div v-if="question === null" class="no-content-warning">
+      加载中...
+    </div>
+    <question-detail-factory :question="question"/>
   </div>
 </template>
 
@@ -9,22 +15,34 @@
 import QuestionKind from '@/server/enums/question-kind'
 import BackTitle from '@/components/Basic/BackTitle'
 import QuestionDetailFactory from '@/views/quiz/components/QuestionDetailFactory'
+import { deleteQuestion, getOneQuestion } from '@/server/question'
+import CButton from '@/components/Basic/CButton'
 
 export default {
   name: 'QuestionDetail',
-  components: { QuestionDetailFactory, BackTitle },
-  data() {
+  components: { CButton, QuestionDetailFactory, BackTitle },
+  data () {
     return {
-      question: {},
+      question: null,
       QuestionKind
     }
   },
-  mounted() {
-    this.$setErrorMessage('这部分API需要等题库系统加入权限管理后开放')
-    // const { questionId } = this.$route.params
-    // getQuestionById(questionId)
-    //   .then(res => (this.question = res))
-    //   .catch(err => this.$setErrorMessage(err.response.data.msg))
+  mounted () {
+    const { questionId } = this.$route.params
+    getOneQuestion({ questionId })
+      .then(res => (this.question = res))
+      .catch(err => this.$setErrorMessage(err.response.data.msg))
+  },
+  methods: {
+    handleDeleteQuestion () {
+      const { questionId } = this.$route.params
+      deleteQuestion({ questionId })
+        .then(res => {
+          this.$setSuccessMessage('删除成功')
+          this.$router.go(-1)
+        })
+        .catch(err => this.$setErrorMessage(err.response.data.msg))
+    }
   }
 }
 </script>

+ 10 - 2
src/views/quiz/Questions.vue

@@ -7,9 +7,12 @@
           创建题目
         </c-button>
       </router-link>
+      <c-button text icon="filter_list" :theme="showOwn ? 'blue' : 'orange'" @click="showOwn = !showOwn">
+        {{ showOwn ? '显示全部' : '只显示我创建的' }}
+      </c-button>
     </back-title>
     <div>
-      <question-table />
+      <question-table :show-own="showOwn"/>
     </div>
   </div>
 </template>
@@ -20,7 +23,12 @@ import QuestionTable from '@/views/quiz/components/QuestionTable'
 import CButton from '@/components/Basic/CButton'
 export default {
   name: 'Questions',
-  components: { CButton, QuestionTable, BackTitle }
+  components: { CButton, QuestionTable, BackTitle },
+  data () {
+    return {
+      showOwn: false
+    }
+  }
 }
 </script>
 

+ 5 - 5
src/views/quiz/components/ChoiceQuestionEditor.vue

@@ -70,7 +70,7 @@ export default {
     },
     submitText: {
       type: String,
-      default: '创建题目'
+      default: '确认提交'
     }
   },
   data () {
@@ -98,13 +98,13 @@ export default {
     handleSubmit () {
       const { stem, options, answer, analysis } = this.value
       if (!stem) {
-        this.$setErrorMessage('请输入题干')
+        this.$setInfoMessage('请输入题干')
       } else if (!options?.A) {
-        this.$setErrorMessage('请输入选项')
+        this.$setInfoMessage('请输入选项')
       } else if (!answer) {
-        this.$setErrorMessage('请选择答案')
+        this.$setInfoMessage('请选择答案')
       } else if (!analysis) {
-        this.$setErrorMessage('请输入解析')
+        this.$setInfoMessage('请输入解析')
       } else {
         this.$emit('submit', this.value)
       }

+ 22 - 6
src/views/quiz/components/QuestionTable.vue

@@ -20,7 +20,7 @@
 
 <script>
 import debounce from '@/utils/debounce'
-import { getQuestionList } from '@/server/question'
+import { getCreatedQuestions, getQuestionList } from '@/server/question'
 import Pagination from '@/components/Pagination'
 import { markdownToHTML } from '@/utils/markdown'
 import QuestionCard from '@/views/quiz/components/QuestionCard'
@@ -37,6 +37,10 @@ export default {
     selectedQuestions: {
       type: Array,
       default: () => []
+    },
+    showOwn: {
+      type: Boolean,
+      default: false
     }
   },
   model: {
@@ -46,10 +50,16 @@ export default {
   filters: {
     markdownToHTML: markdownToHTML
   },
+  watch: {
+    showOwn () {
+      this.fetchQuestionList()
+    }
+  },
   data () {
     return {
       inputStem: '',
       questionList: [],
+      ownQuestionList: [],
       page: {},
 
       // 模糊搜索时使用
@@ -65,11 +75,17 @@ export default {
       page = 1,
       size = 10
     } = {}) {
-      getQuestionList({ key: stem, size, page })
-        .then(({ data: questions, page }) => {
-          this.questionList = questions
-          this.page = page
-        })
+      this.showOwn
+        ? getCreatedQuestions({ key: stem, size, page })
+          .then(({ data: questions, page }) => {
+            this.questionList = questions
+            this.page = page
+          })
+        : getQuestionList({ key: stem, size, page })
+          .then(({ data: questions, page }) => {
+            this.questionList = questions
+            this.page = page
+          })
     },
     judgeIsSelected (question) {
       return Boolean(this.selectedQuestions

+ 1 - 1
src/views/quiz/components/TrueFalseQuestionEditor.vue

@@ -43,7 +43,7 @@ export default {
     },
     submitText: {
       type: String,
-      default: '创建题目'
+      default: '确认提交'
     }
   },
   data () {

+ 5 - 0
src/views/teacher/tabs/TeacherExplore.vue

@@ -131,4 +131,9 @@ export default {
   display: flex;
   align-items: center;
 }
+
+.slide-card {
+  padding-top: 8px;
+  padding-bottom: 8px;
+}
 </style>