Browse Source

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

dusenyao 1 year ago
parent
commit
8ec0fb1b4d

+ 9 - 0
src/views/exercise_questions/create/components/common/QuestionBase.vue

@@ -106,6 +106,15 @@ export default {
             }
           }
         }
+
+        .title-little {
+          display: block;
+          margin: 8px 0;
+          font-size: 14px;
+          font-weight: 400;
+          line-height: 22px;
+          color: #4e5969;
+        }
       }
 
       .footer {

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

@@ -47,6 +47,8 @@ import JudgeQuestion from './exercises/JudgeQuestion.vue';
 import MatchingQuestion from './exercises/MatchingQuestion.vue';
 import FillQuestion from './exercises/FillQuestion.vue';
 import ReadAloudQuestion from './exercises/ReadAloudQuestion.vue';
+import ChineseQuestion from './exercises/ChineseQuestion.vue';
+import WriteQuestion from './exercises/WriteQuestion.vue';
 
 export default {
   name: 'CreateMain',
@@ -57,6 +59,8 @@ export default {
     MatchingQuestion,
     FillQuestion,
     ReadAloudQuestion,
+    ChineseQuestion,
+    WriteQuestion,
   },
   provide() {
     return {
@@ -85,6 +89,8 @@ export default {
         matching: MatchingQuestion,
         fill: FillQuestion,
         read_aloud: ReadAloudQuestion,
+        chinese: ChineseQuestion,
+        write: WriteQuestion,
       },
     };
   },

+ 136 - 0
src/views/exercise_questions/create/components/exercises/ChineseQuestion.vue

@@ -0,0 +1,136 @@
+<!-- 选择题 -->
+<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>
+        <el-input v-model="data.content" rows="3" resize="none" type="textarea" 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-item label="类型">
+          <el-radio
+            v-for="{ value, label } in learnTypeList"
+            :key="value"
+            v-model="data.property.learn_type"
+            :label="value"
+          >
+            {{ label }}
+          </el-radio>
+        </el-form-item>
+      </el-form>
+    </template>
+  </QuestionBase>
+</template>
+
+<script>
+import UploadAudio from '../common/UploadAudio.vue';
+import QuestionMixin from '../common/QuestionMixin.js';
+
+import { scoreTypeList, changeOptionType } from '@/views/exercise_questions/data/common';
+import { chineseData, getOption, learnTypeList } from '@/views/exercise_questions/data/chinese';
+
+export default {
+  name: 'ChineseQuestion',
+  components: {
+    UploadAudio,
+  },
+  mixins: [QuestionMixin],
+  data() {
+    return {
+      learnTypeList,
+      changeOptionType,
+      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));
+      }
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 207 - 0
src/views/exercise_questions/create/components/exercises/WriteQuestion.vue

@@ -0,0 +1,207 @@
+<!-- 选择题 -->
+<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>
+        <RichText v-model="data.article" placeholder="输入文章" />
+        <template v-if="data.property.is_enable_sample_text">
+          <el-divider class="write-divider"></el-divider>
+          <label class="title-little">范文:</label>
+          <RichText v-model="data.sample_text" placeholder="输入范文" />
+          <p class="tips">多篇范文之间使用分割线(---)</p>
+        </template>
+      </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-item label="词数">
+          <el-input-number class="word-num-input" v-model="data.property.word_num" :precision="0"></el-input-number>
+        </el-form-item>
+        <el-form-item label="范文">
+          <el-radio
+            v-for="{ value, label } in switchOption"
+            :key="value"
+            v-model="data.property.is_enable_sample_text"
+            :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_voice_answer"
+            :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_upload"
+            :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_model_essay"
+            :label="value"
+          >
+            {{ label }}
+          </el-radio>
+        </el-form-item>
+      </el-form>
+    </template>
+  </QuestionBase>
+</template>
+
+<script>
+import UploadAudio from '../common/UploadAudio.vue';
+import QuestionMixin from '../common/QuestionMixin.js';
+
+import { scoreTypeList, changeOptionType } from '@/views/exercise_questions/data/common';
+import { wirteData, getOption } from '@/views/exercise_questions/data/write';
+
+export default {
+  name: 'WriteQuestion',
+  components: {
+    UploadAudio,
+  },
+  mixins: [QuestionMixin],
+  data() {
+    return {
+      changeOptionType,
+      data: JSON.parse(JSON.stringify(wirteData)),
+    };
+  },
+  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));
+      }
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.content {
+  .write-divider {
+    margin: 8px 0;
+  }
+
+  .tips {
+    margin: 8px 0;
+    font-size: 12px;
+    line-height: 20px;
+    color: #8e8e8e;
+  }
+}
+
+.property {
+  .word-num-input {
+    width: 100%;
+  }
+}
+</style>
+<style lang="scss">
+.property {
+  .word-num-input {
+    .el-input-number__increase,
+    .el-input-number__decrease {
+      background: #f2f3f5;
+    }
+
+    .el-input input.el-input__inner {
+      background: #e5e6eb;
+    }
+  }
+}
+</style>

+ 11 - 1
src/views/exercise_questions/create/index.vue

@@ -62,6 +62,8 @@ import CreateMain from './components/create.vue';
 import SelectPreview from '@/views/exercise_questions/preview/SelectPreview.vue';
 import JudgePreview from '@/views/exercise_questions/preview/JudgePreview.vue';
 import MatchingPreview from '@/views/exercise_questions/preview/MatchingPreview.vue';
+import ChinesePreview from '@/views/exercise_questions/preview/ChinesePreview.vue';
+import WritePreview from '../preview/WritePreview.vue';
 
 export default {
   name: 'CreateExercise',
@@ -70,6 +72,8 @@ export default {
     SelectPreview,
     JudgePreview,
     MatchingPreview,
+    ChinesePreview,
+    WritePreview,
   },
   provide() {
     return {
@@ -88,7 +92,13 @@ export default {
       isSetUp: false, // 设置
       preview: false, // 预览显示
       previewData: {}, // 预览数据
-      previewComponents: { select: SelectPreview, judge: JudgePreview, matching: MatchingPreview },
+      previewComponents: {
+        select: SelectPreview,
+        judge: JudgePreview,
+        matching: MatchingPreview,
+        chinese: ChinesePreview,
+        write: WritePreview,
+      },
     };
   },
   computed: {

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

@@ -0,0 +1,35 @@
+import { optionTypeList, stemTypeList, scoreTypeList, questionNumberTypeList } from './common';
+import { getRandomNumber } from '@/utils/index';
+
+// 汉字类型列表
+export const learnTypeList = [
+    { value: 'miao', label: '描红' },
+    { value: 'write', label: '书写' },
+    { value: 'learn', label: '笔画学习' },
+];
+export function getOption(content = '') {
+    return { content, mark: getRandomNumber() };
+}
+
+// 选择题数据模板
+export const chineseData = {
+    type: 'chinese', // 题型
+    stem: '', // 题干
+    option_number_show_mode: optionTypeList[0].value, // 选项类型
+    description: '', // 描述
+    content: '', // 题目
+    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, // 分值类型
+        learn_type: learnTypeList[0].value,
+    },
+    // 其他属性
+    other: {
+        question_number_type: questionNumberTypeList[0].value, // 题号类型
+    },
+};

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

@@ -0,0 +1,34 @@
+import { optionTypeList, stemTypeList, scoreTypeList, questionNumberTypeList } from './common';
+import { getRandomNumber } from '@/utils/index';
+
+export function getOption(content = '') {
+    return { content, mark: getRandomNumber() };
+}
+
+// 选择题数据模板
+export const wirteData = {
+    type: 'write', // 题型
+    stem: '', // 题干
+    option_number_show_mode: optionTypeList[0].value, // 选项类型
+    description: '', // 描述
+    article: '', // 文章
+    sample_text: '', // 范文
+    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, // 分值类型
+        word_num: 50, // 词数
+        is_enable_sample_text: true, // 范文开启
+        is_enable_voice_answer: true, // 语音作答
+        is_enable_upload: true, // 上传附件
+        is_enable_model_essay: true, // 参考范文
+    },
+    // 其他属性
+    other: {
+        question_number_type: questionNumberTypeList[0].value, // 题号类型
+    },
+};

+ 87 - 0
src/views/exercise_questions/preview/ChinesePreview.vue

@@ -0,0 +1,87 @@
+<!-- eslint-disable vue/no-v-html -->
+<template>
+  <div class="select-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 { 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);
+        }
+      }
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+@use '@/styles/mixin.scss' as *;
+
+.select-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>

+ 87 - 0
src/views/exercise_questions/preview/WritePreview.vue

@@ -0,0 +1,87 @@
+<!-- eslint-disable vue/no-v-html -->
+<template>
+  <div class="select-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 { 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);
+        }
+      }
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+@use '@/styles/mixin.scss' as *;
+
+.select-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>