dsy 2 veckor sedan
förälder
incheckning
7ed7e745a0
21 ändrade filer med 247 tillägg och 153 borttagningar
  1. 1 1
      package.json
  2. 40 3
      src/components/PinyinText.vue
  3. 5 3
      src/styles/font/font.css
  4. BIN
      src/styles/font/pinyin.ttf
  5. 10 10
      src/views/book/courseware/preview/components/article/Practicechs.vue
  6. 52 14
      src/views/book/courseware/preview/components/article/Voicefullscreen.vue
  7. 2 2
      src/views/book/courseware/preview/components/article/components/WordPhraseDetail.vue
  8. 12 12
      src/views/book/courseware/preview/components/article/index.vue
  9. 1 0
      src/views/book/courseware/preview/components/common/PreviewMixin.js
  10. 10 10
      src/views/book/courseware/preview/components/dialogue_article/Practicechs.vue
  11. 10 11
      src/views/book/courseware/preview/components/dialogue_article/index.vue
  12. 22 5
      src/views/book/courseware/preview/components/drawing/DrawingPreview.vue
  13. 32 47
      src/views/book/courseware/preview/components/fill/FillPreview.vue
  14. 35 29
      src/views/book/courseware/preview/components/h5_games/H5GamesPreview.vue
  15. 1 1
      src/views/book/courseware/preview/components/image_text/ImageTextPreview.vue
  16. 2 1
      src/views/book/courseware/preview/components/new_word/NewWordPreview.vue
  17. 2 2
      src/views/book/courseware/preview/components/new_word/components/WordPhraseDetail.vue
  18. 1 0
      src/views/book/courseware/preview/components/rich_text/RichTextPreview.vue
  19. 5 0
      src/views/book/courseware/preview/components/table/TablePreview.vue
  20. 2 1
      src/views/book/courseware/preview/components/video_interaction/ExercisePreview.vue
  21. 2 1
      src/views/book/courseware/preview/components/video_interaction/Report.vue

+ 1 - 1
package.json

@@ -1,5 +1,5 @@
 {
-  "name": "eep_page",
+  "name": "eep_book_preview",
   "version": "0.0.1",
   "private": true,
   "main": "main.js",

+ 40 - 3
src/components/PinyinText.vue

@@ -33,7 +33,9 @@
                   'align-items': getWordAlignItems(word, block),
                 }"
               >
-                <span class="pinyin" :style="getPinyinStyle(word)"> {{ getPinyinText(word) }}</span>
+                <span v-if="shouldShowWordPinyin(word)" class="pinyin" :style="getPinyinStyle(word, block)">
+                  {{ getPinyinText(word) }}
+                </span>
                 <span class="py-char" :style="getCharStyle(word, block)">{{ convertText(word.text) }}</span>
               </span>
               <template v-else>
@@ -44,7 +46,9 @@
                       'align-items': getWordAlignItems(word, block),
                     }"
                   >
-                    <span class="pinyin" :style="getPinyinStyle(word)"> {{ getCharPinyin(word, cIndex) }}</span>
+                    <span v-if="shouldShowWordPinyin(word)" class="pinyin" :style="getPinyinStyle(word, block)">
+                      {{ getCharPinyin(word, cIndex) }}
+                    </span>
                     <span class="py-char" :style="getCharStyle(word, block, cIndex)">{{ convertText(char) }}</span>
                   </span>
                 </span>
@@ -240,6 +244,10 @@ export default {
       type: Object,
       default: () => ({}),
     },
+    isShowPinyin: {
+      type: Boolean,
+      default: true,
+    },
   },
   data() {
     return {
@@ -606,6 +614,7 @@ export default {
         oldIndex,
         paragraphIndex,
         styleObj,
+        rawStyleText: combinedStyle,
         hasEmphasisDot,
       };
     },
@@ -619,12 +628,26 @@ export default {
       return styleObj;
     },
 
+    getBlockColor(block) {
+      const colorFromObject = block?.styleObj?.color;
+      if (colorFromObject) return colorFromObject;
+
+      const styleText = block?.rawStyleText || '';
+      const colorMatch = styleText.match(/(?:^|;)\s*color\s*:\s*([^;]+)/i);
+      return colorMatch ? colorMatch[1].trim() : '';
+    },
+
     // 获取单个字符的样式(包括着重点)
     getCharStyle(word, block) {
       const baseStyle = { ...word.activeTextStyle };
       baseStyle['font-size'] = baseStyle.fontSize;
       baseStyle['font-family'] = baseStyle.fontFamily;
 
+      const blockColor = this.getBlockColor(block);
+      if (blockColor) {
+        baseStyle.color = `${blockColor} !important`;
+      }
+
       if (this.isAllSetting) {
         baseStyle['font-size'] = this.fontSize;
         baseStyle['font-family'] = this.fontFamily;
@@ -651,6 +674,9 @@ export default {
     getPinyinText(item) {
       return this.checkShowPinyin(item.showPinyin) ? item.pinyin.replace(/\s+/g, '') : '\u200B';
     },
+    shouldShowWordPinyin(item) {
+      return this.checkShowPinyin(item && item.showPinyin);
+    },
     // 拼音与汉字一一对应
     getCharPinyin(word, charIndex) {
       if (!this.checkShowPinyin(word.showPinyin)) {
@@ -673,7 +699,7 @@ export default {
       return 'center';
     },
     // 拼音固定为拼音字体,跟随汉字的字号、颜色、粗细的样式
-    getPinyinStyle(item) {
+    getPinyinStyle(item, block) {
       const styles = {};
 
       // 固定使用 League 字体
@@ -689,6 +715,11 @@ export default {
         }
       }
 
+      const blockColor = this.getBlockColor(block);
+      if (blockColor) {
+        styles['color'] = `${blockColor} !important`;
+      }
+
       // 如果设置了全局字号,优先使用全局字号
       if (this.isAllSetting && this.fontSize) {
         styles['font-size'] = this.fontSize;
@@ -732,11 +763,17 @@ export default {
     },
     // 如段落有拼音,则返回true ,否则不显示拼音行
     hasPinyinInParagraph(paragraphIndex) {
+      if (!this.isShowPinyin) {
+        return false;
+      }
       const paragraph = this.paragraphList[paragraphIndex];
       return paragraph.some((sentence) => sentence.some((item) => this.checkShowPinyin(item.showPinyin)));
     },
     // 兼容历史数据,没有 showPinyin 字段的,则默认为 true
     checkShowPinyin(showPinyin) {
+      if (!this.isShowPinyin) {
+        return false;
+      }
       if (showPinyin === undefined || showPinyin === null) {
         return true;
       }

+ 5 - 3
src/styles/font/font.css

@@ -1,14 +1,16 @@
 @font-face {
   font-family: 'PINYIN-B';
-  src: url('GBPinyinok-B-new.otf');
+  src: url('pinyin.ttf');
 }
 
 @font-face {
   font-family: 'League';
-  src: url('GBPinyinok-B-new.otf');
+
+  /* src: url('GBPinyinok-B-new.otf'); */
+  src: url('pinyin.ttf');
 }
 
 @font-face {
   font-family: 'Sans-GBNPC';
-  src: url('GBPinyinok-B-new.otf');
+  src: url('pinyin.ttf');
 }

BIN
src/styles/font/pinyin.ttf


+ 10 - 10
src/views/book/courseware/preview/components/article/Practicechs.vue

@@ -862,7 +862,7 @@ export default {
       this.pauseAudio();
       this.pauseVideo();
       this.isFull = true;
-      this.goFullscreen();
+      // this.goFullscreen();
     },
     goFullscreen() {
       let id = `screen-${this.mathNum}`;
@@ -879,15 +879,15 @@ export default {
     },
     exitFullscreen() {
       this.isFull = false;
-      if (document.exitFullscreen) {
-        document.exitFullscreen();
-      } else if (document.msExitFullscreen) {
-        document.msExitFullscreen();
-      } else if (document.mozCancelFullScreen) {
-        document.mozCancelFullScreen();
-      } else if (document.webkitExitFullscreen) {
-        document.webkitExitFullscreen();
-      }
+      // if (document.exitFullscreen) {
+      //   document.exitFullscreen();
+      // } else if (document.msExitFullscreen) {
+      //   document.msExitFullscreen();
+      // } else if (document.mozCancelFullScreen) {
+      //   document.mozCancelFullScreen();
+      // } else if (document.webkitExitFullscreen) {
+      //   document.webkitExitFullscreen();
+      // }
     },
     changeIsFull() {
       this.isFull = false;

+ 52 - 14
src/views/book/courseware/preview/components/article/Voicefullscreen.vue

@@ -1,8 +1,14 @@
 <!--  -->
 <template>
-  <div v-if="sentList" :class="['voicefull', bgIndex == 0 ? 'bg1' : 'bg2']">
+  <div
+    v-if="sentList"
+    :class="['voicefull', bgIndex == 0 ? 'bg1' : 'bg2', isLandscape ? 'voicefull-scape' : '']"
+    :style="{
+      paddingRight: isLandscape ? '100px' : '',
+    }"
+  >
     <div class="voicefull-bottom" @mouseover="setTopShow(true)" @mouseleave="setTopShow(false)">
-      <div :class="[isTopShow ? 'voicefull-bottom-show' : 'voicefull-bottom-hidden']">
+      <div :class="[isTopShow || isPhone ? 'voicefull-bottom-show' : 'voicefull-bottom-hidden']">
         <div :class="['bottom-left', TaskModel == 'ANSWER' ? 'bottom-left-margin' : '']"></div>
         <div :class="['page-count', bgIndex == 0 ? 'page-count-white' : 'page-count-green']">
           {{ curSentIndex + 1 }}/{{ sentList.length }}
@@ -467,7 +473,7 @@
       </div>
     </div>
     <div class="voicefull-top" @mouseover="setBottomShow(true)" @mouseleave="setBottomShow(false)">
-      <div :class="[isBottomShow ? 'voicefull-top-show' : 'voicefull-top-hidden']">
+      <div :class="[isBottomShow || isPhone ? 'voicefull-top-show' : 'voicefull-top-hidden']">
         <div class="top-left">
           <div :class="['select-bg', bgIndex == 1 ? 'select-bg-blue' : '']">
             <div :class="['bg-green-box', bgIndex == 1 ? 'active' : '']">
@@ -722,6 +728,8 @@ export default {
       isNoteShow: false,
       oldNoteNum: '',
       windowWidth: window.innerWidth,
+      isLandscape: window.innerWidth < window.innerHeight, // 手机是否为竖屏
+      isPhone: false,
     };
   },
   computed: {
@@ -801,7 +809,10 @@ export default {
     },
   },
   // 生命周期 - 创建完成(可以访问当前this实例)
-  created() {},
+  created() {
+    const regExp = /Android|webOS|iPhone|BlackBerry|IEMobile|Opera Mini/i;
+    this.isPhone = regExp.test(navigator.userAgent) && window.innerWidth < 860;
+  },
   // 生命周期 - 挂载完成(可以访问DOM元素)
   mounted() {
     window.addEventListener('resize', this.getScreenHeight);
@@ -820,6 +831,8 @@ export default {
         } else if (e.keyCode === 40) {
           this.nextSentence();
         } else if (e.keyCode === 13) {
+        } else if (event.key === 'Escape' || event.key === 'Esc' || event.keyCode === 27) {
+          this.exitFullScreen();
         }
       }
     });
@@ -1405,12 +1418,36 @@ export default {
 
 <style lang="scss" scoped>
 .voicefull {
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 99999;
   display: flex;
   flex-direction: column;
   width: 100%;
   height: 100vh;
+  padding-top: env(safe-area-inset-top);
+  padding-bottom: env(safe-area-inset-bottom);
   overflow: hidden;
 
+  &-scape {
+    display: flex;
+    flex-direction: column;
+    width: 100vh;
+    height: 100vw;
+    margin-left: 100vw;
+    transform: rotate(90deg);
+    transform-origin: 0% 0%;
+
+    .vc-main {
+      max-height: calc(100vw - 132px) !important;
+    }
+
+    .op-btn {
+      margin-left: 16px !important;
+    }
+  }
+
   .NNPE-wordDetail {
     position: fixed;
     top: 50%;
@@ -1634,6 +1671,7 @@ export default {
   .op-btn {
     box-sizing: border-box;
     display: flex;
+    flex-shrink: 0;
     align-items: center;
     justify-content: center;
     width: 56px;
@@ -1892,23 +1930,23 @@ export default {
             }
 
             .active {
-              color: #de4444;
+              color: #de4444 !important;
 
               &-yellow {
-                color: #ffc600;
+                color: #ffc600 !important;
               }
             }
 
             .wordActive {
-              color: #de4444;
+              color: #de4444 !important;
             }
 
             .wordActive-blue {
-              color: #ffc600;
+              color: #ffc600 !important;
             }
 
             .newActive {
-              color: #de4444;
+              color: #de4444 !important;
             }
           }
 
@@ -1979,23 +2017,23 @@ export default {
           }
 
           .active {
-            color: #de4444;
+            color: #de4444 !important;
 
             &-yellow {
-              color: #ffc600;
+              color: #ffc600 !important;
             }
           }
 
           .wordActive {
-            color: #de4444;
+            color: #de4444 !important;
           }
 
           .wordActive-blue {
-            color: #ffc600;
+            color: #ffc600 !important;
           }
 
           .newActive {
-            color: #de4444;
+            color: #de4444 !important;
           }
         }
 

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

@@ -343,7 +343,7 @@ export default {
     Strockplayredline,
     Audio,
   },
-  inject: ['getSelectId', 'convertText'],
+  inject: ['getSelectId', 'convertText', 'getCoursewareId'],
   props: [
     'data',
     'changeDetailIndex',
@@ -523,7 +523,7 @@ export default {
       this.loading1 = true;
       this.loading2 = true;
       this.loading3 = true;
-      let currentTreeID = this.$route.params.id || this.getSelectId();
+      let currentTreeID = this.getCoursewareId() || this.getSelectId();
       // 获取本课的 本教材的 本套的 的例句
       getCoursewareWordExampleSentenceList({
         courseware_id: currentTreeID, // 课件id

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

@@ -276,7 +276,7 @@ export default {
     NotesPreview,
   },
   mixins: [PreviewMixin],
-  inject: ['bookInfo'],
+  inject: ['bookInfo', 'getCoursewareId'],
   props: ['isMobile'],
   data() {
     return {
@@ -317,7 +317,7 @@ export default {
       NNPENewWordList: [],
       NpcNewWordMp3: [],
       colLength: 1,
-      courseware_id: this.$route.params.id,
+      courseware_id: this.getCoursewareId(),
       multilingualTextList: {},
       analyser_url: '',
     };
@@ -467,7 +467,7 @@ export default {
       this.pauseAudio();
       this.pauseVideo();
       this.isFull = true;
-      this.goFullscreen();
+      // this.goFullscreen();
     },
     pauseAudio() {
       let audio = document.getElementsByTagName('audio');
@@ -500,15 +500,15 @@ export default {
     },
     exitFullscreen() {
       this.isFull = false;
-      if (document.exitFullscreen) {
-        document.exitFullscreen();
-      } else if (document.msExitFullscreen) {
-        document.msExitFullscreen();
-      } else if (document.mozCancelFullScreen) {
-        document.mozCancelFullScreen();
-      } else if (document.webkitExitFullscreen) {
-        document.webkitExitFullscreen();
-      }
+      // if (document.exitFullscreen) {
+      //   document.exitFullscreen();
+      // } else if (document.msExitFullscreen) {
+      //   document.msExitFullscreen();
+      // } else if (document.mozCancelFullScreen) {
+      //   document.mozCancelFullScreen();
+      // } else if (document.webkitExitFullscreen) {
+      //   document.webkitExitFullscreen();
+      // }
     },
     handleData() {
       if (this.data.new_word_list) {

+ 1 - 0
src/views/book/courseware/preview/components/common/PreviewMixin.js

@@ -207,6 +207,7 @@ const mixin = {
         backgroundPosition: bcImgUrl ? `${pos.left}% ${pos.top}%` : '',
         backgroundImage: bcImgUrl ? `url(${bcImgUrl})` : '',
         backgroundColor: bcImgUrl ? `rgba(255, 255, 255, ${1 - back?.image_opacity / 100})` : '',
+        overflowY: 'auto',
       };
 
       if (back) {

+ 10 - 10
src/views/book/courseware/preview/components/dialogue_article/Practicechs.vue

@@ -968,7 +968,7 @@ export default {
       this.pauseAudio();
       this.pauseVideo();
       this.isFull = true;
-      this.goFullscreen();
+      // this.goFullscreen();
     },
     goFullscreen() {
       let id = `screen-${this.mathNum}`;
@@ -985,15 +985,15 @@ export default {
     },
     exitFullscreen() {
       this.isFull = false;
-      if (document.exitFullscreen) {
-        document.exitFullscreen();
-      } else if (document.msExitFullscreen) {
-        document.msExitFullscreen();
-      } else if (document.mozCancelFullScreen) {
-        document.mozCancelFullScreen();
-      } else if (document.webkitExitFullscreen) {
-        document.webkitExitFullscreen();
-      }
+      // if (document.exitFullscreen) {
+      //   document.exitFullscreen();
+      // } else if (document.msExitFullscreen) {
+      //   document.msExitFullscreen();
+      // } else if (document.mozCancelFullScreen) {
+      //   document.mozCancelFullScreen();
+      // } else if (document.webkitExitFullscreen) {
+      //   document.webkitExitFullscreen();
+      // }
     },
     changeIsFull() {
       this.isFull = false;

+ 10 - 11
src/views/book/courseware/preview/components/dialogue_article/index.vue

@@ -252,7 +252,6 @@ import Practice from './Practicechs.vue'; // 语音练习模式
 import WordModel from './WordModelChs.vue'; // 语音练习模式
 import Voicefullscreen from '../article/Voicefullscreen.vue';
 import { getToken } from '@/utils/auth';
-import { GetFileURLMap } from '@/api/app';
 import { analysSubmit, getSysConfig } from '@/api/article';
 import NewWordPreview from '../new_word/NewWordPreview.vue';
 import NotesPreview from '../notes/NotesPreview.vue';
@@ -461,7 +460,7 @@ export default {
       this.pauseAudio();
       this.pauseVideo();
       this.isFull = true;
-      this.goFullscreen();
+      // this.goFullscreen();
     },
     pauseAudio() {
       let audio = document.getElementsByTagName('audio');
@@ -494,15 +493,15 @@ export default {
     },
     exitFullscreen() {
       this.isFull = false;
-      if (document.exitFullscreen) {
-        document.exitFullscreen();
-      } else if (document.msExitFullscreen) {
-        document.msExitFullscreen();
-      } else if (document.mozCancelFullScreen) {
-        document.mozCancelFullScreen();
-      } else if (document.webkitExitFullscreen) {
-        document.webkitExitFullscreen();
-      }
+      // if (document.exitFullscreen) {
+      //   document.exitFullscreen();
+      // } else if (document.msExitFullscreen) {
+      //   document.msExitFullscreen();
+      // } else if (document.mozCancelFullScreen) {
+      //   document.mozCancelFullScreen();
+      // } else if (document.webkitExitFullscreen) {
+      //   document.webkitExitFullscreen();
+      // }
     },
     handleData() {
       if (this.data.new_word_list) {

+ 22 - 5
src/views/book/courseware/preview/components/drawing/DrawingPreview.vue

@@ -28,7 +28,7 @@
         />
       </div>
       <!-- 如果是查看答案模式 v-if 下面画画的vue-esign不显示 -->
-      <!-- <img :src="answer.answer_list[0].answer" alt="" /> -->
+      <img :src="answer.answer_list[0].answer" alt="" v-if="disabled" />
       <!-- <vue-esign
         ref="esign-drawing"
         class="esign-canvas"
@@ -39,6 +39,7 @@
         :line-color="lineColor"
       /> -->
       <VueSignaturePad
+        v-else
         ref="signaturePad"
         class="esign-canvas"
         :width="isMobile ? '100%' : data.image_width + 'px'"
@@ -46,18 +47,20 @@
         :options="options"
       />
     </div>
-    <div class="footer">
+    <div class="footer" v-if="!disabled">
       <div class="pen">
         <!-- 选择颜色 -->
         <el-color-picker v-model="lineColor" @change="changeColor" />
         <!-- 清除一笔 -->
         <img class="clean-btn" src="@/assets/drawing-back.png" @click="undo" />
+        <!-- 橡皮擦 -->
+        <!-- <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">Save</div>
+        <div class="save" @click="save">保 存</div>
       </div>
     </div>
   </div>
@@ -106,6 +109,7 @@ export default {
       imgSrc: '',
       signData: null,
       mobileHeight: 0,
+      isEraser: false, // 是否为橡皮擦模式
     };
   },
   created() {
@@ -116,10 +120,12 @@ export default {
     // 撤销
     undo() {
       this.$refs.signaturePad.undoSignature();
+      this.isEraser = false;
     },
     // 清除
     clear() {
       this.$refs.signaturePad.clearSignature();
+      this.isEraser = false;
     },
     // 保存
     save() {
@@ -148,9 +154,19 @@ export default {
     changeColor(val) {
       this.options = {
         penColor: val,
-        minWidth: this.weightList[this.penIndex],
-        maxWidth: this.weightList[this.penIndex],
+        minWidth: this.penIndex ? this.weightList[this.penIndex] : 1,
+        maxWidth: this.penIndex ? this.weightList[this.penIndex] : 1,
+      };
+    },
+    // 橡皮擦
+    eraser() {
+      this.penIndex = null;
+      this.options = {
+        penColor: '#DCDFE6',
+        minWidth: 3,
+        maxWidth: 3,
       };
+      this.isEraser = true;
     },
     // 调节画笔粗细大小
     selSize(index) {
@@ -160,6 +176,7 @@ export default {
         minWidth: this.weightList[this.penIndex],
         maxWidth: this.weightList[this.penIndex],
       };
+      this.isEraser = false;
     },
     initData() {
       if (!this.isJudgingRightWrong) {

+ 32 - 47
src/views/book/courseware/preview/components/fill/FillPreview.vue

@@ -14,16 +14,15 @@
           <template v-for="(li, i) in modelEssay">
             <template v-if="li.type === 'text'">
               <PinyinText
-                v-if="isEnable(data.property.view_pinyin)"
                 :key="`text-${i}`"
                 class="content"
                 :paragraph-list="li.paragraph_list"
                 :rich-text-list="li.rich_text_list"
                 :pinyin-position="data.property.pinyin_position"
-                :body-styles="getBodyStyles()"
+                :body-styles="pinyinBodyStyles"
+                :is-show-pinyin="isEnable(data.property.view_pinyin)"
                 :is-preview="true"
               />
-              <span v-else :key="`text-${i}`" class="html-content" v-html="renderTextBlockContent(li)"></span>
             </template>
             <template v-if="li.type === 'input'">
               <!-- 输入填空 -->
@@ -36,7 +35,7 @@
                   :class="[...computedAnswerClass(li.mark)]"
                   :style="[
                     {
-                      fontFamily: data.property.fill_font,
+                      '--fill-font-family': data.property.fill_font,
                       width: (inputWidthMap[li.mark] || data.property.input_default_width) + 'px',
                     },
                   ]"
@@ -86,7 +85,7 @@
                   :key="`record-${i}`"
                   type="mini"
                   :many-times="false"
-                  class="record-box"
+                  class="record-box fill"
                   :attrib="data.unified_attrib"
                   :answer-record-list="data.audio_answer_list"
                   :task-model="isJudgingRightWrong ? 'ANSWER' : ''"
@@ -110,7 +109,12 @@
       <div v-if="showLang" class="lang">
         {{ data.multilingual.find((item) => item.type === getLang())?.translation }}
       </div>
-      <PreviewOperation @showAnswerAnalysis="showAnswerAnalysis" @judgeCorrect="judgeCorrect" @retry="retry" />
+      <PreviewOperation
+        :is-show-retry="isHasFill"
+        @showAnswerAnalysis="showAnswerAnalysis"
+        @judgeCorrect="judgeCorrect"
+        @retry="retry"
+      />
     </div>
 
     <WriteDialog :visible.sync="writeVisible" @confirm="handleWriteConfirm" />
@@ -130,15 +134,15 @@
           <template v-for="(li, i) in modelEssay">
             <template v-if="li.type === 'text'">
               <PinyinText
-                v-if="isEnable(data.property.view_pinyin)"
                 :key="`answer-text-${i}`"
+                :is-show-pinyin="isEnable(data.property.view_pinyin)"
                 class="content"
                 :paragraph-list="li.paragraph_list"
                 :rich-text-list="li.rich_text_list"
+                :body-styles="pinyinBodyStyles"
                 :pinyin-position="data.property.pinyin_position"
                 :is-preview="true"
               />
-              <span v-else :key="`answer-text-${i}`" class="html-content" v-html="renderTextBlockContent(li)"></span>
             </template>
             <template v-if="li.type === 'input'">
               <span v-show="computedAnswerText(li.mark).length > 0" :key="`answer-${i}`" class="right-answer">
@@ -181,6 +185,19 @@ export default {
       writeMark: '',
     };
   },
+  computed: {
+    pinyinBodyStyles() {
+      const unifiedAttrib = this.data?.unified_attrib || {};
+      return {
+        fontFamily: unifiedAttrib.font || undefined,
+        fontSize: unifiedAttrib.font_size || undefined,
+        color: unifiedAttrib.text_color || undefined,
+      };
+    },
+    isHasFill() {
+      return this.data.model_essay.some((li) => li.type === 'input');
+    },
+  },
   watch: {
     'data.model_essay': {
       handler(list) {
@@ -221,21 +238,6 @@ export default {
     handleWav(data) {
       this.data.record_list = data;
     },
-    renderContent(content) {
-      return this.convertText(this.sanitizeHTML(content));
-    },
-    /**
-     * 渲染文本块内容
-     * @param {Object} textBlock 文本块对象,包含纯文本内容和富文本内容
-     */
-    renderTextBlockContent(textBlock) {
-      const richTextList = textBlock?.rich_text_list;
-      if (Array.isArray(richTextList) && richTextList.length > 0) {
-        const richHtml = richTextList.map((item) => item?.text || '').join('');
-        return this.renderContent(richHtml);
-      }
-      return this.renderContent(textBlock?.content || '');
-    },
     syncAnswerList(list) {
       this.answer.answer_list = list
         .map(({ type, input, audio_answer_list, mark }) => {
@@ -478,23 +480,6 @@ export default {
 
       this.$set(this.inputWidthMap, mark, nextWidth);
     },
-    getBodyStyles() {
-      const styles = {};
-      const unifiedAttrib = this.data?.unified_attrib || {};
-
-      // 从统一属性中获取样式
-      if (unifiedAttrib.font) {
-        styles['font-family'] = unifiedAttrib.font;
-      }
-      if (unifiedAttrib.font_size) {
-        styles['font-size'] = unifiedAttrib.font_size;
-      }
-      if (unifiedAttrib.text_color) {
-        styles['color'] = unifiedAttrib.text_color;
-      }
-
-      return styles;
-    },
   },
 };
 </script>
@@ -528,17 +513,16 @@ export default {
       display: inline;
     }
 
-    .html-content {
-      :deep p {
-        display: inline;
-      }
-    }
-
-    .record-box {
+    .record-box.fill {
       display: inline-flex;
       align-items: center;
       background-color: #fff;
       border-bottom: 1px solid $font-color;
+
+      :deep(.playBack) {
+        width: 20px;
+        height: 20px;
+      }
     }
 
     .write-click {
@@ -602,6 +586,7 @@ export default {
 
       :deep .el-input__inner {
         padding: 0;
+        font-family: var(--fill-font-family, 'arial'), sans-serif;
         font-size: 16pt;
         color: $font-color;
         text-align: center;

+ 35 - 29
src/views/book/courseware/preview/components/h5_games/H5GamesPreview.vue

@@ -2,7 +2,7 @@
 <template>
   <div class="imageText-preview" :style="getAreaStyle()">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
-    <div ref="fullscreenElement">
+    <div ref="fullscreenElement" :class="[full_type ? 'full-screen-games' : '']">
       <el-button
         type="primary"
         :class="[full_type ? 'exit-btn' : '']"
@@ -45,6 +45,7 @@ export default {
       data: getH5GamesData(),
       games_url: '',
       full_type: false,
+      scroll_top: 0, // 预览时滚动的高度 切换全屏时使用
     };
   },
   watch: {
@@ -57,7 +58,19 @@ export default {
   created() {
     this.initData();
   },
-  mounted() {},
+  mounted() {
+    document.addEventListener('keyup', (e) => {
+      if (event.key === 'Escape' || event.key === 'Esc' || event.keyCode === 27) {
+        this.full_type = false;
+        this.$nextTick(() => {
+          const div = document.querySelector('.main-container');
+          if (div) {
+            div.scrollTop = this.scroll_top;
+          }
+        });
+      }
+    });
+  },
   methods: {
     initData() {
       this.data.file_list.forEach((item) => {
@@ -72,35 +85,19 @@ export default {
       });
     },
     toggleFullScreen() {
-      const elem = this.$refs.fullscreenElement; // 获取需要全屏的元素
-      if (!document.fullscreenElement) {
-        this.full_type = true;
-        if (elem.requestFullscreen) {
-          elem.requestFullscreen(); // 现代浏览器,如Chrome, Firefox, Opera, Safari等
-        } else if (elem.mozRequestFullScreen) {
-          // Firefox
-          elem.mozRequestFullScreen();
-        } else if (elem.webkitRequestFullscreen) {
-          // Chrome, Safari等
-          elem.webkitRequestFullscreen();
-        } else if (elem.msRequestFullscreen) {
-          // IE/Edge
-          elem.msRequestFullscreen();
+      const div = document.querySelector('.main-container');
+      if (div) {
+        if (this.full_type) {
+          this.full_type = false;
+          this.$nextTick(() => {
+            div.scrollTop = this.scroll_top;
+          });
+        } else {
+          this.scroll_top = div.scrollTop;
+          this.full_type = true;
         }
       } else {
-        this.full_type = false;
-        if (document.exitFullscreen) {
-          document.exitFullscreen(); // 退出全屏模式
-        } else if (document.mozCancelFullScreen) {
-          // Firefox
-          document.mozCancelFullScreen();
-        } else if (document.webkitExitFullscreen) {
-          // Chrome, Safari等
-          document.webkitExitFullscreen();
-        } else if (document.msExitFullscreen) {
-          // IE/Edge
-          document.msExitFullscreen();
-        }
+        this.full_type = !this.full_type;
       }
     },
     /**
@@ -135,4 +132,13 @@ export default {
   top: 10px;
   left: 10px;
 }
+
+.full-screen-games {
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 99999;
+  width: 100%;
+  height: 100vh;
+}
 </style>

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

@@ -340,7 +340,7 @@ export default {
   z-index: 1;
   font-size: 0;
   cursor: pointer;
-  border: 3px solid transparent;
+  border: 2px solid transparent;
 
   &.active {
     border-color: #ff1616;

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

@@ -1559,6 +1559,7 @@ export default {
     Strockplay,
     writeTableZoom,
   },
+  inject: ['getCoursewareId'],
   mixins: [PreviewMixin],
   props: {
     newData: {
@@ -1591,7 +1592,7 @@ export default {
       indexStr: Math.random().toString(36).substring(2, 10),
       is_change: false,
       is_list: true, // 列表还是卡片展示
-      courseware_id: this.$route.params.id,
+      courseware_id: this.getCoursewareId(),
       rowWidth: 0,
       multilingualTextList: {}, // 多语言对应的切割后的翻译
       titleTrans: {},

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

@@ -267,7 +267,7 @@ export default {
     Strockplayredline,
     AudioRed,
   },
-  inject: ['getSelectId', 'convertText'],
+  inject: ['getSelectId', 'convertText', 'getCoursewareId'],
   props: [
     'data',
     'changeDetailIndex',
@@ -412,7 +412,7 @@ export default {
       this.loading1 = true;
       this.loading2 = true;
       this.loading3 = true;
-      let currentTreeID = this.$route.params.id || this.getSelectId();
+      let currentTreeID = this.getCoursewareId() || this.getSelectId();
       // 获取本课的 本教材的 本套的 的例句
       getCoursewareWordExampleSentenceList({
         courseware_id: currentTreeID, // 课件id

+ 1 - 0
src/views/book/courseware/preview/components/rich_text/RichTextPreview.vue

@@ -30,6 +30,7 @@
           />
           <span
             class="rich-text"
+            :style="getBodyStyles()"
             @mouseover="handleRichFillClick"
             @mouseleave="onLeave"
             v-html="convertText(sanitizeHTML(data.content))"

+ 5 - 0
src/views/book/courseware/preview/components/table/TablePreview.vue

@@ -666,6 +666,11 @@ export default {
         this.handleData();
       },
     },
+    'data.property': {
+      handler(val) {
+        this.computedTableCellShow();
+      },
+    },
     isJudgingRightWrong(val) {
       if (!val) return;
       this.data.answer_lists = this.answer.answer_list;

+ 2 - 1
src/views/book/courseware/preview/components/video_interaction/ExercisePreview.vue

@@ -32,6 +32,7 @@ export default {
       modal: false,
     };
   },
+  inject: ['getCoursewareId'],
   props: ['exercise_id', 'feed_back'],
   data() {
     return {
@@ -93,7 +94,7 @@ export default {
       this.loading = true;
       let data = {
         exercise_id: this.exercise_id,
-        courseware_id: this.$route.params.id,
+        courseware_id: this.getCoursewareId(),
       };
       GetCoursewareExerciseView(data)
         .then((res) => {

+ 2 - 1
src/views/book/courseware/preview/components/video_interaction/Report.vue

@@ -50,6 +50,7 @@ export default {
       getPermissionControl: () => this.permissionControl,
     };
   },
+  inject: ['getCoursewareId'],
   props: ['reportResult', 'exerciseList', 'userAnswer', 'fileList'],
   data() {
     return {
@@ -127,7 +128,7 @@ export default {
       this.loading = true;
       let data = {
         exercise_id,
-        courseware_id: this.$route.params.id,
+        courseware_id: this.getCoursewareId(),
       };
       GetCoursewareExerciseView(data)
         .then((res) => {