Browse Source

自动生成音频

natasha 1 year ago
parent
commit
5aa6802ec7

+ 4 - 0
src/components/common/RichText.vue

@@ -8,6 +8,7 @@
     :class="['rich-text', isBorder ? 'is-border' : '']"
     :init="init"
     v-on="$listeners"
+    @onBlur="handleRichTextBlur"
   />
 </template>
 
@@ -314,6 +315,9 @@ export default {
     replaceSpanString(str) {
       return str.replace(/<span\b[^>]*>(.*?)<\/span>/gi, '$1');
     },
+    handleRichTextBlur() {
+      this.$emit('handleRichTextBlur');
+    },
   },
 };
 </script>

+ 53 - 13
src/views/exercise_questions/create/components/exercises/ChineseQuestion.vue

@@ -154,7 +154,7 @@ import QuestionMixin from '../common/QuestionMixin.js';
 import UploadAudio from '../common/UploadAudio.vue';
 import SoundRecord from '../common/SoundRecord.vue';
 import { GetStaticResources } from '@/api/app';
-import { changeOptionType, handleInputNumber } from '@/views/exercise_questions/data/common';
+import { changeOptionType, handleInputNumber, addTone } from '@/views/exercise_questions/data/common';
 import { getRandomNumber } from '@/utils/index';
 
 import {
@@ -192,6 +192,7 @@ export default {
           loadings: false,
         },
       ],
+      matically_pinyin_obj: {}, // 存放转成声调的拼音
     };
   },
   watch: {
@@ -210,18 +211,7 @@ export default {
       immediate: true,
     },
   },
-  mounted() {
-    // if (this.data.option_list.length > 3) {
-    //   let length = this.data.option_list.length - 3;
-    //   for (let i = 0; i < length; i++) {
-    //     let obj = {
-    //       loading: false,
-    //       loadings: false,
-    //     };
-    //     this.loading_list.push(obj);
-    //   }
-    // }
-  },
+  mounted() {},
   methods: {
     addOption() {
       this.data.option_list.push(getOption());
@@ -306,6 +296,13 @@ export default {
     },
     // 切割拼音
     handleSplitPy(item) {
+      let index = item.pinyin.search(/0|1|2|3|4/);
+      if (index > -1) {
+        this.handleItemPinyin(item.pinyin, item.mark);
+        setTimeout(() => {
+          item.pinyin = this.matically_pinyin_obj[item.mark];
+        }, 100);
+      }
       let pinyin_list = item.pinyin.trim().split(' ');
       item.pinyin_item_list = [];
       pinyin_list.forEach((itemp) => {
@@ -315,6 +312,49 @@ export default {
         item.pinyin_item_list.push(obj);
       });
     },
+    handleReplaceTone(value, mark) {
+      if (!value) return;
+      value.split(/\s+/).forEach((item) => {
+        this.handleValue(item);
+      });
+      this.matically_pinyin_obj[mark] = this.res_arr
+        .map((item) =>
+          item.map(({ number, con }) => (number && con ? addTone(Number(number), con) : number || con || '')),
+        )
+        .filter((item) => item.length > 0)
+        .join(' ');
+    },
+    handleValue(valItem) {
+      let numList = [];
+      if (/[A-Za-zü]+\d/g.test(valItem)) {
+        valItem.split('').forEach((item, i) => {
+          if (/\d/.test(item)) {
+            let con = valItem.replace(/\d/g, '');
+            numList.push({
+              index: i,
+              number: item,
+              con,
+              isTran: true,
+            });
+          }
+        });
+      } else {
+        numList = [];
+      }
+
+      this.res_arr.push(numList.length === 0 ? [{ con: valItem }] : numList);
+    },
+    handleItemPinyin(content, mark) {
+      let content_arr = content.trim().split(' ');
+      this.res_arr = [];
+      this.$set(this.matically_pinyin_obj, mark, []);
+      content_arr.forEach((items, index) => {
+        let items_trim = items.trim();
+        if (items_trim) {
+          this.handleReplaceTone(items_trim, mark);
+        }
+      });
+    },
     // 修改拼音
     changePinyin(item) {
       if (this.data.other.audio_generation_method === 'auto') {

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

@@ -270,7 +270,9 @@ export default {
     handleItemAnswer(item) {
       const index = this.data.answer.answer_list.findIndex((items) => items.mark === item.mark);
       let content = item.content.trim();
-      let content_arr = content.split(' ');
+      const regex = /[\u4e00-\u9fa5]/g;
+      item.content_hz = content.match(regex) ? content.match(regex).join('') : '';
+      let content_arr = content.replace(regex, '').trim().split(' ');
       let select_item = '';
       let content_preview = '';
       this.res_arr = [];
@@ -329,6 +331,9 @@ export default {
       if (arr.length > 0) {
         this.data.stem = arr[0];
         this.data.option_list = arr.slice(1).map((content) => getOption(content));
+        this.data.option_list.forEach((item) => {
+          this.handleItemAnswer(item);
+        });
       }
     },
   },

+ 74 - 6
src/views/exercise_questions/create/components/exercises/RepeatQuestion.vue

@@ -20,7 +20,13 @@
               {{ computedQuestionNumber(i, data.option_number_show_mode) }}
             </span>
             <div class="option-content">
-              <RichText v-model="item.content" :class="'repeat' + i" placeholder="输入内容" :inline="true" />
+              <RichText
+                v-model="item.content"
+                class="repeat-richtext"
+                placeholder="输入内容"
+                :inline="true"
+                @handleRichTextBlur="handleRichTextBlur"
+              />
             </div>
             <UploadAudio
               v-if="data.other.audio_generation_method === 'upload'"
@@ -115,7 +121,7 @@ import UploadAudio from '../common/UploadAudio.vue';
 import QuestionMixin from '../common/QuestionMixin.js';
 import SoundRecord from '../common/SoundRecord.vue';
 
-import { selectTypeList, changeOptionType } from '@/views/exercise_questions/data/common';
+import { selectTypeList, changeOptionType, addTone } from '@/views/exercise_questions/data/common';
 import { repeatData, getOption, audioGenerationMethodList } from '@/views/exercise_questions/data/repeat';
 import { GetStaticResources } from '@/api/app';
 
@@ -143,6 +149,7 @@ export default {
           loading: false,
         },
       ],
+      matically_pinyin_obj: {},
     };
   },
   watch: {
@@ -201,17 +208,78 @@ export default {
         })
         .catch(() => {});
     },
+    handleReplaceTone(value, mark) {
+      if (!value) return;
+      value.split(/\s+/).forEach((item) => {
+        this.handleValue(item);
+      });
+      this.matically_pinyin_obj[mark] = this.res_arr
+        .map((item) =>
+          item.map(({ number, con }) => (number && con ? addTone(Number(number), con) : number || con || '')),
+        )
+        .filter((item) => item.length > 0)
+        .join(' ');
+    },
+    handleValue(valItem) {
+      let numList = [];
+      if (/[A-Za-zü]+\d/g.test(valItem)) {
+        valItem.split('').forEach((item, i) => {
+          if (/\d/.test(item)) {
+            let con = valItem.replace(/\d/g, '');
+            numList.push({
+              index: i,
+              number: item,
+              con,
+              isTran: true,
+            });
+          }
+        });
+      } else {
+        numList = [];
+      }
+
+      this.res_arr.push(numList.length === 0 ? [{ con: valItem }] : numList);
+    },
+    handleItemPinyin(content, mark) {
+      let content_arr = content.trim().split(' ');
+      this.res_arr = [];
+      this.$set(this.matically_pinyin_obj, mark, []);
+      content_arr.forEach((items, index) => {
+        let items_trim = items.trim();
+        if (items_trim) {
+          this.handleReplaceTone(items_trim, mark);
+        }
+      });
+    },
+    // 转成带声调的拼音
+    handleRichTextBlur() {
+      let rich_rext_arr = document.getElementsByClassName('repeat-richtext');
+      if (rich_rext_arr) {
+        for (let i = 0; i < rich_rext_arr.length; i++) {
+          let content = rich_rext_arr[i].innerText;
+          let index = content.search(/0|1|2|3|4/);
+          if (index > -1) {
+            this.handleItemPinyin(content, this.data.option_list[i].mark);
+            setTimeout(() => {
+              document.getElementsByClassName('repeat-richtext')[i].innerText =
+                this.matically_pinyin_obj[this.data.option_list[i].mark];
+            }, 100);
+          }
+        }
+      }
+    },
     // 自动生成音频
     handleMatically(item, i) {
       if (
-        document.getElementsByClassName(`repeat${i}`) &&
-        document.getElementsByClassName(`repeat${i}`)[0] &&
-        document.getElementsByClassName(`repeat${i}`)[0].innerText
+        document.getElementsByClassName('repeat-richtext') &&
+        document.getElementsByClassName('repeat-richtext')[i] &&
+        document.getElementsByClassName('repeat-richtext')[i].innerText
       ) {
         this.loading_list[i].loading = true;
+
         let MethodName = 'tool-PinyinToVoiceFile';
         let data = {
-          pinyin: document.getElementsByClassName(`repeat${i}`)[0].innerText.trim().split(' ').join(','),
+          pinyin: document.getElementsByClassName('repeat-richtext')[i].innerText.trim().split(' ').join(','),
         };
         GetStaticResources(MethodName, data)
           .then((res) => {

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

@@ -148,7 +148,7 @@ import QuestionMixin from '../common/QuestionMixin.js';
 import UploadAudio from '../common/UploadAudio.vue';
 import SoundRecord from '../common/SoundRecord.vue';
 import { GetStaticResources, GetFileStoreInfo } from '@/api/app';
-import { changeOptionType, handleInputNumber } from '@/views/exercise_questions/data/common';
+import { changeOptionType, handleInputNumber, addTone } from '@/views/exercise_questions/data/common';
 import UploadDrag from '../common/UploadDrag.vue';
 
 import {
@@ -189,6 +189,7 @@ export default {
           loadings: false,
         },
       ],
+      matically_pinyin_obj: {},
     };
   },
   watch: {
@@ -339,11 +340,61 @@ export default {
     addSentence(item) {
       item.example_sentence.push('');
     },
+    handleReplaceTone(value, mark) {
+      if (!value) return;
+      value.split(/\s+/).forEach((item) => {
+        this.handleValue(item);
+      });
+      this.matically_pinyin_obj[mark] = this.res_arr
+        .map((item) =>
+          item.map(({ number, con }) => (number && con ? addTone(Number(number), con) : number || con || '')),
+        )
+        .filter((item) => item.length > 0)
+        .join(' ');
+    },
+    handleValue(valItem) {
+      let numList = [];
+      if (/[A-Za-zü]+\d/g.test(valItem)) {
+        valItem.split('').forEach((item, i) => {
+          if (/\d/.test(item)) {
+            let con = valItem.replace(/\d/g, '');
+            numList.push({
+              index: i,
+              number: item,
+              con,
+              isTran: true,
+            });
+          }
+        });
+      } else {
+        numList = [];
+      }
+
+      this.res_arr.push(numList.length === 0 ? [{ con: valItem }] : numList);
+    },
+    handleItemPinyin(content, mark) {
+      let content_arr = content.trim().split(' ');
+      this.res_arr = [];
+      this.$set(this.matically_pinyin_obj, mark, []);
+      content_arr.forEach((items, index) => {
+        let items_trim = items.trim();
+        if (items_trim) {
+          this.handleReplaceTone(items_trim, mark);
+        }
+      });
+    },
     // 修改拼音
     changePinyin(item) {
       if (this.data.other.audio_generation_method === 'auto') {
         item.audio_file_id = '';
       }
+      let index = item.pinyin.search(/0|1|2|3|4/);
+      if (index > -1) {
+        this.handleItemPinyin(item.pinyin, item.mark);
+        setTimeout(() => {
+          item.pinyin = this.matically_pinyin_obj[item.mark];
+        }, 100);
+      }
     },
     /**
      * 智能识别

+ 15 - 8
src/views/exercise_questions/preview/ChooseTonePreview.vue

@@ -13,11 +13,12 @@
     <div class="option-list">
       <li v-for="(item, i) in data.option_list" :key="i" :class="['option-item']">
         <span>{{ computeOptionMethods[data.option_number_show_mode](i) }} </span>
-        <AudioPlay v-if="item.audio_file_id" :file-id="item.audio_file_id" />
+        <AudioPlay v-if="item.audio_file_id" :file-id="item.audio_file_id" :showSlider="true" />
         <div
           class="option-content"
           :class="[isJudgingRightWrong ? (con_preview[i].all_right ? 'all-right' : 'has-error') : '']"
         >
+          <span class="items-hz" v-if="item.content_hz">{{ item.content_hz }}</span>
           <template v-if="data.property.answer_mode === 'select'">
             <span
               v-for="(itemc, indexc) in con_preview[i].item_con"
@@ -81,7 +82,8 @@
               con_preview[i].user_answer[con_preview[i].item_active_index].select_index_submit !==
                 con_preview[i].user_answer[con_preview[i].item_active_index].right_answer &&
               data.property.answer_mode === 'select') ||
-            (data.property.answer_mode === 'label' &&
+            (isJudgingRightWrong &&
+              data.property.answer_mode === 'label' &&
               con_preview[i].user_answer[con_preview[i].item_active_index].right_answer === value &&
               con_preview[i].user_answer[con_preview[i].item_active_index].right_index ===
                 con_preview[i].user_answer[con_preview[i].item_active_index].select_index &&
@@ -448,16 +450,12 @@ export default {
   min-height: 450px;
 
   .option-list {
-    display: flex;
-    flex-wrap: wrap;
-    row-gap: 16px;
-
     .option-item {
       display: flex;
       column-gap: 16px;
       align-items: center;
-      width: 45%;
-      margin-right: 5%;
+      width: 100%;
+      margin-bottom: 16px;
 
       .option-content {
         padding: 10px 22px;
@@ -476,9 +474,18 @@ export default {
         }
       }
 
+      .items-hz {
+        margin-right: 4px;
+        font-size: 16px;
+        font-weight: 500;
+        line-height: 24px;
+        color: #000;
+      }
+
       .item-con,
       .items-con {
         font-family: 'League';
+        font-weight: 500;
         color: #000;
         cursor: pointer;