Kaynağa Gözat

预览修改

dsy 1 hafta önce
ebeveyn
işleme
57ae1bcc8f
29 değiştirilmiş dosya ile 367 ekleme ve 166 silme
  1. 3 90
      src/components/CommonPreview.vue
  2. 74 2
      src/components/PinyinText.vue
  3. 13 5
      src/views/book/courseware/preview/components/article/components/Practice.vue
  4. 1 1
      src/views/book/courseware/preview/components/article/components/Strockplayredline.vue
  5. 11 1
      src/views/book/courseware/preview/components/article/components/WordPhraseDetail.vue
  6. 10 1
      src/views/book/courseware/preview/components/article/components/Wordcard.vue
  7. 11 1
      src/views/book/courseware/preview/components/article/index.vue
  8. 5 0
      src/views/book/courseware/preview/components/audio/AudioPreview.vue
  9. 32 5
      src/views/book/courseware/preview/components/character/CharacterPreview.vue
  10. 10 1
      src/views/book/courseware/preview/components/character_base/CharacterBasePreview.vue
  11. 9 1
      src/views/book/courseware/preview/components/character_base/components/AudioPlay.vue
  12. 10 0
      src/views/book/courseware/preview/components/character_base/components/Strockred.vue
  13. 10 0
      src/views/book/courseware/preview/components/character_structure/CharacterStructurePreview.vue
  14. 11 7
      src/views/book/courseware/preview/components/common/AudioPlay.vue
  15. 8 4
      src/views/book/courseware/preview/components/common/VideoPlay.vue
  16. 10 1
      src/views/book/courseware/preview/components/dialogue_article/index.vue
  17. 4 0
      src/views/book/courseware/preview/components/fill/FillPreview.vue
  18. 1 1
      src/views/book/courseware/preview/components/judge/JudgePreview.vue
  19. 10 0
      src/views/book/courseware/preview/components/newWord_template/NewWordTemplatePreview.vue
  20. 2 1
      src/views/book/courseware/preview/components/newWord_template/components/EditDiv.vue
  21. 25 14
      src/views/book/courseware/preview/components/newWord_template/components/Strockplayredline.vue
  22. 1 1
      src/views/book/courseware/preview/components/new_word/components/AudioPlay.vue
  23. 13 2
      src/views/book/courseware/preview/components/new_word/components/WordPhraseDetail.vue
  24. 3 3
      src/views/book/courseware/preview/components/pinyin_base/PinyinBasePreview.vue
  25. 2 0
      src/views/book/courseware/preview/components/rich_text/RichTextPreview.vue
  26. 14 2
      src/views/book/courseware/preview/components/select/SelectPreview.vue
  27. 9 1
      src/views/book/courseware/preview/components/video/VideoPreview.vue
  28. 48 21
      src/views/book/courseware/preview/components/video_interaction/ExercisePreview.vue
  29. 7 0
      src/views/eep_view/index.vue

+ 3 - 90
src/components/CommonPreview.vue

@@ -1,26 +1,5 @@
 <template>
   <div class="common-preview">
-    <div class="common-preview__header">
-      <div class="courseware">
-        <span class="flow-nodename">{{ courseware_info.cur_audit_flow_node_name }}</span>
-        <slot name="middle" :courseware="courseware_info"></slot>
-        <div class="group">
-          <el-checkbox v-model="isShowGroup">显示分组</el-checkbox>
-          <el-checkbox v-model="groupShowAll">分组显示全部</el-checkbox>
-          <el-checkbox v-model="isJudgeCorrect">判断对错</el-checkbox>
-          <el-checkbox v-model="isShowAnswer" :disabled="!isJudgeCorrect">显示答案</el-checkbox>
-        </div>
-        <span class="link">
-          <el-select v-model="lang" placeholder="请选择语言" size="mini" class="lang-select">
-            <el-option v-for="item in langList" :key="item.type" :label="item.name" :value="item.type" />
-          </el-select>
-        </span>
-        <div class="operator">
-          <slot name="operator" :courseware="courseware_info" :project-id="projectId"></slot>
-        </div>
-      </div>
-    </div>
-
     <div class="audit-content">
       <!-- 左侧菜单栏  -->
       <aside v-if="navigationShow" class="left-menu">
@@ -78,7 +57,7 @@
             :group-row-list="content_group_row_list"
             :data="data"
             :component-list="component_list"
-            :courseware-id="select_node"
+            :courseware-id="curSelectId"
             :background="background"
             :can-remark="false"
             :show-remark="false"
@@ -252,6 +231,7 @@ export default {
       convertText: this.convertText,
       getTitleList: () => this.title_list,
       getPermissionControl: () => this.permissionControl,
+      readFileContent: this.readFileContent,
     };
   },
   props: {
@@ -794,73 +774,6 @@ export default {
 $total-width: $courseware-width + $courseware-left-margin + $courseware-right-margin;
 
 .common-preview {
-  &__header {
-    position: sticky;
-    top: 0;
-    left: 0;
-    z-index: 9;
-    display: flex;
-    align-items: center;
-    height: 40px;
-    padding: 6px 4px;
-    margin-bottom: 5px;
-    background-color: #fff;
-    border-top: $border;
-    border-bottom: $border;
-
-    > .courseware {
-      display: flex;
-      flex-grow: 1;
-      column-gap: 16px;
-      align-items: center;
-      justify-content: space-between;
-      height: 40px;
-
-      .lang-select {
-        :deep .el-input {
-          width: 100px;
-        }
-
-        :deep .el-input__inner {
-          height: 24px;
-          line-height: 24px;
-          background-color: #fff;
-        }
-
-        :deep .el-input__icon {
-          line-height: 24px;
-        }
-      }
-
-      .flow-nodename {
-        flex: 1;
-      }
-
-      .group {
-        display: flex;
-        align-items: center;
-      }
-
-      .operator {
-        display: flex;
-        column-gap: 8px;
-        align-items: center;
-
-        .link {
-          + .link {
-            margin-left: 0;
-
-            &::before {
-              margin-right: 8px;
-              color: #999;
-              content: '|';
-            }
-          }
-        }
-      }
-    }
-  }
-
   .main-container {
     position: relative;
     flex: 1;
@@ -967,7 +880,7 @@ $total-width: $courseware-width + $courseware-left-margin + $courseware-right-ma
   .audit-content {
     display: flex;
     min-width: 1810px;
-    height: calc(100vh - 48px);
+    height: calc(100vh - 18px);
 
     .left-menu {
       display: flex;

+ 74 - 2
src/components/PinyinText.vue

@@ -4,6 +4,7 @@
     <AudioPlay
       v-if="isEnable(isEnableVoice)"
       :file-id="audioFileId"
+      :file-url="audioFileUrl"
       :theme-color="unifiedAttrib && unifiedAttrib.topic_color ? unifiedAttrib.topic_color : ''"
     />
 
@@ -43,7 +44,7 @@
                   'align-items': getWordAlignItems(word, block),
                 }"
               >
-                <span class="pinyin" :style="getPinyinStyle(word, block)">
+                <span class="pinyin" :style="[getPinyinStyle(word, block), getWholePinyinStyle(word, block)]">
                   {{ getPinyinText(word) }}
                 </span>
                 <span class="py-char" :style="getCharStyle(word, block)">{{ convertText(word.text) }}</span>
@@ -244,6 +245,10 @@ export default {
       type: String,
       default: '',
     },
+    audioFileUrl: {
+      type: String,
+      default: '',
+    },
     unifiedAttrib: {
       type: Object,
       default: () => ({}),
@@ -829,7 +834,12 @@ export default {
     },
     // 兼容历史数据
     getPinyinText(item) {
-      return this.checkShowPinyin(item.showPinyin) ? item.pinyin : '\u200B';
+      if (!this.checkShowPinyin(item.showPinyin)) return '\u200B';
+      const raw = item.pinyin || '';
+      // 整词渲染时去掉首尾占位空格(这些空格只在逐字对齐时有意义),
+      // 避免占位空格撑宽拼音导致居中偏移
+      const text = String(raw).replace(/^[\u3000\s]+|[\u3000\s]+$/g, '');
+      return text || '\u200B';
     },
     shouldShowWordPinyin(item) {
       return this.checkShowPinyin(item && item.showPinyin);
@@ -855,6 +865,68 @@ export default {
       }
       return 'center';
     },
+    // 判断字符是否半宽(ASCII / 拉丁字母及声调字符等),用于估算文字视觉宽度
+    isHalfWidthChar(char) {
+      const code = char.charCodeAt(0);
+      // 基础拉丁、拉丁扩展 A/B(拼音带声调字符大多在此区间)
+      if (code <= 0x024f) return true;
+      return false;
+    },
+    // 字符视觉宽度单位:中文/全角=1,英文/数字/拉丁字母≈0.5,组合音标不占宽
+    getCharWidthUnit(char) {
+      const code = char.charCodeAt(0);
+      if (code >= 0x0300 && code <= 0x036f) return 0;
+      return this.isHalfWidthChar(char) ? 0.5 : 1;
+    },
+    // 整词渲染时,让拼音只横跨非标点文字区域并居中;
+    getWholePinyinStyle(word, block) {
+      const letterSpacing = block?.styleObj?.letterSpacing;
+      if (letterSpacing && letterSpacing !== '0' && letterSpacing !== '0px') {
+        return {};
+      }
+      const text = word.text || '';
+      const chars = Array.from(text);
+      if (chars.length === 0) {
+        return { alignSelf: 'stretch', textAlign: 'center' };
+      }
+
+      let totalUnits = 0;
+      let leadingUnits = 0;
+      let trailingUnits = 0;
+
+      chars.forEach((c) => {
+        totalUnits += this.getCharWidthUnit(c);
+      });
+
+      for (let i = 0; i < chars.length; i += 1) {
+        PUNCT_REGEX.lastIndex = 0;
+        if (PUNCT_REGEX.test(chars[i])) {
+          leadingUnits += this.getCharWidthUnit(chars[i]);
+        } else {
+          break;
+        }
+      }
+      for (let i = chars.length - 1; i >= 0; i -= 1) {
+        PUNCT_REGEX.lastIndex = 0;
+        if (PUNCT_REGEX.test(chars[i])) {
+          trailingUnits += this.getCharWidthUnit(chars[i]);
+        } else {
+          break;
+        }
+      }
+
+      const nonPunctUnits = totalUnits - leadingUnits - trailingUnits;
+      if (nonPunctUnits <= 0 || totalUnits <= 0 || (leadingUnits === 0 && trailingUnits === 0)) {
+        return { alignSelf: 'stretch', textAlign: 'center' };
+      }
+
+      return {
+        alignSelf: 'flex-start',
+        width: `${(nonPunctUnits / totalUnits) * 100}%`,
+        marginLeft: `${(leadingUnits / totalUnits) * 100}%`,
+        textAlign: 'center',
+      };
+    },
     // 拼音固定为拼音字体,跟随汉字的字号、颜色、粗细的样式
     getPinyinStyle(item, block) {
       const styles = {};

+ 13 - 5
src/views/book/courseware/preview/components/article/components/Practice.vue

@@ -282,7 +282,12 @@ export default {
     Freewrite,
     AudioRed,
   },
-  inject: ['convertText'],
+  inject: {
+    bookStatus: {
+      default: 'online',
+    },
+    convertText: {},
+  },
   props: [
     'cur',
     'changePraShow',
@@ -318,6 +323,9 @@ export default {
   watch: {},
   // 生命周期 - 创建完成(可以访问当前this实例)
   created() {
+    if (this.bookStatus === 'offline') {
+      this.$message.warning('该功能只有在线教材可以支持,请使用在线教材');
+    }
     let _this = this;
     let color = _this.colorsList[_this.colorIndex];
     _this.hanzicolor = color;
@@ -790,6 +798,10 @@ export default {
     &.z-top {
       z-index: 2;
     }
+
+    &.z-none {
+      z-index: -1;
+    }
   }
 
   &-phone {
@@ -855,10 +867,6 @@ export default {
     .z-top {
       display: flex;
     }
-
-    .z-none {
-      z-index: -1;
-    }
   }
 }
 

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

@@ -136,7 +136,7 @@ export default {
   position: absolute;
   top: 0;
   right: 0;
-  z-index: 1;
+  z-index: 2;
   display: flex;
   align-items: center;
   justify-content: center;

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

@@ -346,7 +346,14 @@ export default {
     Strockplayredline,
     Audio,
   },
-  inject: ['getSelectId', 'convertText', 'getCoursewareId'],
+  inject: {
+    bookStatus: {
+      default: 'online',
+    },
+    convertText: {},
+    getSelectId: {},
+    getCoursewareId: {},
+  },
   props: [
     'data',
     'changeDetailIndex',
@@ -411,6 +418,9 @@ export default {
   },
   // 生命周期 - 创建完成(可以访问当前this实例)
   created() {
+    if (this.bookStatus === 'offline') {
+      this.$message.warning('该功能只有在线教材可以支持,请使用在线教材');
+    }
     // 去掉生词里的标点
     let new_word_str = '';
     for (let i = 0; i < this.data.new_word.length; i++) {

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

@@ -233,7 +233,12 @@ export default {
     Practice,
     WordPhraseDetail,
   },
-  inject: ['convertText'],
+  inject: {
+    bookStatus: {
+      default: 'online',
+    },
+    convertText: {},
+  },
   props: [
     'word',
     'changeWordCard',
@@ -280,6 +285,9 @@ export default {
   },
   // 生命周期 - 创建完成(可以访问当前this实例)
   created() {
+    if (this.bookStatus === 'offline') {
+      this.$message.warning('播放笔画动画功能只有在线教材可以支持,请使用在线教材');
+    }
     let new_word_str = '';
     for (let i = 0; i < this.word.detail.new_word.length; i++) {
       if (/[\u4E00-\u9FA5\uF900-\uFA2D]/.test(this.word.detail.new_word[i])) {
@@ -585,6 +593,7 @@ export default {
         position: absolute;
         right: 4px;
         bottom: 4px;
+        z-index: 1;
         width: 16px;
         cursor: pointer;
       }

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

@@ -276,7 +276,13 @@ export default {
     NotesPreview,
   },
   mixins: [PreviewMixin],
-  inject: ['bookInfo', 'getCoursewareId'],
+  inject: {
+    bookStatus: {
+      default: 'online',
+    },
+    bookInfo: {},
+    getCoursewareId: {},
+  },
   props: ['isMobile'],
   data() {
     return {
@@ -437,6 +443,10 @@ export default {
       return pinyin;
     },
     submit() {
+      if (this.bookStatus === 'offline') {
+        this.$message.warning('该功能只有在线教材可以支持,请使用在线教材');
+        return;
+      }
       let access_token =
         this.tokenData && this.tokenData.gcls_sys_session_info
           ? this.tokenData.gcls_sys_session_info.access_token

+ 5 - 0
src/views/book/courseware/preview/components/audio/AudioPreview.vue

@@ -10,6 +10,7 @@
               :view-size="data.property.style_mode"
               :view-method="data.property.view_method"
               :file-id="file.file_id"
+              :file-url="file.file_url"
               :file-name-display="data.property.file_name_display_mode"
               :file-name="file.file_name.slice(0, file.file_name.lastIndexOf('.'))"
               :audio-index="i"
@@ -23,6 +24,7 @@
               :view-size="data.property.style_mode"
               :view-method="data.property.view_method"
               :file-id="file.file_id"
+              :file-url="file.file_url"
               :file-name-display="data.property.file_name_display_mode"
               :file-name="file.file_name.slice(0, file.file_name.lastIndexOf('.'))"
               :audio-index="i"
@@ -36,6 +38,7 @@
           <AudioPlay
             view-size="small"
             :file-id="file.file_id"
+            :file-url="file.file_url"
             :audio-index="i"
             :topic-color="data.unified_attrib?.topic_color"
           />
@@ -55,6 +58,7 @@
               view-size="big"
               :view-method="data.property.view_method"
               :file-id="file.file_id"
+              :file-url="file.file_url"
               :file-name-display="data.property.file_name_display_mode"
               :file-name="file.file_name.slice(0, file.file_name.lastIndexOf('.'))"
               :show-slider="true"
@@ -71,6 +75,7 @@
                 :ref="file.file_id"
                 view-size="list"
                 :file-id="file.file_id"
+                :file-url="file.file_url"
                 :file-name="file.file_name.slice(0, file.file_name.lastIndexOf('.'))"
                 :show-slider="false"
                 :audio-index="i"

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

@@ -242,7 +242,22 @@
                           ? data.unified_attrib.topic_color
                           : '#346cda',
                     }"
+                    v-if="items && items.type === 'hanzi'"
                   />
+                  <template v-if="items && items.type === 'img'">
+                    <el-image
+                      v-if="items.file_list[0]"
+                      class="items-image"
+                      :class="['frame-size-' + data.property.frame_size]"
+                      :src="items.file_list[0].file_url"
+                      fit="contain"
+                      :style="{
+                        borderColor:
+                          data.unified_attrib && data.unified_attrib.topic_color ? data.unified_attrib.topic_color : '',
+                      }"
+                    />
+                  </template>
+                  <template v-if="items && items.type === 'write'"> </template>
                 </div>
               </template>
 
@@ -465,6 +480,11 @@ export default {
     SoundRecord,
   },
   mixins: [PreviewMixin],
+  inject: {
+    bookStatus: {
+      default: 'online',
+    },
+  },
   data() {
     return {
       data: getCharacterData(),
@@ -510,6 +530,9 @@ export default {
     },
   },
   created() {
+    // if (this.bookStatus === 'offline') {
+    //   this.$message.warning('播放笔画动画功能只有在线教材可以支持,请使用在线教材');
+    // }
     if (
       (this.data.answer_list && this.data.answer_list.length > 0) ||
       (this.data.analysis_list && this.data.analysis_list.length > 0)
@@ -544,15 +567,19 @@ export default {
           if (item.content.trim()) {
             for (let i = 0; i < this.data.property.write_number; i++) {
               item.content_list.forEach((items) => {
-                arr.push({ imgArr: null, flag: null });
+                if (items.type !== 'img') {
+                  arr.push({ imgArr: null, flag: null });
+                }
               });
             }
             for (let i = 0; i < this.data.property.miao_number; i++) {
               item.content_list.forEach((items) => {
-                miao_arr.push({
-                  strokes: items && items.hz_info && items.hz_info[0] ? items.hz_info[0].hzDetail.hz_json : null,
-                  length: item.content_list.length,
-                });
+                if (items && items.hz_info && items.hz_info[0]) {
+                  miao_arr.push({
+                    strokes: items && items.hz_info && items.hz_info[0] ? items.hz_info[0].hzDetail.hz_json : null,
+                    length: item.content_list.length,
+                  });
+                }
               });
             }
             item.imgArr = arr;

+ 10 - 1
src/views/book/courseware/preview/components/character_base/CharacterBasePreview.vue

@@ -182,6 +182,11 @@ export default {
     FreewriteLettle,
   },
   mixins: [PreviewMixin],
+  inject: {
+    bookStatus: {
+      default: 'online',
+    },
+  },
   data() {
     return {
       data: getCharacterBaseData(),
@@ -220,7 +225,11 @@ export default {
       immediate: true,
     },
   },
-  created() {},
+  created() {
+    // if (this.bookStatus === 'offline') {
+    //   this.$message.warning('播放笔画动画功能只有在线教材可以支持,请使用在线教材');
+    // }
+  },
   methods: {
     handleWav(data) {
       this.data.record_list = data;

+ 9 - 1
src/views/book/courseware/preview/components/character_base/components/AudioPlay.vue

@@ -59,6 +59,10 @@ export default {
       type: String,
       required: true,
     },
+    fileUrl: {
+      type: String,
+      default: '',
+    },
     themeColor: {
       type: String,
       default: '',
@@ -99,8 +103,12 @@ export default {
   watch: {
     fileId: {
       handler(val) {
+        if (this.fileUrl) {
+          this.url = this.fileUrl;
+          return;
+        }
         if (!val) return;
-        if (val.indexOf('http') > -1) {
+        if (val.indexOf('/') > -1) {
           this.url = val;
         } else {
           GetFileURLMap({ file_id_list: [val] }).then(({ url_map }) => {

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

@@ -52,6 +52,11 @@ export default {
       default: 'tian',
     },
   },
+  inject: {
+    bookStatus: {
+      default: 'online',
+    },
+  },
   data() {
     return {
       writer: null,
@@ -73,6 +78,11 @@ export default {
       deep: true,
     },
   },
+  created() {
+    if (this.bookStatus === 'offline') {
+      this.$message.warning('该功能只有在线教材可以支持,请使用在线教材');
+    }
+  },
   mounted() {
     this.$nextTick(() => {
       this.initHanziwrite();

+ 10 - 0
src/views/book/courseware/preview/components/character_structure/CharacterStructurePreview.vue

@@ -280,6 +280,11 @@ export default {
 
   components: { draggable, Strockplay },
   mixins: [PreviewMixin],
+  inject: {
+    bookStatus: {
+      default: 'online',
+    },
+  },
   data() {
     return {
       data: getCharacterStructureData(),
@@ -307,6 +312,11 @@ export default {
       immediate: true,
     },
   },
+  created() {
+    // if (this.bookStatus === 'offline') {
+    //   this.$message.warning('播放笔画动画功能只有在线教材可以支持,请使用在线教材');
+    // }
+  },
   methods: {
     onMove(e) {
       return false;

+ 11 - 7
src/views/book/courseware/preview/components/common/AudioPlay.vue

@@ -112,7 +112,7 @@
     <audio
       :id="fileId"
       :ref="fileId"
-      :src="url"
+      :src="fileUrl"
       preload="metadata"
       @loadedmetadata="onLoadedmetadata"
       @timeupdate="onTimeupdate"
@@ -131,6 +131,10 @@ export default {
       type: String,
       required: true,
     },
+    fileUrl: {
+      type: String,
+      default: '',
+    },
     fileName: {
       type: String,
       default: '',
@@ -206,6 +210,10 @@ export default {
   watch: {
     fileId: {
       handler(val) {
+        if (this.fileUrl) {
+          this.url = this.fileUrl;
+          return;
+        }
         if (!val) return;
         GetFileURLMap({ file_id_list: [val] }).then(({ url_map }) => {
           this.url = url_map[val];
@@ -232,7 +240,7 @@ export default {
       this.curTime = curTime * 1000;
     },
     playAudio() {
-      if (!this.url) return;
+      if (!this.url || !this.fileUrl) return;
       const audio = this.$refs[this.fileId] || {};
       this.pauseAll();
       audio.playbackRate = this.audio.playbackRate;
@@ -289,11 +297,7 @@ export default {
       let audioArr = document.getElementsByTagName('audio');
       if (audioArr && audioArr.length > 0) {
         for (let i = 0; i < audioArr.length; i++) {
-          if (audioArr[i].src === this.url) {
-            if (audioArr[i].id !== this.fileId) {
-              audioArr[i].pause();
-            }
-          } else {
+          if (audioArr[i].id !== this.fileId) {
             audioArr[i].pause();
           }
         }

+ 8 - 4
src/views/book/courseware/preview/components/common/VideoPlay.vue

@@ -4,7 +4,7 @@
       <div class="video-box">
         <video
           ref="veo"
-          :src="url"
+          :src="fileUrl"
           :style="`object-fit: ${zoom};`"
           :poster="veoPoster"
           :autoplay="autoplay"
@@ -26,7 +26,7 @@
       <template v-if="'independent' === viewMethod">
         <video
           ref="veo"
-          :src="url"
+          :src="fileUrl"
           :style="`object-fit: ${zoom};`"
           :poster="veoPoster"
           :autoplay="autoplay"
@@ -43,7 +43,7 @@
         </video>
       </template>
       <template v-else>
-        <video ref="veo" :src="url" :poster="veoPoster">
+        <video ref="veo" :src="fileUrl" :poster="veoPoster">
           <h2>您的浏览器<strong>不支持video标签</strong></h2>
         </video>
       </template>
@@ -65,6 +65,10 @@ export default {
       type: String,
       required: true,
     },
+    fileUrl: {
+      type: String,
+      default: '',
+    },
     autoplay: {
       // 视频就绪后是否马上播放
       type: Boolean,
@@ -141,7 +145,7 @@ export default {
   watch: {
     fileId: {
       handler(val) {
-        if (!val) return;
+        if (!val || !this.fileUrl) return;
         GetFileURLMap({ file_id_list: [val] }).then(({ url_map }) => {
           this.url = url_map[val];
         });

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

@@ -269,7 +269,12 @@ export default {
     NotesPreview,
   },
   mixins: [PreviewMixin],
-  inject: ['bookInfo'],
+  inject: {
+    bookStatus: {
+      default: 'online',
+    },
+    bookInfo: {},
+  },
   props: ['isMobile'],
   data() {
     return {
@@ -427,6 +432,10 @@ export default {
       return pinyin;
     },
     submit() {
+      if (this.bookStatus === 'offline') {
+        this.$message.warning('该功能只有在线教材可以支持,请使用在线教材');
+        return;
+      }
       let access_token =
         this.tokenData && this.tokenData.gcls_sys_session_info
           ? this.tokenData.gcls_sys_session_info.access_token

+ 4 - 0
src/views/book/courseware/preview/components/fill/FillPreview.vue

@@ -615,6 +615,10 @@ export default {
 
     .fill-content {
       margin: 0;
+
+      :deep .el-input__inner {
+        background-color: transparent !important;
+      }
     }
 
     .content {

+ 1 - 1
src/views/book/courseware/preview/components/judge/JudgePreview.vue

@@ -281,7 +281,7 @@ export default {
         }
 
         &.wrong {
-          box-shadow: 0 0 0 1px $error-color;
+          background-color: #ffd4d9;
         }
 
         .serial-number {

+ 10 - 0
src/views/book/courseware/preview/components/newWord_template/NewWordTemplatePreview.vue

@@ -469,6 +469,11 @@ export default {
 
   components: { Strockplayredline, EditDiv },
   mixins: [PreviewMixin],
+  inject: {
+    bookStatus: {
+      default: 'online',
+    },
+  },
   data() {
     return {
       data: getNewWordTemplateData(),
@@ -491,6 +496,11 @@ export default {
       this.userAnswer = this.answer.answer_list;
     },
   },
+  created() {
+    // if (this.bookStatus === 'offline') {
+    //   this.$message.warning('播放笔画动画功能只有在线教材可以支持,请使用在线教材');
+    // }
+  },
   methods: {
     // 修改数字高亮对应笔画(待优化)
     changeAnswer(items, col, row) {

+ 2 - 1
src/views/book/courseware/preview/components/newWord_template/components/EditDiv.vue

@@ -208,7 +208,7 @@ export default {
             e.target.innerHTML = str.trim();
             _this.fullContent = str.trim();
             _this.$emit('input', str.trim());
-            _this.textFocus('tone');
+            // _this.textFocus('tone');
           }, 10);
         }
       });
@@ -278,6 +278,7 @@ export default {
       this.isLocked = false;
       e.target.innerHTML = e.target.innerHTML.replace(/&nbsp;/gi, '').trim();
       this.$emit('input', e.target.innerHTML);
+      this.handleReplaceTone(e);
     },
     handleReset() {
       document.getElementById(this.id).innerHTML = '';

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

@@ -120,20 +120,31 @@ export default {
           },
         });
       } else {
-        _this.writer = HanziWriter.default.create(_this.targetDiv, _this.Book_text, {
-          charDataLoader(char, onComplete) {
-            let MethodName = 'hz_resource_manager-GetHZStrokesContent';
-            let data = {
-              hz: char,
-            };
-            GetStaticResources(MethodName, data).then((res) => {
-              onComplete(res);
-            });
-          },
-          padding: 5,
-          showOutline: true,
-          strokeColor: _this.strokeColor ? _this.strokeColor : '#333',
-        });
+        if (_this.hz_json) {
+          _this.writer = HanziWriter.default.create(_this.targetDiv, _this.Book_text, {
+            charDataLoader(char, onComplete) {
+              onComplete(_this.hz_json);
+            },
+            padding: 5,
+            showOutline: true,
+            strokeColor: _this.strokeColor ? _this.strokeColor : '#333',
+          });
+        } else {
+          _this.writer = HanziWriter.default.create(_this.targetDiv, _this.Book_text, {
+            charDataLoader(char, onComplete) {
+              let MethodName = 'hz_resource_manager-GetHZStrokesContent';
+              let data = {
+                hz: char,
+              };
+              GetStaticResources(MethodName, data).then((res) => {
+                onComplete(res);
+              });
+            },
+            padding: 5,
+            showOutline: true,
+            strokeColor: _this.strokeColor ? _this.strokeColor : '#333',
+          });
+        }
       }
     },
     handleData(stroke_num) {

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

@@ -38,7 +38,7 @@ export default {
     fileId: {
       handler(val) {
         if (!val) return;
-        if (val.indexOf('http') > -1) {
+        if (val.indexOf('/') > -1) {
           this.url = val;
         } else {
           GetFileURLMap({ file_id_list: [val] }).then(({ url_map }) => {

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

@@ -272,7 +272,14 @@ export default {
     Strockplayredline,
     AudioRed,
   },
-  inject: ['getSelectId', 'convertText', 'getCoursewareId'],
+  inject: {
+    bookStatus: {
+      default: 'online',
+    },
+    convertText: {},
+    getSelectId: {},
+    getCoursewareId: {},
+  },
   props: [
     'data',
     'changeDetailIndex',
@@ -336,7 +343,11 @@ export default {
     },
   },
   // 生命周期 - 创建完成(可以访问当前this实例)
-  created() {},
+  created() {
+    if (this.bookStatus === 'offline') {
+      this.$message.warning('该功能只有在线教材可以支持,请使用在线教材');
+    }
+  },
   // 生命周期 - 挂载完成(可以访问DOM元素)
   mounted() {
     this.initData();

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

@@ -46,7 +46,7 @@
                   :disabled="disabled"
                   :style="[
                     {
-                      width: Math.max(20, itemc.con.length * 10) + 'px',
+                      width: Math.max(30, itemc.con.length * 18) + 'px',
                       fontSize:
                         data.unified_attrib && data.unified_attrib.font_size ? data.unified_attrib.font_size : '',
                       '--input-color':
@@ -231,7 +231,7 @@
                       :disabled="true"
                       :style="[
                         {
-                          width: Math.max(20, itemc.answer.length * 10) + 'px',
+                          width: Math.max(30, itemc.answer.length * 18) + 'px',
                           fontSize:
                             data.unified_attrib && data.unified_attrib.font_size ? data.unified_attrib.font_size : '',
                           '--input-color':
@@ -938,7 +938,7 @@ export default {
     }
 
     &.active {
-      color: #2f6fec;
+      color: #2f6fec !important;
     }
   }
 

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

@@ -9,6 +9,7 @@
           v-if="isEnable(data.property.view_pinyin)"
           :is-enable-voice="data.property.is_enable_voice"
           :audio-file-id="data.audio_file_id"
+          :audio-file-url="data.file_list?.[0]?.file_url"
           :unified-attrib="data.unified_attrib"
           :paragraph-list="data.paragraph_list"
           :rich-text-list="data.rich_text_list"
@@ -26,6 +27,7 @@
           <AudioPlay
             v-if="isEnable(data.property.is_enable_voice)"
             :file-id="data.audio_file_id"
+            :file-url="data.file_list?.[0]?.file_url"
             :theme-color="data.unified_attrib && data.unified_attrib.topic_color ? data.unified_attrib.topic_color : ''"
           />
           <span

+ 14 - 2
src/views/book/courseware/preview/components/select/SelectPreview.vue

@@ -6,7 +6,13 @@
     <div class="main">
       <ul
         class="option-list"
-        :style="{ flexDirection: data.property.arrange_type === arrangeTypeList[0].value ? 'row' : 'column' }"
+        :style="{
+          flexDirection: isMobile
+            ? 'column'
+            : data.property.arrange_type === arrangeTypeList[0].value
+              ? 'row'
+              : 'column',
+        }"
       >
         <li
           v-for="({ content, mark, multilingual, paragraph_list, rich_text_list }, i) in data.option_list"
@@ -63,7 +69,13 @@
       <ul
         slot="right-answer"
         class="option-list"
-        :style="{ flexDirection: data.property.arrange_type === arrangeTypeList[0].value ? 'row' : 'column' }"
+        :style="{
+          flexDirection: isMobile
+            ? 'column'
+            : data.property.arrange_type === arrangeTypeList[0].value
+              ? 'row'
+              : 'column',
+        }"
       >
         <li
           v-for="({ content, mark, multilingual, paragraph_list, rich_text_list }, i) in data.option_list"

+ 9 - 1
src/views/book/courseware/preview/components/video/VideoPreview.vue

@@ -14,6 +14,7 @@
             view-size="small"
             view-method="independent"
             :file-id="file.file_id"
+            :file-url="file.file_url"
             :cur-video-index="curVideoIndex"
             @changeFile="changeFile"
           />
@@ -42,6 +43,7 @@
             <VideoPlay
               view-size="big"
               :file-id="file.file_id"
+              :file-url="file.file_url"
               :cur-video-index="curVideoIndex"
               @changeFile="changeFile"
             />
@@ -58,7 +60,13 @@
             :style="{ height: elementHeight + 'px', transform: `translateY(${translateY}px)` }"
           >
             <li v-for="(file, i) in data.file_list" :key="i" @click="handleAudioClick(i)">
-              <VideoPlay view-size="small" :file-id="file.file_id" :audio-index="i" :cur-video-index="curVideoIndex" />
+              <VideoPlay
+                view-size="small"
+                :file-id="file.file_id"
+                :file-url="file.file_url"
+                :audio-index="i"
+                :cur-video-index="curVideoIndex"
+              />
             </li>
           </ul>
           <button v-if="viewTopBottomBtn" class="arrow top" @click="scroll(1)">

+ 48 - 21
src/views/book/courseware/preview/components/video_interaction/ExercisePreview.vue

@@ -32,7 +32,21 @@ export default {
       modal: false,
     };
   },
-  inject: ['getCoursewareId'],
+  inject: {
+    bookStatus: {
+      default: 'online',
+    },
+    getCoursewareId: {
+      default() {
+        return () => '';
+      },
+    },
+    readFileContent: {
+      default() {
+        return false;
+      },
+    },
+  },
   props: ['exercise_id', 'feed_back'],
   data() {
     return {
@@ -90,27 +104,40 @@ export default {
         this.permissionControl.can_show_answer = true;
       }
     },
-    handleData() {
+    async handleData() {
       this.loading = true;
-      let data = {
-        exercise_id: this.exercise_id,
-        courseware_id: this.getCoursewareId(),
-      };
-      GetCoursewareExerciseView(data)
-        .then((res) => {
-          this.loading = false;
-          if (res.status === 1) {
-            this.content = res.content_exercise;
-            this.title = res.content_title;
-            this.typeValue = res.content_exercise ? JSON.parse(res.content_exercise).type : '';
-            // setTimeout(() => {
-            //   this.$refs.preview.showAnswer(true, false, this.$refs.preview.answer, true);
-            // }, 100);
-          }
-        })
-        .catch(() => {
-          this.loading = false;
-        });
+
+      if (this.readFileContent) {
+        this.loading = false;
+        const content = await this.readFileContent(`EX_${this.getCoursewareId()}/${this.exercise_id}`, 'courseware');
+        if (content) {
+          this.content = content.content_exercise;
+          this.title = content.content_title;
+          this.typeValue = content.content_exercise ? JSON.parse(content.content_exercise).type : '';
+        } else {
+          this.$message.warning('获取练习题数据失败');
+        }
+      } else {
+        let data = {
+          exercise_id: this.exercise_id,
+          courseware_id: this.getCoursewareId(),
+        };
+        GetCoursewareExerciseView(data)
+          .then((res) => {
+            this.loading = false;
+            if (res.status === 1) {
+              this.content = res.content_exercise;
+              this.title = res.content_title;
+              this.typeValue = res.content_exercise ? JSON.parse(res.content_exercise).type : '';
+              // setTimeout(() => {
+              //   this.$refs.preview.showAnswer(true, false, this.$refs.preview.answer, true);
+              // }, 100);
+            }
+          })
+          .catch(() => {
+            this.loading = false;
+          });
+      }
     },
   },
 };

+ 7 - 0
src/views/eep_view/index.vue

@@ -12,11 +12,18 @@ export default {
   components: {
     CommonPreview,
   },
+  provide() {
+    return {
+      tempDir: this.tempDir,
+      bookStatus: this.bookStatus,
+    };
+  },
   data() {
     return {
       filePath: this.$route.query.filePath || '', // 从路由参数获取文件路径
       entries: null, // 压缩包内的条目列表
       tempDir: '', // 临时目录
+      bookStatus: 'offline', // 教材状态:offline 离线 | online 在线
     };
   },
   created() {