dsy vor 1 Monat
Ursprung
Commit
24ac0c2df2
20 geänderte Dateien mit 296 neuen und 89 gelöschten Zeilen
  1. 6 1
      src/api/article.js
  2. 4 3
      src/courseware_preview/index.vue
  3. 6 1
      src/views/book/courseware/preview/common/PreviewOperation.vue
  4. 10 0
      src/views/book/courseware/preview/components/3d_model/3DModelPreview.vue
  5. 1 1
      src/views/book/courseware/preview/components/article/components/WordPhraseDetail.vue
  6. 5 1
      src/views/book/courseware/preview/components/article/index.vue
  7. 2 2
      src/views/book/courseware/preview/components/character/CharacterPreview.vue
  8. 2 2
      src/views/book/courseware/preview/components/character_base/components/FreewriteLettle.vue
  9. 2 2
      src/views/book/courseware/preview/components/character_base/components/Strockplayredline.vue
  10. 2 2
      src/views/book/courseware/preview/components/character_base/components/Strockred.vue
  11. 5 1
      src/views/book/courseware/preview/components/dialogue_article/index.vue
  12. 44 14
      src/views/book/courseware/preview/components/drawing/DrawingPreview.vue
  13. 21 7
      src/views/book/courseware/preview/components/fill/FillPreview.vue
  14. 10 0
      src/views/book/courseware/preview/components/image_text/ImageTextPreview.vue
  15. 4 4
      src/views/book/courseware/preview/components/newWord_template/components/Strockplayredline.vue
  16. 2 2
      src/views/book/courseware/preview/components/new_word/components/Strockplay.vue
  17. 1 1
      src/views/book/courseware/preview/components/new_word/components/WordPhraseDetail.vue
  18. 63 10
      src/views/book/courseware/preview/components/pinyin_base/PinyinBasePreview.vue
  19. 92 29
      src/views/book/courseware/preview/components/table/TablePreview.vue
  20. 14 6
      src/views/book/courseware/preview/components/voice_matrix/VoiceMatrixPreview.vue

+ 6 - 1
src/api/article.js

@@ -7,7 +7,7 @@ export function segSentences(data) {
 
 // 批量分词
 export function BatchSegContent(data) {
-  return http.post(`/OtherSysTool/GCLSHMToolsServer/api/nlp/chinese/text2word`, data);
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=tool-TextFC`, data);
 }
 
 // 音频预处理
@@ -47,3 +47,8 @@ export function getSysConfig() {
 export function getWordTimes(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=tool-AudioTextDot_OldFormat`, data);
 }
+
+// 词典
+export function toolDictionary(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=tool-Dictionary`, data);
+}

+ 4 - 3
src/courseware_preview/index.vue

@@ -174,12 +174,13 @@ export default {
       // { icon: 'setting', title: '设置', handle: '', param: {} },
     ];
     const token = getToken();
+    const courseware_id = getLocalStore('courseware_id');
     return {
       user_id: token?.gcls_sys_session_info?.user_id ?? '',
-      id: getLocalStore('courseware_id'),
+      id: courseware_id,
       projectId: getLocalStore('book_id') || '',
       task_id: getLocalStore('task_id') || '',
-      select_node: this.id,
+      select_node: courseware_id,
       courseware_info: {
         book_name: '',
         is_can_start_edit: 'false',
@@ -245,7 +246,7 @@ export default {
       lang: 'ZH',
       chinese: 'zh-Hans',
       unified_attrib: {},
-      curSelectId: this.id,
+      curSelectId: courseware_id,
       sidebarShow: true,
       project: {
         editor: '', // 作者

+ 6 - 1
src/views/book/courseware/preview/common/PreviewOperation.vue

@@ -4,7 +4,7 @@
     <div v-show="permissionControl.can_answer && isShowRetry" class="button retry" @click="retry()"></div>
     <!-- 判断对错 -->
     <div
-      v-show="permissionControl.can_judge_correct && isShowAnswer"
+      v-show="permissionControl.can_judge_correct && isShowAnswer && isShowJudgeCorrect"
       class="button correct"
       @click="judgeCorrect"
     ></div>
@@ -36,6 +36,11 @@ export default {
       type: Boolean,
       default: true,
     },
+    // 是否显示判断对错按钮(填空等题型无固定答案时应隐藏)
+    isShowJudgeCorrect: {
+      type: Boolean,
+      default: true,
+    },
   },
   data() {
     return {

+ 10 - 0
src/views/book/courseware/preview/components/3d_model/3DModelPreview.vue

@@ -448,6 +448,16 @@ export default {
         this.$message.warning('请等待模型加载完成后,再切换全屏');
         return;
       }
+
+      // 在切换全屏前,记录当前容器的宽高,以便在退出全屏时恢复
+      const container = this.$refs.three;
+      const width = container.clientWidth;
+      const height = container.clientHeight;
+      if (document.fullscreenElement === null) {
+        this.$refs.three.style.width = `${width}px`;
+        this.$refs.three.style.height = `${height}px`;
+      }
+
       toggleFullScreen(this.$refs.three);
       this.$nextTick(() => {
         setTimeout(() => {

+ 1 - 1
src/views/book/courseware/preview/components/article/components/WordPhraseDetail.vue

@@ -526,7 +526,7 @@ export default {
       this.loading1 = true;
       this.loading2 = true;
       this.loading3 = true;
-      let currentTreeID = this.getCoursewareId() || this.getSelectId();
+      let currentTreeID = this?.getCoursewareId() || this?.getSelectId();
       // 获取本课的 本教材的 本套的 的例句
       getCoursewareWordExampleSentenceList({
         courseware_id: currentTreeID, // 课件id

+ 5 - 1
src/views/book/courseware/preview/components/article/index.vue

@@ -435,7 +435,11 @@ export default {
     },
     submit() {
       let access_token =
-        this.tokenData && this.tokenData.gcls_sys_session_info ? this.tokenData.gcls_sys_session_info.access_token : '';
+        this.tokenData && this.tokenData.gcls_sys_session_info
+          ? this.tokenData.gcls_sys_session_info.access_token
+          : this.tokenData && this.tokenData.access_token
+            ? this.tokenDataaccess_token
+            : '';
       let loading = this.$loading({
         lock: true,
         text: 'Loading',

+ 2 - 2
src/views/book/courseware/preview/components/character/CharacterPreview.vue

@@ -756,8 +756,8 @@ export default {
       position: absolute;
       top: 0;
       left: 0;
-      width: 100%;
-      height: 100%;
+      width: 100% !important;
+      height: 100% !important;
       color: #dedede;
     }
 

+ 2 - 2
src/views/book/courseware/preview/components/character_base/components/FreewriteLettle.vue

@@ -387,8 +387,8 @@ export default {
       position: absolute;
       top: 30px;
       left: 16px;
-      width: 300px;
-      height: 300px;
+      width: 300px !important;
+      height: 300px !important;
       color: #dedede;
     }
 

+ 2 - 2
src/views/book/courseware/preview/components/character_base/components/Strockplayredline.vue

@@ -135,8 +135,8 @@ export default {
   position: absolute;
   top: 0;
   left: 0;
-  width: 100%;
-  height: 100%;
+  width: 100% !important;
+  height: 100% !important;
   color: #dedede;
 }
 

+ 2 - 2
src/views/book/courseware/preview/components/character_base/components/Strockred.vue

@@ -147,8 +147,8 @@ export default {
     position: absolute;
     top: 0;
     left: 0;
-    width: 100%;
-    height: 100%;
+    width: 100% !important;
+    height: 100% !important;
     color: #dedede;
   }
 

+ 5 - 1
src/views/book/courseware/preview/components/dialogue_article/index.vue

@@ -425,7 +425,11 @@ export default {
     },
     submit() {
       let access_token =
-        this.tokenData && this.tokenData.gcls_sys_session_info ? this.tokenData.gcls_sys_session_info.access_token : '';
+        this.tokenData && this.tokenData.gcls_sys_session_info
+          ? this.tokenData.gcls_sys_session_info.access_token
+          : this.tokenData && this.tokenData.access_token
+            ? this.tokenDataaccess_token
+            : '';
       let loading = this.$loading({
         lock: true,
         text: 'Loading',

+ 44 - 14
src/views/book/courseware/preview/components/drawing/DrawingPreview.vue

@@ -12,11 +12,7 @@
         border: '1px dotted #DCDFE6',
       }"
     >
-      <div
-        v-if="image_url"
-        class="img-set"
-        :style="{ top: `${data.imgData.top - 9}px`, left: `${data.imgData.left}px` }"
-      >
+      <div v-if="image_url" class="img-set" :style="{ top: `${data.imgData.top}px`, left: `${data.imgData.left}px` }">
         <img
           :src="image_url"
           draggable="false"
@@ -47,20 +43,46 @@
         :options="options"
       />
     </div>
-    <div v-if="!disabled" class="footer">
+    <!-- v-if="!disabled" -->
+    <div class="footer">
       <div class="pen">
         <!-- 选择颜色 -->
-        <el-color-picker v-model="lineColor" @change="changeColor" />
+        <el-color-picker v-model="lineColor" @change="changeColor" :disabled="disabled" />
         <!-- 清除一笔 -->
-        <img class="clean-btn" src="@/assets/drawing-back.png" @click="undo" />
+        <img
+          class="clean-btn"
+          src="@/assets/drawing-back.png"
+          @click="undo"
+          :style="{ cursor: disabled ? 'not-allowed' : 'pointer' }"
+        />
         <!-- 橡皮擦 -->
         <!-- <SvgIcon icon-class="eraser" size="24" @click="eraser" :style="{ color: isEraser ? 'green' : '#000' }" /> -->
         <!-- 清空画板 -->
-        <img class="clean-btn" src="@/assets/icon-clean.png" @click="clear" />
-        <img :src="penIndex == 0 ? thinpenActive : thinpen" class="pen-img" @click="selSize(0)" />
-        <img :src="penIndex == 1 ? thickpenActive : thickpen" class="pen-img" @click="selSize(1)" />
-        <img :src="penIndex == 2 ? thicskpenActive : thicskpen" class="pen-img" @click="selSize(2)" />
-        <div class="save" @click="save">保 存</div>
+        <img
+          class="clean-btn"
+          src="@/assets/icon-clean.png"
+          @click="clear"
+          :style="{ cursor: disabled ? 'not-allowed' : 'pointer' }"
+        />
+        <img
+          :src="penIndex == 0 ? thinpenActive : thinpen"
+          class="pen-img"
+          @click="selSize(0)"
+          :style="{ cursor: disabled ? 'not-allowed' : 'pointer' }"
+        />
+        <img
+          :src="penIndex == 1 ? thickpenActive : thickpen"
+          class="pen-img"
+          @click="selSize(1)"
+          :style="{ cursor: disabled ? 'not-allowed' : 'pointer' }"
+        />
+        <img
+          :src="penIndex == 2 ? thicskpenActive : thicskpen"
+          class="pen-img"
+          @click="selSize(2)"
+          :style="{ cursor: disabled ? 'not-allowed' : 'pointer' }"
+        />
+        <div class="save" @click="save" :style="{ cursor: disabled ? 'not-allowed' : 'pointer' }">保 存</div>
       </div>
     </div>
   </div>
@@ -119,16 +141,19 @@ export default {
   methods: {
     // 撤销
     undo() {
+      if (this.disabled) return;
       this.$refs.signaturePad.undoSignature();
       this.isEraser = false;
     },
     // 清除
     clear() {
+      if (this.disabled) return;
       this.$refs.signaturePad.clearSignature();
       this.isEraser = false;
     },
     // 保存
     save() {
+      if (this.disabled) return;
       this.signData = this.$refs.signaturePad.toData();
       const { data } = this.$refs.signaturePad.saveSignature();
       this.imgSrc = data;
@@ -170,6 +195,7 @@ export default {
     },
     // 调节画笔粗细大小
     selSize(index) {
+      if (this.disabled) return;
       this.penIndex = index;
       this.options = {
         penColor: this.lineColor,
@@ -231,7 +257,7 @@ export default {
 
 .img-box {
   position: relative;
-  margin: 20px auto;
+  margin: 0 auto 20px;
 }
 
 .img-set {
@@ -288,4 +314,8 @@ export default {
     cursor: pointer;
   }
 }
+
+.footer {
+  height: 38px;
+}
 </style>

+ 21 - 7
src/views/book/courseware/preview/components/fill/FillPreview.vue

@@ -10,7 +10,7 @@
         :file-id="data.audio_file_id"
       />
       <div class="fill-wrapper">
-        <p>
+        <div class="fill-content">
           <template v-for="(li, i) in modelEssay">
             <template v-if="li.type === 'text'">
               <PinyinText
@@ -94,7 +94,7 @@
               </template>
             </template>
           </template>
-        </p>
+        </div>
       </div>
       <SoundRecord
         v-if="isEnable(data.property.is_enable_voice_answer)"
@@ -111,7 +111,8 @@
       </div>
       <PreviewOperation
         :is-show-retry="isHasFill"
-        :is-show-answer="isHasFill"
+        :is-show-answer="isHasFill && isShowAnswerFill"
+        :is-show-judge-correct="isShowJudgeCorrect"
         @showAnswerAnalysis="showAnswerAnalysis"
         @judgeCorrect="judgeCorrect"
         @retry="retry"
@@ -131,7 +132,7 @@
       @closeAnswerAnalysis="closeAnswerAnalysis"
     >
       <div slot="right-answer" class="fill-wrapper">
-        <p>
+        <div class="fill-content">
           <template v-for="(li, i) in modelEssay">
             <template v-if="li.type === 'text'">
               <PinyinText
@@ -151,7 +152,7 @@
               </span>
             </template>
           </template>
-        </p>
+        </div>
       </div>
     </AnswerAnalysis>
   </div>
@@ -201,6 +202,18 @@ export default {
     isHorizontal() {
       return this.data.property.arrange_type === arrangeTypeList[0].value;
     },
+    // 是否有固定答案(data.answer.answer_list 中存在任意一项 value 非空)
+    hasFixedAnswer() {
+      return (this.data.answer?.answer_list || []).some((item) => item?.value);
+    },
+    // 是否显示判断对错按钮:有固定答案时才显示
+    isShowJudgeCorrect() {
+      return this.hasFixedAnswer;
+    },
+    // 是否显示查看答案按钮:有固定答案或参考答案(answer_list)有数据时显示
+    isShowAnswerFill() {
+      return this.hasFixedAnswer || (this.data.answer_list || []).length > 0;
+    },
   },
   watch: {
     'data.model_essay': {
@@ -506,11 +519,12 @@ export default {
     font-size: 16pt;
 
     :deep .pinyin-area .rich-text-container,
-    :deep .pinyin-area .pinyin-paragraph {
+    :deep .pinyin-area .pinyin-paragraph,
+    :deep .pinyin-area .pinyin-sentence {
       display: inline;
     }
 
-    p {
+    .fill-content {
       margin: 0;
     }
 

+ 10 - 0
src/views/book/courseware/preview/components/image_text/ImageTextPreview.vue

@@ -54,6 +54,11 @@
           :disabled="disabled"
           type="textarea"
           style="height: 100%"
+          :style="{
+            fontSize: data.unified_attrib && data.unified_attrib.font_size ? data.unified_attrib.font_size : '',
+            '--input-color':
+              data.unified_attrib && data.unified_attrib.text_color ? data.unified_attrib.text_color : '#1d2129',
+          }"
           :placeholder="convertText('请输入')"
         />
       </div>
@@ -374,7 +379,12 @@ export default {
     height: 100%;
     padding: 5px;
     font-family: 'League', '楷体';
+
+    // font-size: 16px;
     line-height: 1;
+
+    // color: #000;
+    color: var(--input-color);
     text-align: center;
     resize: none;
     background: transparent;

+ 4 - 4
src/views/book/courseware/preview/components/newWord_template/components/Strockplayredline.vue

@@ -218,8 +218,8 @@ export default {
     top: 0;
     left: 0;
     z-index: 0;
-    width: 100%;
-    height: 100%;
+    width: 100% !important;
+    height: 100% !important;
     color: #dedede;
   }
 
@@ -227,8 +227,8 @@ export default {
     position: absolute;
     top: 0;
     left: 0;
-    width: 100%;
-    height: 100%;
+    width: 100% !important;
+    height: 100% !important;
     stroke: rgba(212, 112, 100, 20%);
   }
 }

+ 2 - 2
src/views/book/courseware/preview/components/new_word/components/Strockplay.vue

@@ -143,8 +143,8 @@ export default {
   top: 0;
   left: 0;
   z-index: -1;
-  width: 100%;
-  height: 100%;
+  width: 100% !important;
+  height: 100% !important;
 }
 
 .animate-butto {

+ 1 - 1
src/views/book/courseware/preview/components/new_word/components/WordPhraseDetail.vue

@@ -417,7 +417,7 @@ export default {
       this.loading1 = true;
       this.loading2 = true;
       this.loading3 = true;
-      let currentTreeID = this.getCoursewareId() || this.getSelectId();
+      let currentTreeID = this?.getCoursewareId() || this?.getSelectId();
       // 获取本课的 本教材的 本套的 的例句
       getCoursewareWordExampleSentenceList({
         courseware_id: currentTreeID, // 课件id

+ 63 - 10
src/views/book/courseware/preview/components/pinyin_base/PinyinBasePreview.vue

@@ -24,7 +24,7 @@
                 : '',
             ]"
             :style="{
-              fontSize: data.unified_attrib && data.unified_attrib.font_size ? data.unified_attrib.font_size : '',
+              fontSize: data.unified_attrib && data.unified_attrib.pinyin_size ? data.unified_attrib.pinyin_size : '',
             }"
           >
             <span v-if="data.content_hz" class="items-hz">{{ convertText(data.content_hz) }}</span>
@@ -34,12 +34,26 @@
                 v-for="(itemc, indexc) in con_preview"
                 :key="indexc"
                 :class="['item-con', isJudgingRightWrong && itemc.type === 'input' ? itemc.is_right : '']"
+                :style="{
+                  color:
+                    data.unified_attrib && data.unified_attrib.text_color ? data.unified_attrib.text_color : '#000',
+                }"
               >
                 <el-input
                   v-if="itemc.type === 'input'"
                   v-model="itemc.con"
                   :disabled="disabled"
-                  :style="[{ width: Math.max(20, itemc.con.length * 10) + 'px' }]"
+                  :style="[
+                    {
+                      width: Math.max(20, itemc.con.length * 10) + 'px',
+                      fontSize:
+                        data.unified_attrib && data.unified_attrib.pinyin_size ? data.unified_attrib.pinyin_size : '',
+                      '--input-color':
+                        data.unified_attrib && data.unified_attrib.text_color
+                          ? data.unified_attrib.text_color
+                          : '#1d2129',
+                    },
+                  ]"
                   class="item-input"
                   @blur="onInputChange(itemc)"
                 />
@@ -48,7 +62,15 @@
             </template>
             <template v-else-if="data.property.answer_mode === 'select'">
               <template v-if="data.property.fun_type === 'show'">
-                <span v-for="(itemc, indexc) in data.matically_pinyin_str[data.mark]" :key="indexc" class="items-box">
+                <span
+                  v-for="(itemc, indexc) in data.matically_pinyin_str[data.mark]"
+                  :key="indexc"
+                  class="items-box"
+                  :style="{
+                    color:
+                      data.unified_attrib && data.unified_attrib.text_color ? data.unified_attrib.text_color : '#000',
+                  }"
+                >
                   <span v-for="(itemi, indexi) in itemc" :key="indexi" :class="['items-con']">{{ itemi }}</span>
                 </span>
               </template>
@@ -66,6 +88,10 @@
                       : '',
                     data.property.fun_type === 'show' ? 'item-con-in' : '',
                   ]"
+                  :style="{
+                    color:
+                      data.unified_attrib && data.unified_attrib.text_color ? data.unified_attrib.text_color : '#000',
+                  }"
                   @click="
                     if (data.property.fun_type === 'show') return;
                     con_preview[0].item_active_index = indexc;
@@ -82,6 +108,10 @@
                   v-for="(itemi, indexi) in itemc"
                   :key="indexi"
                   :class="['items-con', active_index_str === 0 + '-' + indexc + '-' + indexi ? 'active' : '']"
+                  :style="{
+                    color:
+                      data.unified_attrib && data.unified_attrib.text_color ? data.unified_attrib.text_color : '#000',
+                  }"
                   @click="handleSelectItemTone(0, indexc, indexi, con_preview[0].item_con_yuan[indexc][indexi])"
                   >{{ itemi }}</span
                 >
@@ -128,7 +158,7 @@
             ]"
             @click="chooseTone(con_preview[0], value)"
           >
-            <SvgIcon :icon-class="img" />
+            <SvgIcon :icon-class="img" size="16" />
           </span>
         </div>
         <template v-else-if="data.property.fun_type !== 'mark' && isEnable(data.property.is_enable_voice_answer)">
@@ -181,7 +211,9 @@
               <div
                 class="option-content"
                 :style="{
-                  fontSize: data.unified_attrib && data.unified_attrib.font_size ? data.unified_attrib.font_size : '',
+                  fontSize:
+                    data.unified_attrib && data.unified_attrib.pinyin_size ? data.unified_attrib.pinyin_size : '',
+                  color: data.unified_attrib && data.unified_attrib.text_color ? data.unified_attrib.text_color : '',
                 }"
               >
                 <span v-if="data.content_hz" class="items-hz">{{ convertText(data.content_hz) }}</span>
@@ -196,7 +228,19 @@
                       v-if="itemc.type === 'input'"
                       v-model="itemc.answer"
                       :disabled="true"
-                      :style="[{ width: Math.max(20, itemc.answer.length * 10) + 'px' }]"
+                      :style="[
+                        {
+                          width: Math.max(20, itemc.answer.length * 10) + 'px',
+                          fontSize:
+                            data.unified_attrib && data.unified_attrib.pinyin_size
+                              ? data.unified_attrib.pinyin_size
+                              : '',
+                          '--input-color':
+                            data.unified_attrib && data.unified_attrib.text_color
+                              ? data.unified_attrib.text_color
+                              : '#1d2129',
+                        },
+                      ]"
                       class="item-input"
                     />
                     <span v-else>{{ itemc.con }}</span>
@@ -242,6 +286,12 @@
                       v-for="(itemi, indexi) in itemc"
                       :key="indexi"
                       :class="['items-con', active_index_str_right === 0 + '-' + indexc + '-' + indexi ? 'active' : '']"
+                      :style="{
+                        color:
+                          data.unified_attrib && data.unified_attrib.text_color
+                            ? data.unified_attrib.text_color
+                            : '#000',
+                      }"
                       @click="
                         handleSelectItemTone(0, indexc, indexi, con_preview_right[0].item_con_yuan[indexc][indexi])
                       "
@@ -867,7 +917,7 @@ export default {
     font-family: 'League';
 
     // font-weight: 500;
-    color: #000;
+    // color: #000;
     cursor: pointer;
 
     &.right {
@@ -934,12 +984,15 @@ export default {
     :deep .el-input__inner {
       padding: 0;
       font-family: 'League';
-      font-size: 16px;
-      color: #1d2129;
+
+      // color: '#000';
+      color: var(--input-color);
+
+      // font-size: 16px;
       text-align: center;
       background-color: transparent;
       border-width: 0;
-      border-bottom: 1px solid #1d2129;
+      border-bottom: 1px solid #000;
       border-radius: 0;
     }
   }

+ 92 - 29
src/views/book/courseware/preview/components/table/TablePreview.vue

@@ -51,7 +51,8 @@
                 }"
               >
                 <div class="cell-wrap">
-                  <div class="cell-wrap-content" :style="[tdStyle, computedRichStyle(col.content)]">
+                  <!-- , computedRichStyle(col.content) -->
+                  <div class="cell-wrap-content" :style="[tdStyle(col.content)]">
                     <template v-if="isEnable(data.property.view_pinyin)">
                       <template v-if="col.rich_text_list">
                         <div
@@ -86,7 +87,9 @@
                                     'align-items': getWordAlignItems(word, item),
                                   }"
                                 >
-                                  <span class="pinyin" :style="getPinyinStyle(word)"> {{ word.pinyin }}</span>
+                                  <span class="pinyin" :style="getPinyinStyle(word)">
+                                    {{ word.pinyin.trim() ? word.pinyin : '&nbsp;' }}</span
+                                  >
                                   <span class="py-char" :style="getCharStyle(word, item)">{{
                                     convertText(word.text)
                                   }}</span>
@@ -100,7 +103,7 @@
                                     v-model="word.value"
                                     :disabled="disabled"
                                     :class="[...computedAnswerClass(word, i, j)]"
-                                    :style="[{ width: Math.max(40, word.value.length * 21.3) + 'px' }]"
+                                    :style="getInputStyle(item.styleObj, word)"
                                   />
                                 </template>
                                 <template v-else-if="data.property.fill_type === fillTypeList[1].value">
@@ -121,7 +124,7 @@
                                       v-model="word.value"
                                       :readonly="true"
                                       :class="[...computedAnswerClass(word, i, j)]"
-                                      :style="[{ width: Math.max(40, word.value.length * 21.3) + 'px' }]"
+                                      :style="getInputStyle(item.styleObj, word)"
                                     />
                                   </el-popover>
                                 </template>
@@ -237,7 +240,17 @@
                               v-model="item.value"
                               :disabled="disabled"
                               :class="[...computedAnswerClass(item, i, j)]"
-                              :style="[{ width: Math.max(40, item.value.length * 21.3) + 'px' }]"
+                              :style="[
+                                {
+                                  width: Math.max(40, item.value.length * 21.3) + 'px',
+                                  fontSize:
+                                    data.unified_attrib && data.unified_attrib.font_size
+                                      ? data.unified_attrib.font_size
+                                      : '',
+                                  fontFamily:
+                                    data.unified_attrib && data.unified_attrib.font ? data.unified_attrib.font : '',
+                                },
+                              ]"
                             />
                           </template>
                           <template v-else-if="data.property.fill_type === fillTypeList[1].value">
@@ -303,7 +316,8 @@
                     @click="toggle(col)"
                   ></i>
                 </div>
-                <span v-if="showLang" class="multilingual" :style="[tdStyle, computedRichStyle(col.content)]">
+                <!-- , computedRichStyle(col.content) -->
+                <span v-if="showLang" class="multilingual" :style="[tdStyle(col.content)]">
                   {{
                     multilingualTextList[getLang()] &&
                     multilingualTextList[getLang()][i] &&
@@ -387,7 +401,8 @@
                   }"
                 >
                   <div class="cell-wrap">
-                    <div class="cell-wrap-content" :style="[tdStyle, computedRichStyle(col.content)]">
+                    <!-- , computedRichStyle(col.content) -->
+                    <div class="cell-wrap-content" :style="[tdStyle(col.content)]">
                       <template v-if="isEnable(data.property.view_pinyin)">
                         <template v-if="col.rich_text_list">
                           <div
@@ -422,7 +437,9 @@
                                       'align-items': getWordAlignItems(word, item),
                                     }"
                                   >
-                                    <span class="pinyin" :style="getPinyinStyle(word)"> {{ word.pinyin }}</span>
+                                    <span class="pinyin" :style="getPinyinStyle(word)">{{
+                                      word.pinyin.trim() ? word.pinyin : '&nbsp;'
+                                    }}</span>
                                     <span class="py-char" :style="getCharStyle(word, item)">{{
                                       convertText(word.text)
                                     }}</span>
@@ -586,7 +603,8 @@
                       style="display: block; width: 18px; height: 18px; border: 1px solid #30a47d"
                     ></i>
                   </div>
-                  <span v-if="showLang" class="multilingual" :style="[tdStyle, computedRichStyle(col.content)]">
+                  <!-- , computedRichStyle(col.content) -->
+                  <span v-if="showLang" class="multilingual" :style="[tdStyle(col.content)]">
                     {{
                       multilingualTextList[getLang()] &&
                       multilingualTextList[getLang()][i] &&
@@ -639,25 +657,51 @@ export default {
   },
   computed: {
     tdStyle() {
-      let obj = {};
-      // if (this.data.mode === 'short') {
-      let styles = this.data.styles;
-      obj = {
-        fontFamily: styles.fontFamily,
-        fontSize: styles.fontSize,
-        color: styles.fontColor,
-        textDecoration: styles.isUnderline ? 'underline' : styles.isStrikethrough ? 'line-through' : '',
-        fontWeight: styles.isBold ? 'bold' : '',
-        fontStyle: styles.isItalic ? 'italic' : '',
-        justifyContent: styles.textAlign === 'MIDDLE' ? 'center' : styles.textAlign === 'RIGHT' ? 'end' : 'left',
-        textAlign: styles.textAlign === 'MIDDLE' ? 'center' : styles.textAlign === 'RIGHT' ? 'end' : 'left',
+      return function (content) {
+        if (!content) return {};
+
+        let styleObject = {};
+        // 提取首个标签的 style 样式属性
+        let styleMatch = content.match(/<\w+\s+[^>]*style=["']([^"']*)["'][^>]*>/i);
+        if (styleMatch && styleMatch[1]) {
+          let styleString = styleMatch[1];
+          let styleArray = styleString.split(';').filter((s) => s.trim() !== '');
+          styleArray.forEach((style) => {
+            let [key, value] = style.split(':');
+            if (key && value) {
+              styleObject[key.trim()] = value.trim();
+            }
+          });
+          if (styleObject.hasOwnProperty('text-align')) {
+            if (styleObject['text-align'] === 'center') {
+              this.$set(styleObject, 'justify-content', 'center');
+            } else if (styleObject['text-align'] === 'right') {
+              this.$set(styleObject, 'justify-content', 'end');
+            } else {
+              this.$set(styleObject, 'justify-content', 'left');
+            }
+          }
+        }
+        let obj = {};
+        // if (this.data.mode === 'short') {
+        let styles = this.data.styles;
+        obj = {
+          fontFamily: styles.fontFamily,
+          fontSize: styles.fontSize,
+          color: styles.fontColor,
+          textDecoration: styles.isUnderline ? 'underline' : styles.isStrikethrough ? 'line-through' : '',
+          fontWeight: styles.isBold ? 'bold' : '',
+          fontStyle: styles.isItalic ? 'italic' : '',
+          // justifyContent: styles.textAlign === 'MIDDLE' ? 'center' : styles.textAlign === 'RIGHT' ? 'end' : 'left',
+          // textAlign: styles.textAlign === 'MIDDLE' ? 'center' : styles.textAlign === 'RIGHT' ? 'end' : 'left',
+        };
+        // } else {
+        //   obj = {
+        //     width: '100%',
+        //   };
+        // }
+        return { ...obj, ...styleObject };
       };
-      // } else {
-      //   obj = {
-      //     width: '100%',
-      //   };
-      // }
-      return obj;
     },
   },
   watch: {
@@ -848,7 +892,13 @@ export default {
         .map((tagItem) => tagItem.style)
         .join(';');
 
-      const styleObj = combinedStyle ? this.parseStyleToObject(combinedStyle) : null;
+      const styleObj = combinedStyle
+        ? this.parseStyleToObject(combinedStyle)
+        : {
+            fontSize:
+              this.data.unified_attrib && this.data.unified_attrib.font_size ? this.data.unified_attrib.font_size : '',
+            fontFamily: this.data.unified_attrib && this.data.unified_attrib.font ? this.data.unified_attrib.font : '',
+          };
 
       return {
         type: 'text',
@@ -866,7 +916,8 @@ export default {
 
       // 固定使用 League 字体
       styles['font-family'] = 'League';
-
+      styles['font-size'] =
+        this.data.unified_attrib && this.data.unified_attrib.pinyin_size ? this.data.unified_attrib.pinyin_size : '';
       // 只继承字号和颜色
       if (item.activeTextStyle) {
         if (item.activeTextStyle.fontSize) {
@@ -918,6 +969,16 @@ export default {
 
       return baseStyle;
     },
+    // input的高度
+    getInputStyle(styles, words) {
+      const baseStyle = {};
+      baseStyle['font-size'] = styles ? styles.fontSize : '16px';
+      baseStyle['--input-font-family'] = styles ? styles.fontFamily : '';
+      baseStyle['width'] = Math.max(40, words.value.length * 21.3) + 'px';
+      baseStyle['height'] = styles && styles.fontSize ? styles.fontSize.replace('pt', '') * 1.5 + 'pt' : '24px';
+
+      return baseStyle;
+    },
     // 如果汉字有字间距,拼音与汉字左对齐,如果没有则居中对齐
     getWordAlignItems(word, block) {
       const letterSpacing = block.styleObj?.letterSpacing;
@@ -1411,6 +1472,7 @@ $border-color: #e6e6e6;
 
         :deep input.el-input__inner {
           padding: 0;
+          font-family: var(--input-font-family);
 
           // font-size: 16pt;
           color: $font-color;
@@ -1484,6 +1546,7 @@ $border-color: #e6e6e6;
 
       span {
         display: inline-flex;
+        flex-wrap: wrap;
       }
 
       .pinyin-sentence {

+ 14 - 6
src/views/book/courseware/preview/components/voice_matrix/VoiceMatrixPreview.vue

@@ -114,6 +114,7 @@
                     ? 'read'
                     : '',
                 ]"
+                :style="{ ...getBodyStyles() }"
                 @mouseenter="matrixCellMouseenter(i, j, column.type)"
               >
                 <!-- 文本 -->
@@ -317,11 +318,7 @@ export default {
     },
   },
   created() {
-    Bus.$on('audioPause', (id) => {
-      if (this.cid === id) return;
-      if (this.$refs.luyin?.microphoneStatus) this.$refs.luyin.microphone();
-      this.handleParentPlay();
-    });
+    Bus.$on('audioPause', this.onAudioPause);
     if (!('record_list' in this.answer)) {
       this.$set(this.answer, 'record_list', []);
     }
@@ -330,9 +327,15 @@ export default {
     document.querySelector('body').addEventListener('click', this.restoreAudioStatus);
   },
   beforeDestroy() {
+    Bus.$off('audioPause', this.onAudioPause);
     document.querySelector('body').removeEventListener('click', this.restoreAudioStatus);
   },
   methods: {
+    onAudioPause(id) {
+      if (this.cid === id) return;
+      if (this.$refs.luyin?.microphoneStatus) this.$refs.luyin.microphone();
+      this.handleParentPlay();
+    },
     handleWav(data) {
       this.data.record_list = data;
     },
@@ -552,7 +555,7 @@ export default {
         }
         return lrc_data;
       }
-      return false;
+      return null;
     },
 
     sentPause(isRecord) {
@@ -653,6 +656,11 @@ $border-color: #e6e6e6;
 .voice-matrix-preview {
   @include preview-base;
 
+  .rich-text {
+    font-family: inherit;
+    font-size: inherit;
+  }
+
   .main {
     color: #262626;