dusenyao 1 рік тому
батько
коміт
cfb247fca1
35 змінених файлів з 572 додано та 243 видалено
  1. 2 2
      src/views/exercise_questions/create/components/exercises/ChooseToneQuestion.vue
  2. 10 72
      src/views/exercise_questions/create/components/exercises/DialogueQuestion.vue
  3. 3 6
      src/views/exercise_questions/create/components/exercises/FillQuestion.vue
  4. 3 1
      src/views/exercise_questions/create/components/exercises/JudgeQuestion.vue
  5. 3 6
      src/views/exercise_questions/create/components/exercises/ListenFillQuestion.vue
  6. 3 1
      src/views/exercise_questions/create/components/exercises/ListenJudgeQuestion.vue
  7. 10 2
      src/views/exercise_questions/create/components/exercises/ListenSelectQuestion.vue
  8. 8 26
      src/views/exercise_questions/create/components/exercises/MatchingQuestion.vue
  9. 3 8
      src/views/exercise_questions/create/components/exercises/ReadAloudQuestion.vue
  10. 8 8
      src/views/exercise_questions/create/components/exercises/ReadQuestion.vue
  11. 10 4
      src/views/exercise_questions/create/components/exercises/RepeatQuestion.vue
  12. 7 19
      src/views/exercise_questions/create/components/exercises/ReplaceAnswerQuestion.vue
  13. 4 2
      src/views/exercise_questions/create/components/exercises/SelectQuestion.vue
  14. 8 9
      src/views/exercise_questions/create/components/exercises/SortQuestion.vue
  15. 7 40
      src/views/exercise_questions/create/components/exercises/TableFillQuestion.vue
  16. 2 2
      src/views/exercise_questions/create/components/exercises/WordCardQuestion.vue
  17. 2 3
      src/views/exercise_questions/create/components/exercises/WordDictationQuestion.vue
  18. 3 10
      src/views/exercise_questions/create/components/exercises/WriteQuestion.vue
  19. 22 4
      src/views/exercise_questions/create/index.vue
  20. 85 0
      src/views/exercise_questions/data/dialogue.js
  21. 10 0
      src/views/exercise_questions/data/fill.js
  22. 11 0
      src/views/exercise_questions/data/judge.js
  23. 10 0
      src/views/exercise_questions/data/listenFill.js
  24. 11 0
      src/views/exercise_questions/data/listenJudge.js
  25. 11 0
      src/views/exercise_questions/data/listenSelect.js
  26. 33 0
      src/views/exercise_questions/data/matching.js
  27. 125 16
      src/views/exercise_questions/data/questionType.js
  28. 12 0
      src/views/exercise_questions/data/read.js
  29. 12 0
      src/views/exercise_questions/data/readAloud.js
  30. 10 0
      src/views/exercise_questions/data/repeat.js
  31. 29 0
      src/views/exercise_questions/data/replaceAnswer.js
  32. 2 2
      src/views/exercise_questions/data/select.js
  33. 17 0
      src/views/exercise_questions/data/sort.js
  34. 58 0
      src/views/exercise_questions/data/tableFill.js
  35. 18 0
      src/views/exercise_questions/data/write.js

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

@@ -40,19 +40,19 @@
             <template v-else>
               <div :class="['upload-audio-play']">
                 <UploadAudio
+                  v-if="data.other.audio_generation_method === 'upload'"
                   :key="item.audio_file_id || i"
                   :file-id="item.audio_file_id"
                   :item-index="i"
                   :show-upload="!item.audio_file_id"
                   @upload="uploads"
                   @deleteFile="deleteFiles"
-                  v-if="data.other.audio_generation_method === 'upload'"
                 />
                 <div
                   v-else-if="data.other.audio_generation_method === 'auto'"
+                  v-loading="loading_list[i] ? loading_list[i].loading : false"
                   class="auto-matically"
                   @click="handleMatically(item, i)"
-                  v-loading="loading_list[i] ? loading_list[i].loading : false"
                 >
                   <SvgIcon icon-class="voiceprint-line" class="record" />
                   <span class="auto-btn">{{ item.audio_file_id ? '已生成' : '生成音频' }}</span>

+ 10 - 72
src/views/exercise_questions/create/components/exercises/DialogueQuestion.vue

@@ -202,7 +202,12 @@ import SoundRecordPreview from '@/views/exercise_questions/preview/components/co
 
 import { getRandomNumber } from '@/utils';
 import { fileUpload, GetFileURLMap } from '@/api/app';
-import { getDialogueData, getRole } from '@/views/exercise_questions/data/dialogue';
+import {
+  analysisRecognitionDialogueData,
+  getDialogueData,
+  getRole,
+  getTextContenList,
+} from '@/views/exercise_questions/data/dialogue';
 
 export default {
   name: 'DialogueQuestion',
@@ -269,43 +274,8 @@ export default {
      */
     recognition(text) {
       let arr = this.recognitionCommon(text);
-      // 角色列表
-      let roleList = [];
-      arr.forEach((item) => {
-        if (!item.match(/.+[::].+/)) return;
-        let [role] = item.split(/[::]/);
-        if (!roleList.includes(role)) {
-          roleList.push(role);
-        }
-      });
-      if (roleList.length < 2 || roleList.length > 5) {
-        return this.$message.warning('角色数不符合要求,最小为 2,最大为 5');
-      }
-      this.data.property.role_number = roleList.length;
-      this.data.property.role_list = roleList.map((item, i) => getRole(i, item));
-
-      this.data.option_list = [];
-      arr.forEach((item) => {
-        if (item.match(/.+[::].+/)) {
-          let [role, content] = item.split(/[::]/);
-          let { hasFill, content_list } = this.getTextContenList(content);
-          this.data.option_list.push({
-            role: this.data.property.role_list.find((item) => item.name === role).mark,
-            text: content,
-            mark: getRandomNumber(),
-            file_id: '',
-            content_list,
-            type: hasFill ? 'input' : 'text',
-          });
-        }
-        if (item.match(/^-学生$/)) {
-          this.data.option_list.push({
-            mark: getRandomNumber(),
-            file_id: '',
-            type: 'input_student',
-          });
-        }
-      });
+      let obj = analysisRecognitionDialogueData(arr);
+      this.recognitionCommonSetObj(obj);
       this.identifyText();
     },
     // 识别
@@ -378,7 +348,7 @@ export default {
       }
       let text = this.textInput.replace(/\n/, '');
 
-      let { hasFill, content_list } = this.getTextContenList(text);
+      let { hasFill, content_list } = getTextContenList(text);
       this.data.option_list.push({
         role: this.curRole,
         text: this.textInput.replace(/\n/, ''),
@@ -389,39 +359,7 @@ export default {
       });
       this.textInput = '';
     },
-    /**
-     * 获取文本内容列表
-     * @param {string} text 文本
-     */
-    getTextContenList(text) {
-      let str = text;
-      let reg = /_{3,}/;
-      let hasFill = reg.test(str); // 是否有填空
-      let content_list = [];
-      if (hasFill) {
-        str = str.replace(/(_{3,})/g, '###$1###');
-        content_list = str
-          .split('###')
-          .filter((item) => item)
-          .map((content) => {
-            let isInput = reg.test(content);
-            return {
-              content: isInput ? '' : content,
-              mark: isInput ? getRandomNumber() : '',
-              type: isInput ? 'input' : 'text',
-            };
-          });
-      } else {
-        content_list = [
-          {
-            content: str,
-            mark: '',
-            type: 'text',
-          },
-        ];
-      }
-      return { hasFill, content_list };
-    },
+
     /**
      * 移动选项
      * @param {'up'|'down'} type 类型

+ 3 - 6
src/views/exercise_questions/create/components/exercises/FillQuestion.vue

@@ -112,7 +112,7 @@ import QuestionMixin from '../common/QuestionMixin.js';
 
 import { getRandomNumber } from '@/utils';
 import { addTone, handleToneValue } from '@/views/exercise_questions/data/common';
-import { fillData } from '@/views/exercise_questions/data/fill';
+import { fillData, analysisRecognitionFillData } from '@/views/exercise_questions/data/fill';
 
 export default {
   name: 'FillQuestion',
@@ -129,11 +129,8 @@ export default {
      */
     recognition(text) {
       let arr = this.recognitionCommon(text);
-      let str = '';
-      arr.forEach((text) => {
-        str += `<p>${text}</p>`; // 拼接成富文本
-      });
-      this.data.article = str;
+      let obj = analysisRecognitionFillData(arr);
+      this.recognitionCommonSetObj(obj);
       this.identifyText();
     },
     // 识别文本

+ 3 - 1
src/views/exercise_questions/create/components/exercises/JudgeQuestion.vue

@@ -127,6 +127,7 @@ import {
   option_type_list,
   option_type_value_list,
   getOption,
+  analysisRecognitionJudgeData,
 } from '@/views/exercise_questions/data/judge';
 
 export default {
@@ -173,7 +174,8 @@ export default {
      */
     recognition(text) {
       let arr = this.recognitionCommon(text);
-      this.data.option_list = arr.map((content) => getOption(content));
+      let obj = analysisRecognitionJudgeData(arr);
+      this.recognitionCommonSetObj(obj);
       this.data.answer.answer_list = [];
     },
     /**

+ 3 - 6
src/views/exercise_questions/create/components/exercises/ListenFillQuestion.vue

@@ -141,7 +141,7 @@ import QuestionMixin from '../common/QuestionMixin.js';
 
 import { getRandomNumber } from '@/utils';
 import { addTone, handleToneValue } from '@/views/exercise_questions/data/common';
-import { listenFillData } from '@/views/exercise_questions/data/listenFill';
+import { analysisRecognitionListenFillData, listenFillData } from '@/views/exercise_questions/data/listenFill';
 
 export default {
   name: 'ListenFillQuestion',
@@ -172,11 +172,8 @@ export default {
      */
     recognition(text) {
       let arr = this.recognitionCommon(text);
-      let str = '';
-      arr.forEach((text) => {
-        str += `<p>${text}</p>`; // 拼接成富文本
-      });
-      this.data.article = str;
+      let obj = analysisRecognitionListenFillData(arr);
+      this.recognitionCommonSetObj(obj);
       this.identifyText();
     },
     // 识别文本

+ 3 - 1
src/views/exercise_questions/create/components/exercises/ListenJudgeQuestion.vue

@@ -148,6 +148,7 @@ import {
   option_type_list,
   option_type_value_list,
   getOption,
+  analysisRecognitionListenJudgeData,
 } from '@/views/exercise_questions/data/listenJudge';
 
 export default {
@@ -194,7 +195,8 @@ export default {
      */
     recognition(text) {
       let arr = this.recognitionCommon(text);
-      this.data.option_list = arr.map((content) => getOption(content));
+      let obj = analysisRecognitionListenJudgeData(arr);
+      this.recognitionCommonSetObj(obj);
       this.data.answer.answer_list = [];
     },
     /**

+ 10 - 2
src/views/exercise_questions/create/components/exercises/ListenSelectQuestion.vue

@@ -166,7 +166,12 @@ import UploadAudio from '../common/UploadAudio.vue';
 import QuestionMixin from '../common/QuestionMixin.js';
 
 import { selectTypeList, scoreTypeList, changeOptionType, isEnable } from '@/views/exercise_questions/data/common';
-import { getListenSelectData, getOption, getSubdivisionOption } from '@/views/exercise_questions/data/listenSelect';
+import {
+  analysisRecognitionListenSelectData,
+  getListenSelectData,
+  getOption,
+  getSubdivisionOption,
+} from '@/views/exercise_questions/data/listenSelect';
 
 export default {
   name: 'ListenSelectQuestion',
@@ -196,7 +201,10 @@ export default {
      */
     recognition(text) {
       let arr = this.recognitionCommon(text);
-      this.data.option_list = arr.map((content) => getOption(content));
+      let obj = analysisRecognitionListenSelectData(arr);
+      this.data.property.is_option_subdivision = 'false';
+      this.changeOptionSubdivision('false');
+      this.recognitionCommonSetObj(obj);
       this.data.answer.answer_list = [];
     },
     changeSelectType(val) {

+ 8 - 26
src/views/exercise_questions/create/components/exercises/MatchingQuestion.vue

@@ -110,7 +110,12 @@
 import QuestionMixin from '../common/QuestionMixin.js';
 
 import { changeOptionType } from '@/views/exercise_questions/data/common';
-import { columnNumberList, getOption, getMatchingDataTemplate } from '@/views/exercise_questions/data/matching';
+import {
+  analysisRecognitionMatchingData,
+  columnNumberList,
+  getOption,
+  getMatchingDataTemplate,
+} from '@/views/exercise_questions/data/matching';
 
 export default {
   name: 'MatchingQuestion',
@@ -142,31 +147,8 @@ export default {
      */
     recognition(text) {
       let arr = this.recognitionCommon(text);
-      if (arr.length === 0) {
-        this.data.option_list = [];
-        return;
-      }
-      let column = arr[0].split(' - ').length;
-      if (column !== 2 && column !== 3) {
-        this.$message.error('列数应为2列或3列');
-        return;
-      }
-      // 判断选项格式是否一致
-      let isQualify = arr.every((item) => {
-        if (item.split(' - ').length !== column) {
-          this.$message.error('选项格式不一致');
-          return false;
-        }
-        return true;
-      });
-      if (!isQualify) return;
-
-      this.data.property.column_number = column;
-      this.data.option_list = arr.map((item) => {
-        return item.split(' - ').map((li) => {
-          return getOption(li);
-        });
-      });
+      let obj = analysisRecognitionMatchingData(arr);
+      this.recognitionCommonSetObj(obj);
     },
     addOption() {
       let newOption = [];

+ 3 - 8
src/views/exercise_questions/create/components/exercises/ReadAloudQuestion.vue

@@ -91,7 +91,7 @@
 import QuestionMixin from '../common/QuestionMixin.js';
 import UploadAudio from '../common/UploadAudio.vue';
 
-import { readAloudData } from '@/views/exercise_questions/data/readAloud';
+import { analysisRecognitionReadAloudData, readAloudData } from '@/views/exercise_questions/data/readAloud';
 
 export default {
   name: 'ReadAloudQuestion',
@@ -111,13 +111,8 @@ export default {
      */
     recognition(text) {
       let arr = this.recognitionCommon(text);
-      if (arr.length === 0) return;
-      let reg = /^文段[::]/;
-      if (reg.test(arr[0])) {
-        this.data.text = arr[0].replace(reg, '');
-      } else {
-        this.data.text = '';
-      }
+      let obj = analysisRecognitionReadAloudData(arr);
+      this.recognitionCommonSetObj(obj);
     },
   },
 };

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

@@ -80,7 +80,12 @@
 </template>
 
 <script>
-import { readData, questionTypeOption, exerciseTypeList } from '@/views/exercise_questions/data/read';
+import {
+  readData,
+  questionTypeOption,
+  exerciseTypeList,
+  analysisRecognitionReadData,
+} from '@/views/exercise_questions/data/read';
 import { AddQuestionToExercise, DeleteQuestion } from '@/api/exercise';
 import { questionDataList } from '@/views/exercise_questions/data/questionType';
 
@@ -151,13 +156,8 @@ export default {
      */
     recognition(text) {
       let arr = this.recognitionCommon(text);
-      if (arr.length < 0) return;
-      let reg = /^文章[::]/;
-      if (reg.test(arr[0])) {
-        this.data.article = arr[0].replace(reg, '');
-      } else {
-        this.data.article = '';
-      }
+      let obj = analysisRecognitionReadData(arr);
+      this.recognitionCommonSetObj(obj);
     },
     /**
      * 智能识别

+ 10 - 4
src/views/exercise_questions/create/components/exercises/RepeatQuestion.vue

@@ -34,19 +34,19 @@
             <template v-else>
               <div :class="['upload-audio-play']">
                 <UploadAudio
+                  v-if="data.other.audio_generation_method === 'upload'"
                   :key="item.audio_file_id || i"
                   :file-id="item.audio_file_id"
                   :item-index="i"
                   :show-upload="!item.audio_file_id"
                   @upload="uploads"
                   @deleteFile="deleteFiles"
-                  v-if="data.other.audio_generation_method === 'upload'"
                 />
                 <div
                   v-else-if="data.other.audio_generation_method === 'auto'"
+                  v-loading="loading_list[i] ? loading_list[i].loading : false"
                   class="auto-matically"
                   @click="handleMatically(item, i)"
-                  v-loading="loading_list[i] ? loading_list[i].loading : false"
                 >
                   <SvgIcon icon-class="voiceprint-line" class="record" />
                   <span class="auto-btn">{{ item.audio_file_id ? '已生成' : '生成音频' }}</span>
@@ -134,7 +134,12 @@ import QuestionMixin from '../common/QuestionMixin.js';
 import SoundRecord from '../common/SoundRecord.vue';
 
 import { selectTypeList, changeOptionType, addTone } from '@/views/exercise_questions/data/common';
-import { repeatData, getOption, audioGenerationMethodList } from '@/views/exercise_questions/data/repeat';
+import {
+  analysisRecognitionRepeatData,
+  repeatData,
+  getOption,
+  audioGenerationMethodList,
+} from '@/views/exercise_questions/data/repeat';
 import { GetStaticResources } from '@/api/app';
 
 export default {
@@ -186,7 +191,8 @@ export default {
      */
     recognition(text) {
       let arr = this.recognitionCommon(text);
-      this.data.option_list = arr.map((content) => getOption(content));
+      let obj = analysisRecognitionRepeatData(arr);
+      this.recognitionCommonSetObj(obj);
     },
     addOption() {
       this.data.option_list.push(getOption());

+ 7 - 19
src/views/exercise_questions/create/components/exercises/ReplaceAnswerQuestion.vue

@@ -116,7 +116,11 @@ import QuestionMixin from '../common/QuestionMixin.js';
 import { getRandomNumber } from '@/utils/index';
 
 import { selectTypeList, changeOptionType } from '@/views/exercise_questions/data/common';
-import { replaceAnswerData, getOption } from '@/views/exercise_questions/data/replaceAnswer';
+import {
+  analysisRecognitionReplaceAnswerData,
+  replaceAnswerData,
+  getOption,
+} from '@/views/exercise_questions/data/replaceAnswer';
 
 export default {
   name: 'ReplaceAnswertQuestion',
@@ -136,24 +140,8 @@ export default {
      */
     recognition(text) {
       let arr = this.recognitionCommon(text);
-      this.data.option_list = [];
-      this.data.property.row_count = arr.length;
-      let column_count = 0;
-      arr.forEach((item) => {
-        let item_arr = item.split(' ');
-        if (item_arr.length > column_count) {
-          column_count = item_arr.length;
-        }
-      });
-      this.data.property.column_count = column_count;
-      for (let i = 0; i < arr.length; i++) {
-        let table_item = [];
-        let item_arr = arr[i].split(' ');
-        for (let j = 0; j < column_count; j++) {
-          table_item.push({ content: item_arr[j] ? item_arr[j] : '', mark: getRandomNumber() });
-        }
-        this.data.option_list.push(table_item);
-      }
+      let obj = analysisRecognitionReplaceAnswerData(arr);
+      this.recognitionCommonSetObj(obj);
     },
     addOption() {
       this.data.option_list.push(getOption());

+ 4 - 2
src/views/exercise_questions/create/components/exercises/SelectQuestion.vue

@@ -157,7 +157,7 @@ import {
   getSelectData,
   getOption,
   getSubdivisionOption,
-  analysisRecognitionData,
+  analysisRecognitionSelectData,
 } from '@/views/exercise_questions/data/select';
 
 export default {
@@ -185,7 +185,9 @@ export default {
      */
     recognition(text) {
       let arr = this.recognitionCommon(text);
-      let obj = analysisRecognitionData(arr);
+      let obj = analysisRecognitionSelectData(arr);
+      this.data.property.is_option_subdivision = 'false';
+      this.changeOptionSubdivision('false');
       this.recognitionCommonSetObj(obj);
       this.data.answer.answer_list = [];
     },

+ 8 - 9
src/views/exercise_questions/create/components/exercises/SortQuestion.vue

@@ -129,7 +129,12 @@
 <script>
 import QuestionMixin from '../common/QuestionMixin.js';
 
-import { getOption, sortTypeList, getSortDataTemplate } from '@/views/exercise_questions/data/sort';
+import {
+  analysisRecognitionSortData,
+  getOption,
+  sortTypeList,
+  getSortDataTemplate,
+} from '@/views/exercise_questions/data/sort';
 import { changeOptionType } from '@/views/exercise_questions/data/common';
 import { getRandomNumber } from '@/utils/index';
 
@@ -182,14 +187,8 @@ export default {
      */
     recognition(text) {
       let arr = this.recognitionCommon(text);
-      this.data.option_list = arr.map((item) => {
-        let data_list = item.split(' ').map((content) => getOption(content));
-        let item_obj = {
-          mark: getRandomNumber(),
-          data_list,
-        };
-        return item_obj;
-      });
+      let obj = analysisRecognitionSortData(arr);
+      this.recognitionCommonSetObj(obj);
     },
     changeType(item) {
       if (item === 'vertical') {

+ 7 - 40
src/views/exercise_questions/create/components/exercises/TableFillQuestion.vue

@@ -155,7 +155,11 @@
 import QuestionMixin from '../common/QuestionMixin.js';
 
 import { getRandomNumber } from '@/utils';
-import { getTableFillData, getOption } from '@/views/exercise_questions/data/tableFill.js';
+import {
+  analysisRecognitionTableFillData,
+  getTableFillData,
+  getOption,
+} from '@/views/exercise_questions/data/tableFill.js';
 import { addTone, handleToneValue } from '@/views/exercise_questions/data/common';
 
 export default {
@@ -237,45 +241,8 @@ export default {
      */
     recognition(text) {
       let arr = this.recognitionCommon(text, true);
-      if (arr.length === 0) return;
-      // 表头
-      let headerNum = arr[0].split('-').length;
-      if (headerNum > 1 && (headerNum < 2 || headerNum > 5)) {
-        this.$message.error('行数在2-5之间');
-        return;
-      }
-      if (headerNum > 1) {
-        this.data.property.is_enable_table_header = 'true';
-        this.data.property.column_number = headerNum;
-        this.data.option_header_list = arr[0]
-          .split('-')
-          .map((item) => ({ mark: getRandomNumber(), text: item, width: 100 / headerNum }));
-        arr.shift();
-      } else {
-        this.data.property.is_enable_table_header = 'false';
-        this.data.option_header_list = [];
-      }
-
-      // 通过正则表达式匹配出表格内容
-      let matches = arr.map((str) => {
-        return str.match(/[((](.*?)[))]/g).map((item) => item.replace(/[((](.*?)[))]/g, '$1'));
-      });
-      let isMate = matches.every((item) => {
-        if (item.length !== headerNum) {
-          this.$message.error('表格列数不匹配');
-          return false;
-        }
-        return true;
-      });
-      if (!isMate) return;
-      if (this.data.property.row_number < 1 || this.data.property.row_number > 30) {
-        this.$message.error('行数在1-30之间');
-        return;
-      }
-      this.data.property.row_number = matches.length;
-      this.data.option_list = matches.map((item) => {
-        return item.map((li) => getOption(li));
-      });
+      let obj = analysisRecognitionTableFillData(arr);
+      this.recognitionCommonSetObj(obj);
     },
     /**
      *鼠标拖动更改列宽

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

@@ -45,19 +45,19 @@
                 <template v-else>
                   <div :class="['upload-audio-play']">
                     <UploadAudio
+                      v-if="data.other.audio_generation_method === 'upload'"
                       :key="item.audio_file_id || i"
                       :file-id="item.audio_file_id"
                       :item-index="i"
                       :show-upload="!item.audio_file_id"
                       @upload="uploads"
                       @deleteFile="deleteFiles"
-                      v-if="data.other.audio_generation_method === 'upload'"
                     />
                     <div
                       v-else-if="data.other.audio_generation_method === 'auto'"
+                      v-loading="loading_list[i] ? loading_list[i].loading : false"
                       class="auto-matically"
                       @click="handleMatically(item, i)"
-                      v-loading="loading_list[i] ? loading_list[i].loading : false"
                     >
                       <SvgIcon icon-class="voiceprint-line" class="record" />
                       <span class="auto-btn">{{ item.audio_file_id ? '已生成' : '生成音频' }}</span>

+ 2 - 3
src/views/exercise_questions/create/components/exercises/WordDictationQuestion.vue

@@ -31,19 +31,19 @@
             <template v-else>
               <div :class="['upload-audio-play']">
                 <UploadAudio
+                  v-if="data.other.audio_generation_method === 'upload'"
                   :key="item.audio_file_id || i"
                   :file-id="item.audio_file_id"
                   :item-index="i"
                   :show-upload="!item.audio_file_id"
                   @upload="uploads"
                   @deleteFile="deleteFiles"
-                  v-if="data.other.audio_generation_method === 'upload'"
                 />
                 <div
                   v-else-if="data.other.audio_generation_method === 'auto'"
+                  v-loading="loading_list[i] ? loading_list[i].loading : false"
                   class="auto-matically"
                   @click="handleMatically(item, i)"
-                  v-loading="loading_list[i] ? loading_list[i].loading : false"
                 >
                   <SvgIcon icon-class="voiceprint-line" class="record" />
                   <span class="auto-btn">{{ item.audio_file_id ? '已生成' : '生成音频' }}</span>
@@ -192,7 +192,6 @@ export default {
       immediate: true,
     },
   },
-  mounted() {},
   methods: {
     addOption() {
       this.data.option_list.push(getOption());

+ 3 - 10
src/views/exercise_questions/create/components/exercises/WriteQuestion.vue

@@ -98,7 +98,7 @@
 <script>
 import QuestionMixin from '../common/QuestionMixin.js';
 
-import { writeData } from '@/views/exercise_questions/data/write';
+import { analysisRecognitionWriteData, writeData } from '@/views/exercise_questions/data/write';
 
 export default {
   name: 'WriteQuestion',
@@ -115,15 +115,8 @@ export default {
      */
     recognition(text) {
       let arr = this.recognitionCommon(text);
-      if (arr.length < 0) return;
-      let reg = /^范文[::]/;
-      if (reg.test(arr[0])) {
-        this.data.sample_text = arr[0].replace(reg, '');
-        this.data.property.is_enable_sample_text = 'true';
-      } else {
-        this.data.sample_text = '';
-        this.data.property.is_enable_sample_text = 'false';
-      }
+      let obj = analysisRecognitionWriteData(arr);
+      this.recognitionCommonSetObj(obj);
     },
   },
 };

+ 22 - 4
src/views/exercise_questions/create/index.vue

@@ -28,7 +28,7 @@
         </ul>
       </div>
       <div class="list-operate">
-        <!-- <el-button type="primary" @click="oneClickImport">一键导入</el-button> -->
+        <el-button type="primary" @click="oneClickImport">一键导入</el-button>
         <el-button type="primary" @click="showSelectQuestionType">新建</el-button>
       </div>
     </div>
@@ -177,9 +177,27 @@ export default {
       this.importVisible = true;
     },
     oneKeyImport(text) {
-      let questionList = text.split(/\n\s*\n+/);
-      console.log(questionList);
-      analysisOneKeyImportData(questionList);
+      let dataList = analysisOneKeyImportData(text.split(/\n\s*\n+/));
+      this.importVisible = false;
+      const loading = this.$loading({
+        text: '正在导入题目,请稍等...',
+      });
+      // 一键导入题目,按 dataList 顺序添加题目,等待第一个题目添加成功后,再添加第二个题目
+      dataList
+        .reduce((promise, { type, additional_type, content }) => {
+          return promise.then(() => {
+            return AddQuestionToExercise({
+              exercise_id: this.exercise_id,
+              type,
+              additional_type,
+              content,
+            });
+          });
+        }, Promise.resolve())
+        .then(() => {
+          this.getExerciseQuestionIndexList(false, true);
+          loading.close();
+        });
     },
     // 创建默认题目
     createDefaultQuestion() {

+ 85 - 0
src/views/exercise_questions/data/dialogue.js

@@ -1,5 +1,6 @@
 import { getRandomNumber } from '@/utils';
 import { stemTypeList, questionNumberTypeList, scoreTypeList, switchOption, fontSizeList } from './common';
+import { Message } from 'element-ui';
 
 export const roleDefaultColorList = ['#306EFF', '#3ABD38', '#FC8E3D', '#FC493D', '#BF3DFC']; // 角色默认颜色
 
@@ -16,6 +17,90 @@ export function getRole(index, name = '') {
   };
 }
 
+/**
+ * 获取文本内容列表
+ * @param {string} text 文本
+ */
+export function getTextContenList(text) {
+  let str = text;
+  let reg = /_{3,}/;
+  let hasFill = reg.test(str); // 是否有填空
+  let content_list = [];
+  if (hasFill) {
+    str = str.replace(/(_{3,})/g, '###$1###');
+    content_list = str
+      .split('###')
+      .filter((item) => item)
+      .map((content) => {
+        let isInput = reg.test(content);
+        return {
+          content: isInput ? '' : content,
+          mark: isInput ? getRandomNumber() : '',
+          type: isInput ? 'input' : 'text',
+        };
+      });
+  } else {
+    content_list = [
+      {
+        content: str,
+        mark: '',
+        type: 'text',
+      },
+    ];
+  }
+  return { hasFill, content_list };
+}
+
+/**
+ * 解析智能识别数据
+ * @param {array} arr 智能识别数据
+ * @returns object
+ */
+export function analysisRecognitionDialogueData(arr) {
+  // 角色列表
+  let roleList = [];
+  arr.forEach((item) => {
+    if (!item.match(/.+[::].+/)) return;
+    let [role] = item.split(/[::]/);
+    if (!roleList.includes(role)) {
+      roleList.push(role);
+    }
+  });
+  if (roleList.length < 2 || roleList.length > 5) {
+    return Message.warning('角色数不符合要求,最小为 2,最大为 5');
+  }
+  let role_number = roleList.length;
+  let role_list = roleList.map((item, i) => getRole(i, item));
+
+  let option_list = [];
+  arr.forEach((item) => {
+    if (item.match(/.+[::].+/)) {
+      let [role, content] = item.split(/[::]/);
+      let { hasFill, content_list } = getTextContenList(content);
+      option_list.push({
+        role: role_list.find((item) => item.name === role).mark,
+        text: content,
+        mark: getRandomNumber(),
+        file_id: '',
+        content_list,
+        type: hasFill ? 'input' : 'text',
+      });
+    }
+    if (item.match(/^-学生$/)) {
+      option_list.push({
+        mark: getRandomNumber(),
+        file_id: '',
+        type: 'input_student',
+      });
+    }
+  });
+  return {
+    'data.property.role_number': role_number,
+    'data.property.role_list': role_list,
+    'data.option_list': option_list,
+  };
+}
+
 // 对话题数据模板
 export function getDialogueData() {
   return {

+ 10 - 0
src/views/exercise_questions/data/fill.js

@@ -1,5 +1,15 @@
 import { stemTypeList, questionNumberTypeList, scoreTypeList, switchOption, fontSizeList } from './common';
 
+/**
+ * 解析智能识别数据
+ * @param {array} arr 智能识别数据
+ * @returns object
+ */
+export function analysisRecognitionFillData(arr) {
+  const article = arr.map((text) => `<p>${text}</p>`).join('');
+  return { 'data.article': article };
+}
+
 // 填空题数据模板
 export const fillData = {
   type: 'fill', // 题型

+ 11 - 0
src/views/exercise_questions/data/judge.js

@@ -22,6 +22,17 @@ export function getOption(content = '') {
 }
 
 /**
+ * 解析智能识别数据
+ * @param {array} arr 智能识别数据
+ * @returns object
+ */
+export function analysisRecognitionJudgeData(arr) {
+  return {
+    'data.option_list': arr.map((content) => getOption(content)),
+  };
+}
+
+/**
  * 获取判断题数据模板(防止 mark 重复)
  * @returns {object} 判断题数据模板
  */

+ 10 - 0
src/views/exercise_questions/data/listenFill.js

@@ -1,5 +1,15 @@
 import { stemTypeList, questionNumberTypeList, scoreTypeList, switchOption, fontSizeList } from './common';
 
+/**
+ * 解析智能识别数据
+ * @param {array} arr 智能识别数据
+ * @returns object
+ */
+export function analysisRecognitionListenFillData(arr) {
+  const article = arr.map((text) => `<p>${text}</p>`).join('');
+  return { 'data.article': article };
+}
+
 // 听后填空题数据模板
 export const listenFillData = {
   type: 'listen_fill', // 题型

+ 11 - 0
src/views/exercise_questions/data/listenJudge.js

@@ -22,6 +22,17 @@ export function getOption(content = '') {
 }
 
 /**
+ * 解析智能识别数据
+ * @param {array} arr 智能识别数据
+ * @returns object
+ */
+export function analysisRecognitionListenJudgeData(arr) {
+  return {
+    'data.option_list': arr.map((content) => getOption(content)),
+  };
+}
+
+/**
  * 获取听后判断题数据模板(防止 mark 重复)
  * @returns {object} 判断题数据模板
  */

+ 11 - 0
src/views/exercise_questions/data/listenSelect.js

@@ -27,6 +27,17 @@ export function getSubdivisionOption(number = 2, index) {
 }
 
 /**
+ * 解析智能识别数据
+ * @param {array} arr 智能识别数据
+ * @returns object
+ */
+export function analysisRecognitionListenSelectData(arr) {
+  return {
+    'data.option_list': arr.map(getOption),
+  };
+}
+
+/**
  * 获取听后选择题数据模板(防止 mark 重复)
  */
 export function getListenSelectData() {

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

@@ -7,6 +7,7 @@ import {
   fontSizeList,
 } from './common';
 import { getRandomNumber } from '@/utils/index';
+import { Message } from 'element-ui';
 
 // 连线类型列表
 export const columnNumberList = [
@@ -19,6 +20,38 @@ export function getOption(content = '') {
 }
 
 /**
+ * 解析智能识别数据
+ * @param {array} arr 智能识别数据
+ * @returns object
+ */
+export function analysisRecognitionMatchingData(arr) {
+  if (arr.length === 0) {
+    return {};
+  }
+  let column = arr[0].split(' - ').length;
+  if (column !== 2 && column !== 3) {
+    Message.error('连线题列数应为2列或3列');
+    return {};
+  }
+  // 判断选项格式是否一致
+  let isQualify = arr.every((item) => {
+    if (item.split(' - ').length !== column) {
+      Message.error('连线题选项格式不一致');
+      return false;
+    }
+    return true;
+  });
+  if (!isQualify) return {};
+
+  const option_list = arr.map((item) => {
+    return item.split(' - ').map((li) => {
+      return getOption(li);
+    });
+  });
+  return { 'data.option_list': option_list, 'data.property.column_number': column };
+}
+
+/**
  * 获取连线题数据模板
  * 因为 option_list 和 answer.answer_list 中的数据是一一对应的,所以需要函数生成来保持一致
  */

+ 125 - 16
src/views/exercise_questions/data/questionType.js

@@ -1,28 +1,30 @@
-import { getSelectData } from './select';
-import { getJudgeData } from './judge';
-import { fillData } from './fill';
-import { getSortDataTemplate } from './sort';
-import { getMatchingDataTemplate } from './matching';
+import { analysisRecognitionSelectData, getSelectData } from './select';
+import { analysisRecognitionJudgeData, getJudgeData } from './judge';
+import { analysisRecognitionFillData, fillData } from './fill';
+import { analysisRecognitionSortData, getSortDataTemplate } from './sort';
+import { analysisRecognitionMatchingData, getMatchingDataTemplate } from './matching';
 import { ChooseToneData } from './chooseTone';
 import { essayQuestionData } from './essayQuestion';
-import { readAloudData } from './readAloud';
-import { repeatData } from './repeat';
+import { analysisRecognitionReadAloudData, readAloudData } from './readAloud';
+import { analysisRecognitionRepeatData, repeatData } from './repeat';
 import { talkPictrueData } from './talkPicture';
-import { getDialogueData } from './dialogue';
+import { analysisRecognitionDialogueData, getDialogueData } from './dialogue';
 import { answerQuestionData } from './answerQuestion';
-import { replaceAnswerData } from './replaceAnswer';
-import { getListenSelectData } from './listenSelect';
-import { listenFillData } from './listenFill';
-import { getListenJudgeData } from './listenJudge';
+import { analysisRecognitionReplaceAnswerData, replaceAnswerData } from './replaceAnswer';
+import { analysisRecognitionListenSelectData, getListenSelectData } from './listenSelect';
+import { analysisRecognitionListenFillData, listenFillData } from './listenFill';
+import { analysisRecognitionListenJudgeData, getListenJudgeData } from './listenJudge';
 import { chineseData } from './chinese';
 import { wordCardData } from './wordCard';
-import { writeData } from './write';
+import { analysisRecognitionWriteData, writeData } from './write';
 import { writePictrueData } from './writePicture';
-import { readData } from './read';
-import { getTableFillData } from './tableFill';
+import { analysisRecognitionReadData, readData } from './read';
+import { analysisRecognitionTableFillData, getTableFillData } from './tableFill';
 import { wordDictationData } from './wordDictation';
 import { activityData } from './activity';
 
+import { AddQuestionToExercise } from '@/api/exercise';
+
 // 题型源数据
 export const questionTypeDataOption = [
   {
@@ -147,10 +149,83 @@ export function getExerciseTypeList(arr) {
 export const exerciseTypeList = getExerciseTypeList(questionTypeOption);
 
 /**
+ * 解析智能识别数据
+ * @param {array} arr 智能识别数据
+ * @param {object} data 题目数据
+ * @param {boolean} isEnableReferenceAnswer 是否启用参考答案
+ */
+function analysisRecognition(arr, data, isEnableReferenceAnswer = false) {
+  let sliceLength = 0;
+  const stemReg = /^题干[::]/;
+  if (stemReg.test(arr[0])) {
+    data.stem = arr[0].replace(stemReg, '');
+    sliceLength += 1;
+  }
+
+  const desReg = /^提示[::]/;
+  if (desReg.test(arr[sliceLength])) {
+    data.description = arr[sliceLength].replace(desReg, '');
+    data.property.is_enable_description = 'true';
+    sliceLength += 1;
+  } else {
+    data.description = '';
+    data.property.is_enable_description = 'false';
+  }
+
+  if (isEnableReferenceAnswer) {
+    let referenceReg = /^参考答案[::]/;
+    let index = arr.findIndex((item) => referenceReg.test(item));
+    if (index === -1) {
+      data.reference_answer = '';
+      data.property.is_enable_reference_answer = 'false';
+    } else {
+      data.reference_answer = arr[index].replace(referenceReg, '');
+      data.property.is_enable_reference_answer = 'true';
+      arr.splice(index, 1);
+    }
+  }
+  return arr.slice(sliceLength);
+}
+
+// 问答题、看图说话、看图写作 无单独数据解析函数
+// 各个题型的智能识别数据解析函数
+const analysisFunctions = {
+  select: analysisRecognitionSelectData,
+  judge: analysisRecognitionJudgeData,
+  fill: analysisRecognitionFillData,
+  sort: analysisRecognitionSortData,
+  matching: analysisRecognitionMatchingData,
+  // choose_tone: analysisRecognitionSelectData,
+  table_fill: analysisRecognitionTableFillData,
+  read_aloud: analysisRecognitionReadAloudData,
+  repeat: analysisRecognitionRepeatData,
+  dialogue: analysisRecognitionDialogueData,
+  replace_answer: analysisRecognitionReplaceAnswerData,
+  listen_select: analysisRecognitionListenSelectData,
+  listen_fill: analysisRecognitionListenFillData,
+  listen_judge: analysisRecognitionListenJudgeData,
+  write: analysisRecognitionReadData,
+  read: analysisRecognitionWriteData,
+};
+/**
+ * 获取智能识别各个题型独有的数据解析返回值
+ * @param {array} dataArr 智能识别数据
+ * @param {string} type 题型类型
+ * @returns object
+ */
+function getRecognitionData(dataArr, type) {
+  const analysisFunction = analysisFunctions[type];
+  if (!analysisFunction) return {};
+
+  return analysisFunction(dataArr);
+}
+
+/**
  * 解析一键导入数据
  * @param {Array} questionList 题目列表
  */
 export function analysisOneKeyImportData(questionList) {
+  let dataList = [];
   questionList.forEach((item) => {
     let arr = item
       .split(/[\r\n]/)
@@ -158,10 +233,44 @@ export function analysisOneKeyImportData(questionList) {
       .filter((item) => item);
     if (arr.length === 0) return;
     let [question_number, type_name] = arr[0].split('.');
+    // 题型类型
     let type = Object.entries(exerciseNames).find(([key, value]) => {
       return value === type_name.trim();
     })?.[0];
+    arr = arr.slice(1);
 
-    if (!type) return;
+    if (!type || arr.length === 0) return;
+
+    // 是否启用参考答案
+    let isEnableReferenceAnswer = false; // 是否启用参考答案
+    if (['table_fill', 'answer_question'].includes(type)) {
+      isEnableReferenceAnswer = true;
+    }
+    let data = JSON.parse(JSON.stringify(questionDataList[type]));
+    data.property.question_number = question_number; // 题号
+
+    let dataArr = analysisRecognition(arr, data, isEnableReferenceAnswer); // 解析公有的数据,并返回剩余的数据
+
+    let recObj = getRecognitionData(dataArr, type); // 获取各个题型独有的数据解析返回值
+    // 将独有的数据解析返回值合并到 data 中
+    Object.keys(recObj).forEach((key) => {
+      key
+        .split('.')
+        .slice(1)
+        .reduce((prev, cur, index, arr) => {
+          if (index === arr.length - 1) {
+            prev[cur] = recObj[key];
+          } else {
+            return prev[cur];
+          }
+        }, data);
+    });
+    dataList.push({
+      type: data.type,
+      additional_type: data.type === 'select' ? 'single' : '',
+      content: data,
+    });
   });
+
+  return dataList;
 }

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

@@ -13,6 +13,18 @@ export const questionTypeOption = [
 // 题型类型列表
 export const exerciseTypeList = getExerciseTypeList(questionTypeOption);
 
+/**
+ * 解析智能识别数据
+ * @param {array} arr 智能识别数据
+ * @returns object
+ */
+export function analysisRecognitionReadData(arr) {
+  if (arr.length < 0) return;
+  let reg = /^文章[::]/;
+  let article = reg.test(arr[0]) ? arr[0].replace(reg, '') : '';
+  return { 'data.article': article };
+}
+
 // 阅读题数据模板
 export const readData = {
   type: 'read', // 题型

+ 12 - 0
src/views/exercise_questions/data/readAloud.js

@@ -1,5 +1,17 @@
 import { stemTypeList, questionNumberTypeList, scoreTypeList, switchOption, fontSizeList } from './common';
 
+/**
+ * 解析智能识别数据
+ * @param {array} arr 智能识别数据
+ * @returns object
+ */
+export function analysisRecognitionReadAloudData(arr) {
+  if (arr.length === 0) return {};
+  let reg = /^文段[::]/;
+  let text = reg.test(arr[0]) ? arr[0].replace(reg, '') : '';
+  return { 'data.text': text };
+}
+
 // 朗读题数据模板
 export const readAloudData = {
   type: 'read_aloud', // 题型

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

@@ -27,6 +27,16 @@ export const audioGenerationMethodList = [
   },
 ];
 
+/**
+ * 解析智能识别数据
+ * @param {array} arr 智能识别数据
+ * @returns object
+ */
+export function analysisRecognitionRepeatData(arr) {
+  const option_list = arr.map(getOption);
+  return { 'data.option_list': option_list };
+}
+
 // 听说训练数据模板
 export const repeatData = {
   type: 'repeat', // 题型

+ 29 - 0
src/views/exercise_questions/data/replaceAnswer.js

@@ -10,6 +10,35 @@ export function getOption(content = '') {
   ];
 }
 
+/**
+ * 解析智能识别数据
+ * @param {array} arr 智能识别数据
+ * @returns object
+ */
+export function analysisRecognitionReplaceAnswerData(arr) {
+  let option_list = [];
+  let column_count = 0;
+  arr.forEach((item) => {
+    let item_arr = item.split(' ');
+    if (item_arr.length > column_count) {
+      column_count = item_arr.length;
+    }
+  });
+  for (let i = 0; i < arr.length; i++) {
+    let table_item = [];
+    let item_arr = arr[i].split(' ');
+    for (let j = 0; j < column_count; j++) {
+      table_item.push({ content: item_arr[j] ? item_arr[j] : '', mark: getRandomNumber() });
+    }
+    option_list.push(table_item);
+  }
+  return {
+    'data.option_list': option_list,
+    'data.property.row_count': arr.length,
+    'data.property.column_count': column_count,
+  };
+}
+
 // 替换练习数据模板
 export const replaceAnswerData = {
   type: 'replace_answer', // 题型

+ 2 - 2
src/views/exercise_questions/data/select.js

@@ -29,9 +29,9 @@ export function getSubdivisionOption(number = 2) {
  * @param {array} arr 智能识别数据
  * @returns object
  */
-export function analysisRecognitionData(arr) {
+export function analysisRecognitionSelectData(arr) {
   return {
-    'data.option_list': arr.map((content) => getOption(content)),
+    'data.option_list': arr.map(getOption),
   };
 }
 

+ 17 - 0
src/views/exercise_questions/data/sort.js

@@ -15,6 +15,23 @@ export const sortTypeList = [
   { value: 'horizontal', label: '连词成句' },
   { value: 'vertical', label: '连句成段' },
 ];
+
+/**
+ * 解析智能识别数据
+ * @param {array} arr 智能识别数据
+ * @returns object
+ */
+export function analysisRecognitionSortData(arr) {
+  const option_list = arr.map((item) => ({
+    mark: getRandomNumber(),
+    data_list: item.split(' ').map(getOption),
+  }));
+
+  return {
+    'data.option_list': option_list,
+  };
+}
+
 // 选择题数据模板
 export function getSortDataTemplate() {
   let option_list = [

+ 58 - 0
src/views/exercise_questions/data/tableFill.js

@@ -1,5 +1,6 @@
 import { getRandomNumber } from '@/utils';
 import { stemTypeList, questionNumberTypeList, scoreTypeList, switchOption, fontSizeList } from './common';
+import { Message } from 'element-ui';
 
 export function getOption(text = '') {
   return {
@@ -10,6 +11,63 @@ export function getOption(text = '') {
 }
 
 /**
+ * 解析智能识别数据
+ * @param {array} arr 智能识别数据
+ * @returns object
+ */
+export function analysisRecognitionTableFillData(arr) {
+  if (arr.length === 0) return {};
+  // 表头
+  let headerNum = arr[0].split('-').length;
+  if (headerNum > 1 && (headerNum < 2 || headerNum > 5)) {
+    Message.error('填表题行数在2-5之间');
+    return {};
+  }
+  let is_enable_table_header = switchOption[0].value;
+  let column_number = 2;
+  let option_header_list = [];
+  if (headerNum > 1) {
+    is_enable_table_header = switchOption[1].value;
+    column_number = headerNum;
+    option_header_list = arr[0]
+      .split('-')
+      .map((item) => ({ mark: getRandomNumber(), text: item, width: 100 / headerNum }));
+    arr.shift();
+  } else {
+    is_enable_table_header = switchOption[0].value;
+    option_header_list = [];
+  }
+
+  // 通过正则表达式匹配出表格内容
+  console.log(arr);
+  let matches = arr.map((str) => {
+    return str.match(/[((](.*?)[))]/g).map((item) => item.replace(/[((](.*?)[))]/g, '$1'));
+  });
+  let isMate = matches.every((item) => {
+    if (item.length !== headerNum) {
+      Message.error('填表题表格列数不匹配');
+      return false;
+    }
+    return true;
+  });
+  if (!isMate) return {};
+  const row_number = matches.length;
+  if (row_number < 1 || row_number > 30) {
+    Message.error('填表题行数在1-30之间');
+    return {};
+  }
+  const option_list = matches.map((item) => item.map(getOption));
+
+  return {
+    'data.property.is_enable_table_header': is_enable_table_header,
+    'data.property.column_number': column_number,
+    'data.option_header_list': option_header_list,
+    'data.property.row_number': row_number,
+    'data.option_list': option_list,
+  };
+}
+
+/**
  * 获取填表题数据模板(防止 mark 重复)
  * @returns {object} 判断题数据模板
  */

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

@@ -7,6 +7,24 @@ import {
   fontSizeList,
 } from './common';
 
+/**
+ * 解析智能识别数据
+ * @param {array} arr 智能识别数据
+ * @returns object
+ */
+export function analysisRecognitionWriteData(arr) {
+  if (arr.length < 0) return {};
+  let reg = /^范文[::]/;
+  let isSample = reg.test(arr[0]);
+  let sample_text = isSample ? arr[0].replace(reg, '') : '';
+  let is_enable_description = isSample ? 'false' : 'true';
+
+  return {
+    'data.sample_text': sample_text,
+    'data.property.is_enable_description': is_enable_description,
+  };
+}
+
 // 基础写作数据模板
 export const writeData = {
   type: 'write', // 题型