Browse Source

Merge branch 'master' of http://60.205.254.193:3000/GCLS/GCLS_Page_Exercise

dusenyao 1 year ago
parent
commit
bce9536ce8

+ 6 - 3
src/views/exercise_questions/create/components/common/QuestionBase.vue

@@ -8,6 +8,9 @@
         <slot name="property"></slot>
       </div>
     </div>
+    <slot name="question-footer">
+      <slot name="footer"></slot>
+    </slot>
   </div>
 </template>
 
@@ -30,14 +33,14 @@ export default {
 <style lang="scss" scoped>
 .question {
   width: 100%;
-  background-color: #fff;
-  border: $border;
-  border-radius: 0 0 8px 8px;
 
   &-container {
     display: flex;
     width: 100%;
     padding: 24px;
+    background-color: #fff;
+    border: $border;
+    border-radius: 0 0 8px 8px;
 
     .question-content {
       flex: 1;

+ 6 - 2
src/views/exercise_questions/create/components/common/UploadAudio.vue

@@ -31,6 +31,10 @@ export default {
       type: String,
       default: '',
     },
+    itemIndex: {
+      type: Number,
+      default: null,
+    },
   },
   data() {
     return {
@@ -72,7 +76,7 @@ export default {
           this.file_id = file_id;
           this.file_url = file_url;
           this.file_name = file_name;
-          this.$emit('upload', file_id);
+          this.$emit('upload', file_id, this.itemIndex);
         }
       });
     },
@@ -83,7 +87,7 @@ export default {
         type: 'warning',
       })
         .then(() => {
-          this.$emit('deleteFile', this.file_id);
+          this.$emit('deleteFile', this.file_id, this.itemIndex);
           this.file_id = '';
           this.file_url = '';
           this.file_name = '';

+ 6 - 0
src/views/exercise_questions/create/components/create.vue

@@ -52,6 +52,8 @@ import WriteQuestion from './exercises/WriteQuestion.vue';
 import DialogueQuestion from './exercises/DialogueQuestion.vue';
 import TalkPicture from './exercises/TalkPicture.vue';
 import ChooseToneQuestion from './exercises/ChooseToneQuestion.vue';
+import RepeatQuestion from './exercises/RepeatQuestion.vue';
+import ReadQuestion from './exercises/ReadQuestion.vue';
 
 export default {
   name: 'CreateMain',
@@ -67,6 +69,8 @@ export default {
     DialogueQuestion,
     TalkPicture,
     ChooseToneQuestion,
+    RepeatQuestion,
+    ReadQuestion,
   },
   provide() {
     return {
@@ -100,6 +104,8 @@ export default {
         dialogue: DialogueQuestion,
         talk_picture: TalkPicture,
         choose_tone: ChooseToneQuestion,
+        repeat: RepeatQuestion,
+        read: ReadQuestion,
       },
     };
   },

+ 2 - 18
src/views/exercise_questions/create/components/exercises/ChineseQuestion.vue

@@ -96,7 +96,7 @@
 import QuestionMixin from '../common/QuestionMixin.js';
 
 import { changeOptionType } from '@/views/exercise_questions/data/common';
-import { chineseData, getOption, learnTypeList } from '@/views/exercise_questions/data/chinese';
+import { chineseData, learnTypeList } from '@/views/exercise_questions/data/chinese';
 
 export default {
   name: 'ChineseQuestion',
@@ -108,23 +108,7 @@ export default {
       data: JSON.parse(JSON.stringify(chineseData)),
     };
   },
-  methods: {
-    /**
-     * 智能识别
-     * @param {String} text 识别数据
-     */
-    recognition(text) {
-      let arr = text
-        .split(/[\r\n]/)
-        .map((item) => item.trim())
-        .filter((item) => item);
-
-      if (arr.length > 0) {
-        this.data.stem = arr[0];
-        this.data.option_list = arr.slice(1).map((content) => getOption(content));
-      }
-    },
-  },
+  methods: {},
 };
 </script>
 

+ 122 - 0
src/views/exercise_questions/create/components/exercises/ReadQuestion.vue

@@ -0,0 +1,122 @@
+<template>
+  <QuestionBase>
+    <template #content>
+      <div class="stem">
+        <el-input
+          v-if="data.property.stem_type === stemTypeList[0].value"
+          v-model="data.stem"
+          rows="3"
+          resize="none"
+          type="textarea"
+          placeholder="输入题干"
+        />
+
+        <RichText v-if="data.property.stem_type === stemTypeList[1].value" v-model="data.stem" placeholder="输入题干" />
+
+        <el-input
+          v-show="data.property.is_enable_description"
+          v-model="data.description"
+          rows="3"
+          resize="none"
+          type="textarea"
+          placeholder="输入描述"
+        />
+      </div>
+
+      <div class="content">
+        <RichText v-model="data.article" placeholder="输入文章" />
+      </div>
+    </template>
+    <template #property>
+      <el-form :model="data.property">
+        <el-form-item label="题干">
+          <el-radio
+            v-for="{ value, label } in stemTypeList"
+            :key="value"
+            v-model="data.property.stem_type"
+            :label="value"
+          >
+            {{ label }}
+          </el-radio>
+        </el-form-item>
+        <el-form-item label="题号">
+          <el-input v-model="data.property.question_number" />
+        </el-form-item>
+        <el-form-item label-width="45px">
+          <el-radio
+            v-for="{ value, label } in questionNumberTypeList"
+            :key="value"
+            v-model="data.other.question_number_type"
+            :label="value"
+          >
+            {{ label }}
+          </el-radio>
+        </el-form-item>
+        <el-form-item label="描述">
+          <el-radio
+            v-for="{ value, label } in switchOption"
+            :key="value"
+            v-model="data.property.is_enable_description"
+            :label="value"
+          >
+            {{ label }}
+          </el-radio>
+        </el-form-item>
+        <el-form-item label="分值">
+          <el-radio
+            v-for="{ value, label } in scoreTypeList"
+            :key="value"
+            v-model="data.property.score_type"
+            :label="value"
+          >
+            {{ label }}
+          </el-radio>
+        </el-form-item>
+        <el-form-item label-width="45px">
+          <el-input v-model="data.property.score" type="number" />
+        </el-form-item>
+      </el-form>
+    </template>
+    <template #footer>
+      <div class="footer">
+        <span class="add-option" @click="addOption">
+          <SvgIcon icon-class="add-circle" size="14" /> <span>增加配题</span>
+        </span>
+      </div>
+    </template>
+  </QuestionBase>
+</template>
+
+<script>
+import QuestionMixin from '../common/QuestionMixin.js';
+
+import { changeOptionType } from '@/views/exercise_questions/data/common';
+import { readData } from '@/views/exercise_questions/data/read';
+
+export default {
+  name: 'ReadQuestion',
+  mixins: [QuestionMixin],
+  data() {
+    return {
+      changeOptionType,
+      data: JSON.parse(JSON.stringify(readData)),
+    };
+  },
+  methods: {
+    addOption() {
+      this.data.question_list.push('');
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.footer {
+  padding: 5px 16px;
+  margin: 16px 0;
+  text-align: center;
+  cursor: pointer;
+  background-color: #fff;
+  border-radius: 2px;
+}
+</style>

+ 177 - 0
src/views/exercise_questions/create/components/exercises/RepeatQuestion.vue

@@ -0,0 +1,177 @@
+<!-- 听读训练 -->
+<template>
+  <QuestionBase>
+    <template #content>
+      <div class="stem">
+        <el-input
+          v-if="data.property.stem_type === stemTypeList[0].value"
+          v-model="data.stem"
+          rows="3"
+          resize="none"
+          type="textarea"
+          placeholder="输入题干"
+        />
+
+        <RichText v-if="data.property.stem_type === stemTypeList[1].value" v-model="data.stem" placeholder="输入题干" />
+
+        <el-input
+          v-show="data.property.is_enable_description"
+          v-model="data.description"
+          rows="3"
+          resize="none"
+          type="textarea"
+          placeholder="输入描述"
+        />
+      </div>
+
+      <div class="content">
+        <label class="title-little">内容</label>
+        <ul>
+          <li
+            v-for="(item, i) in data.option_list"
+            :key="i + Math.random().toString(36).slice(-6)"
+            class="content-item repeat-option"
+          >
+            <span class="question-number" @dblclick="changeOptionType(data)">
+              {{ computedQuestionNumber(i, data.option_number_show_mode) }}.
+            </span>
+            <div class="option-content">
+              <RichText v-model="item.content" placeholder="输入内容" :inline="true" />
+            </div>
+            <UploadAudio
+              :file-id="item.file_id_list?.[0]"
+              :item-index="i"
+              @upload="uploads"
+              @deleteFile="deleteFiles"
+            />
+            <SvgIcon icon-class="delete" class="delete pointer" @click="deleteOption(i)" />
+          </li>
+        </ul>
+      </div>
+
+      <div class="footer">
+        <span class="add-option" @click="addOption">
+          <SvgIcon icon-class="add-circle" size="14" /> <span>增加选项</span>
+        </span>
+      </div>
+    </template>
+
+    <template #property>
+      <el-form :model="data.property">
+        <el-form-item label="题干">
+          <el-radio
+            v-for="{ value, label } in stemTypeList"
+            :key="value"
+            v-model="data.property.stem_type"
+            :label="value"
+          >
+            {{ label }}
+          </el-radio>
+        </el-form-item>
+        <el-form-item label="题号">
+          <el-input v-model="data.property.question_number" />
+        </el-form-item>
+        <el-form-item label-width="45px">
+          <el-radio
+            v-for="{ value, label } in questionNumberTypeList"
+            :key="value"
+            v-model="data.other.question_number_type"
+            :label="value"
+          >
+            {{ label }}
+          </el-radio>
+        </el-form-item>
+        <el-form-item label="描述">
+          <el-radio
+            v-for="{ value, label } in switchOption"
+            :key="value"
+            v-model="data.property.is_enable_description"
+            :label="value"
+          >
+            {{ label }}
+          </el-radio>
+        </el-form-item>
+        <el-form-item label="分值">
+          <el-radio
+            v-for="{ value, label } in scoreTypeList"
+            :key="value"
+            v-model="data.property.score_type"
+            :label="value"
+          >
+            {{ label }}
+          </el-radio>
+        </el-form-item>
+        <el-form-item label-width="45px">
+          <el-input v-model="data.property.score" type="number" />
+        </el-form-item>
+      </el-form>
+    </template>
+  </QuestionBase>
+</template>
+
+<script>
+import UploadAudio from '../common/UploadAudio.vue';
+import QuestionMixin from '../common/QuestionMixin.js';
+
+import { selectTypeList, changeOptionType } from '@/views/exercise_questions/data/common';
+import { repeatData, getOption } from '@/views/exercise_questions/data/repeat';
+
+export default {
+  name: 'RepeatQuestion',
+  components: {
+    UploadAudio,
+  },
+  mixins: [QuestionMixin],
+  data() {
+    return {
+      selectTypeList,
+      changeOptionType,
+      data: JSON.parse(JSON.stringify(repeatData)),
+    };
+  },
+  methods: {
+    /**
+     * 智能识别
+     * @param {String} text 识别数据
+     */
+    recognition(text) {
+      let arr = text
+        .split(/[\r\n]/)
+        .map((item) => item.trim())
+        .filter((item) => item);
+
+      if (arr.length > 0) {
+        this.data.stem = arr[0];
+        this.data.option_list = arr.slice(1).map((content) => getOption(content));
+      }
+    },
+    addOption() {
+      this.data.option_list.push(getOption());
+    },
+    uploads(file_id, index) {
+      this.data.option_list[index].file_id_list.push(file_id);
+    },
+    deleteFiles(file_id, itemIndex) {
+      let index = this.data.option_list[itemIndex].file_id_list.indexOf(file_id);
+      if (index !== -1) {
+        this.data.option_list[itemIndex].file_id_list.splice(index, 1);
+      }
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.repeat-option {
+  :deep .upload-wrapper {
+    margin-top: 0;
+  }
+
+  :deep .file-name {
+    width: 140px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+}
+</style>

+ 6 - 0
src/views/exercise_questions/create/index.vue

@@ -69,6 +69,8 @@ import ReadAloudPreview from '../preview/ReadAloudPreview.vue';
 import DialoguePreview from '../preview/DialoguePreview.vue';
 import TalkPictruePreview from '../preview/TalkPictruePreview.vue';
 import ChooseTonePreview from '../preview/ChooseTonePreview.vue';
+import RepeatPreview from '../preview/RepeatPreview.vue';
+import ReadPreview from '../preview/ReadPreview.vue';
 
 export default {
   name: 'CreateExercise',
@@ -84,6 +86,8 @@ export default {
     DialoguePreview,
     TalkPictruePreview,
     ChooseTonePreview,
+    RepeatPreview,
+    ReadPreview,
   },
   provide() {
     return {
@@ -113,6 +117,8 @@ export default {
         dialogue: DialoguePreview,
         talk_picture: TalkPictruePreview,
         choose_tone: ChooseTonePreview,
+        repeat: RepeatPreview,
+        read: ReadPreview,
       },
     };
   },

+ 0 - 4
src/views/exercise_questions/data/chinese.js

@@ -1,5 +1,4 @@
 import { optionTypeList, stemTypeList, scoreTypeList, questionNumberTypeList } from './common';
-import { getRandomNumber } from '@/utils/index';
 
 // 汉字类型列表
 export const learnTypeList = [
@@ -7,9 +6,6 @@ export const learnTypeList = [
   { value: 'write', label: '书写' },
   { value: 'learn', label: '笔画学习' },
 ];
-export function getOption(content = '') {
-  return { content, mark: getRandomNumber() };
-}
 
 // 选择题数据模板
 export const chineseData = {

+ 24 - 0
src/views/exercise_questions/data/read.js

@@ -0,0 +1,24 @@
+import { optionTypeList, stemTypeList, scoreTypeList, questionNumberTypeList } from './common';
+// 数据模板
+export const readData = {
+  type: 'read', // 题型
+  stem: '', // 题干
+  option_number_show_mode: optionTypeList[0].value, // 选项类型
+  description: '', // 描述
+  article: '', // 文章
+  sample_text: '', // 范文
+  answer: { select_list: [], score: 0, score_type: scoreTypeList[0].value }, // 答案
+  question_list: [], // 题目列表
+  // 题型属性
+  property: {
+    stem_type: stemTypeList[0].value, // 题干类型
+    question_number: 1, // 题号
+    is_enable_description: false, // 描述
+    score: 1, // 分值
+    score_type: scoreTypeList[0].value, // 分值类型
+  },
+  // 其他属性
+  other: {
+    question_number_type: questionNumberTypeList[0].value, // 题号类型
+  },
+};

+ 33 - 0
src/views/exercise_questions/data/repeat.js

@@ -0,0 +1,33 @@
+import { optionTypeList, stemTypeList, scoreTypeList, questionNumberTypeList } from './common';
+import { getRandomNumber } from '@/utils/index';
+
+export function getOption(content = '') {
+  return { content, mark: getRandomNumber(), file_id_list: [] };
+}
+
+// 听后训练数据模板
+export const repeatData = {
+  type: 'repeat', // 题型
+  stem: '', // 题干
+  option_number_show_mode: optionTypeList[0].value, // 选项类型
+  description: '', // 描述
+  option_list: [
+    { content: '', mark: getRandomNumber(), file_id_list: [] },
+    { content: '', mark: getRandomNumber(), file_id_list: [] },
+    { content: '', mark: getRandomNumber(), file_id_list: [] },
+  ], // 选项
+
+  answer: { select_list: [], score: 0, score_type: scoreTypeList[0].value }, // 答案
+  // 题型属性
+  property: {
+    stem_type: stemTypeList[0].value, // 题干类型
+    question_number: 1, // 题号
+    is_enable_description: false, // 描述
+    score: 1, // 分值
+    score_type: scoreTypeList[0].value, // 分值类型
+  },
+  // 其他属性
+  other: {
+    question_number_type: questionNumberTypeList[0].value, // 题号类型
+  },
+};

+ 0 - 6
src/views/exercise_questions/data/write.js

@@ -1,10 +1,4 @@
 import { optionTypeList, stemTypeList, scoreTypeList, questionNumberTypeList } from './common';
-import { getRandomNumber } from '@/utils/index';
-
-export function getOption(content = '') {
-  return { content, mark: getRandomNumber() };
-}
-
 // 数据模板
 export const writeData = {
   type: 'write', // 题型

+ 4 - 60
src/views/exercise_questions/preview/ChinesePreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="select-preview">
+  <div class="chinese-preview">
     <div class="stem">
       <span class="question-number">{{ data.property.question_number }}.</span>
       <span v-html="sanitizeHTML(data.stem)"></span>
@@ -10,78 +10,22 @@
 </template>
 
 <script>
-import { computeOptionMethods, selectTypeList } from '@/views/exercise_questions/data/common';
-
 import PreviewMixin from './components/PreviewMixin';
 
 export default {
   name: 'ChinesePreview',
   mixins: [PreviewMixin],
   data() {
-    return {
-      computeOptionMethods,
-    };
-  },
-  methods: {
-    isAnswer(mark) {
-      return this.answer.select_list.indexOf(mark) !== -1;
-    },
-    selectAnswer(mark) {
-      const index = this.answer.select_list.indexOf(mark);
-      if (this.data.property.select_type === selectTypeList[0].value) {
-        this.answer.select_list = [mark];
-      }
-      if (this.data.property.select_type === selectTypeList[1].value) {
-        if (index === -1) {
-          this.answer.select_list.push(mark);
-        } else {
-          this.answer.select_list.splice(index, 1);
-        }
-      }
-    },
+    return {};
   },
+  methods: {},
 };
 </script>
 
 <style lang="scss" scoped>
 @use '@/styles/mixin.scss' as *;
 
-.select-preview {
+.chinese-preview {
   @include preview;
-
-  .option-list {
-    display: flex;
-    flex-direction: column;
-    row-gap: 16px;
-
-    .option-item {
-      display: flex;
-      column-gap: 8px;
-      align-items: center;
-      padding: 12px 24px;
-      color: #706f78;
-      cursor: pointer;
-      background-color: #f9f8f9;
-      border-radius: 40px;
-
-      .selectionbox {
-        width: 14px;
-        height: 14px;
-        margin-right: 8px;
-        border: 2px solid #dcdbdd;
-        border-radius: 50%;
-      }
-
-      &.active {
-        color: #34343a;
-        background-color: #e7eeff;
-
-        .selectionbox {
-          border-color: #306eff;
-          border-width: 4px;
-        }
-      }
-    }
-  }
 }
 </style>

+ 32 - 0
src/views/exercise_questions/preview/ReadPreview.vue

@@ -0,0 +1,32 @@
+<!-- eslint-disable vue/no-v-html -->
+<template>
+  <div class="read-preview">
+    <div class="stem">
+      <span class="question-number">{{ data.property.question_number }}.</span>
+      <span v-html="sanitizeHTML(data.stem)"></span>
+    </div>
+    <div v-if="data.property.is_enable_description" class="description">{{ data.description }}</div>
+  </div>
+</template>
+
+<script>
+import PreviewMixin from './components/PreviewMixin';
+
+export default {
+  name: 'ReadPreview',
+  mixins: [PreviewMixin],
+  data() {
+    return {};
+  },
+  created() {},
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped>
+@use '@/styles/mixin.scss' as *;
+
+.read-preview {
+  @include preview;
+}
+</style>

+ 31 - 0
src/views/exercise_questions/preview/RepeatPreview.vue

@@ -0,0 +1,31 @@
+<!-- eslint-disable vue/no-v-html -->
+<template>
+  <div class="repeat-preview">
+    <div class="stem">
+      <span class="question-number">{{ data.property.question_number }}.</span>
+      <span v-html="sanitizeHTML(data.stem)"></span>
+    </div>
+    <div v-if="data.property.is_enable_description" class="description">{{ data.description }}</div>
+  </div>
+</template>
+
+<script>
+import PreviewMixin from './components/PreviewMixin';
+
+export default {
+  name: 'RepeatPreview',
+  mixins: [PreviewMixin],
+  data() {
+    return {};
+  },
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped>
+@use '@/styles/mixin.scss' as *;
+
+.repeat-preview {
+  @include preview;
+}
+</style>

+ 5 - 60
src/views/exercise_questions/preview/WritePreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="select-preview">
+  <div class="write-preview">
     <div class="stem">
       <span class="question-number">{{ data.property.question_number }}.</span>
       <span v-html="sanitizeHTML(data.stem)"></span>
@@ -10,78 +10,23 @@
 </template>
 
 <script>
-import { computeOptionMethods, selectTypeList } from '@/views/exercise_questions/data/common';
-
 import PreviewMixin from './components/PreviewMixin';
 
 export default {
   name: 'WritePreview',
   mixins: [PreviewMixin],
   data() {
-    return {
-      computeOptionMethods,
-    };
-  },
-  methods: {
-    isAnswer(mark) {
-      return this.answer.select_list.indexOf(mark) !== -1;
-    },
-    selectAnswer(mark) {
-      const index = this.answer.select_list.indexOf(mark);
-      if (this.data.property.select_type === selectTypeList[0].value) {
-        this.answer.select_list = [mark];
-      }
-      if (this.data.property.select_type === selectTypeList[1].value) {
-        if (index === -1) {
-          this.answer.select_list.push(mark);
-        } else {
-          this.answer.select_list.splice(index, 1);
-        }
-      }
-    },
+    return {};
   },
+  created() {},
+  methods: {},
 };
 </script>
 
 <style lang="scss" scoped>
 @use '@/styles/mixin.scss' as *;
 
-.select-preview {
+.write-preview {
   @include preview;
-
-  .option-list {
-    display: flex;
-    flex-direction: column;
-    row-gap: 16px;
-
-    .option-item {
-      display: flex;
-      column-gap: 8px;
-      align-items: center;
-      padding: 12px 24px;
-      color: #706f78;
-      cursor: pointer;
-      background-color: #f9f8f9;
-      border-radius: 40px;
-
-      .selectionbox {
-        width: 14px;
-        height: 14px;
-        margin-right: 8px;
-        border: 2px solid #dcdbdd;
-        border-radius: 50%;
-      }
-
-      &.active {
-        color: #34343a;
-        background-color: #e7eeff;
-
-        .selectionbox {
-          border-color: #306eff;
-          border-width: 4px;
-        }
-      }
-    }
-  }
 }
 </style>