Browse Source

预览优化

dsy 1 week ago
parent
commit
376aafb783

+ 50 - 61
src/views/book/courseware/preview/components/3d_model/3DModelPreview.vue

@@ -25,7 +25,6 @@ import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
 
 import { get3DModelData } from '@/views/book/courseware/data/3dModel';
 import { toggleFullScreen } from '@/utils/common';
-import { GetFileStoreInfo } from '@/api/app';
 
 export default {
   name: 'ThreeModelPreview',
@@ -33,11 +32,12 @@ export default {
   data() {
     return {
       data: get3DModelData(),
-      scene: null,
-      camera: null,
-      renderer: null,
-      controls: null,
-      animationId: null,
+      scene: null, // 三维场景
+      camera: null, // 相机
+      mixer: null, // 动画混合器
+      renderer: null, // 渲染器
+      controls: null, // 轨道控制器
+      animationId: null, // 动画帧ID
       isFullScreen: false, // 是否全屏
       loaded: false, // 是否加载完成
     };
@@ -81,40 +81,40 @@ export default {
       // 创建相机
       const width = container.clientWidth;
       const height = container.clientHeight;
-      this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
-      this.camera.position.set(5, 5, 5);
+      this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000); // 视角75度
+      this.camera.position.set(5, 5, 5); // 设置相机位置
 
       // 创建渲染器
-      this.renderer = new THREE.WebGLRenderer({ antialias: true });
+      this.renderer = new THREE.WebGLRenderer({ antialias: true }); // 开启抗锯齿
       this.renderer.setSize(width, height);
-      this.renderer.shadowMap.enabled = true;
-      this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
+      this.renderer.shadowMap.enabled = true; // 启用阴影映射
+      this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 使用柔和阴影
 
       // 启用颜色管理和正确的色彩空间
-      this.renderer.outputColorSpace = THREE.SRGBColorSpace;
-      this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
-      this.renderer.toneMappingExposure = 1.0;
+      this.renderer.outputColorSpace = THREE.SRGBColorSpace; // 设置输出色彩空间
+      this.renderer.toneMapping = THREE.ACESFilmicToneMapping; // 使用ACES电影色调映射
+      this.renderer.toneMappingExposure = 1.0; // 曝光度
 
-      container.appendChild(this.renderer.domElement);
+      container.appendChild(this.renderer.domElement); // 将渲染器添加到DOM
 
       // 添加光源
-      const ambientLight = new THREE.AmbientLight(0x404040, 1.0); // 增加环境光强度
-      this.scene.add(ambientLight);
+      const ambientLight = new THREE.AmbientLight(0xffffff); // 环境光
+      this.scene.add(ambientLight); // 添加环境光到场景
 
       const directionalLight = new THREE.DirectionalLight(0xffffff, 1.0); // 增加方向光强度
-      directionalLight.position.set(10, 10, 5);
-      directionalLight.castShadow = true;
-      this.scene.add(directionalLight);
+      directionalLight.position.set(10, 10, 5); // 设置光源位置
+      directionalLight.castShadow = true; // 启用阴影投射
+      this.scene.add(directionalLight); // 添加方向光到场景
 
       // 添加额外的填充光
-      const fillLight = new THREE.DirectionalLight(0xffffff, 0.5);
-      fillLight.position.set(-10, -10, -5);
-      this.scene.add(fillLight);
+      const fillLight = new THREE.DirectionalLight(0xffffff, 0.5); // 较低强度的填充光
+      fillLight.position.set(-10, -10, -5); // 设置填充光位置
+      this.scene.add(fillLight); // 添加填充光到场景
 
       // 添加控制器
       this.controls = new OrbitControls(this.camera, this.renderer.domElement);
-      this.controls.enableDamping = true;
-      this.controls.dampingFactor = 0.25;
+      this.controls.enableDamping = true; // 启用阻尼效果
+      this.controls.dampingFactor = 0.25; // 阻尼系数
 
       // 开始渲染循环
       this.animate();
@@ -128,12 +128,11 @@ export default {
         return;
       }
 
-      const fileStore = await GetFileStoreInfo({ file_id: this.data.model_list[0].file_id });
-      if (!fileStore || fileStore.error) {
-        console.error('模型文件加载失败:', fileStore);
+      const modelUrl = this.data.file_list[0].file_url;
+      if (!modelUrl) {
+        console.error('模型文件URL不存在');
         return;
       }
-      const modelUrl = fileStore.file_url;
       // 根据文件扩展名选择合适的加载器
       const extension = modelUrl.split('.').pop().toLowerCase();
       let loader = null;
@@ -148,9 +147,9 @@ export default {
           loader = new FBXLoader();
           this.loadFBX(loader, modelUrl);
           break;
-        case 'obj':
+        case 'zip':
           loader = new OBJLoader();
-          this.loadOBJ(loader, modelUrl);
+          this.loadOBJ(loader, this.data.obj_info.obj_file_url);
           break;
         default:
           console.error('不支持的模型格式:', extension);
@@ -240,16 +239,13 @@ export default {
 
     async loadOBJ(loader, url) {
       // 首先尝试加载MTL材质文件
-      let mtlUrl = '';
-      const mtlLoader = new MTLLoader();
-      if (this.data.model_list.length > 1) {
-        const fileStore = await GetFileStoreInfo({ file_id: this.data.model_list[1].file_id });
-        if (!fileStore || fileStore.error) {
-          console.error('mtl 材质文件加载失败:', fileStore);
-        } else {
-          mtlUrl = fileStore.file_url;
-        }
+      let mtlUrl = this.data.mtl_info.mtl_file_url;
+      if (!mtlUrl) {
+        // 如果没有提供MTL文件URL,直接加载OBJ模型
+        this.loadOBJModel(loader, url);
+        return;
       }
+      const mtlLoader = new MTLLoader();
 
       // 设置材质加载路径
       const basePath = mtlUrl.substring(0, mtlUrl.lastIndexOf('/') + 1);
@@ -262,10 +258,9 @@ export default {
       mtlLoader.load(
         mtlFileName,
         (materials) => {
-          // 材质文件加载成功
-          materials.preload();
-          loader.setMaterials(materials);
-          this.loadOBJModel(loader, url, true);
+          materials.preload(); // 预加载
+          loader.setMaterials(materials); // 将材质应用到OBJ加载器
+          this.loadOBJModel(loader, url);
         },
         undefined,
         (error) => {
@@ -280,26 +275,11 @@ export default {
      * 加载OBJ模型并应用默认材质
      * @param {OBJLoader} loader - OBJ加载器
      * @param {string} url - OBJ文件URL
-     * @param {boolean} hasMaterials - 是否有材质文件
      */
-    loadOBJModel(loader, url, hasMaterials = false) {
+    loadOBJModel(loader, url) {
       loader.load(
         url,
         (obj) => {
-          let colorList = [0xff6666, 0x66ff66, 0x6666ff, 0xffff66, 0x66ffff, 0xff66ff]; // 可自定义颜色列表
-          let meshIndex = 0;
-          obj.traverse((child) => {
-            if (child.isMesh && !hasMaterials) {
-              // 为每个 mesh 设置自定义颜色材质
-              child.material = new THREE.MeshPhongMaterial({
-                color: colorList[meshIndex % colorList.length], // 循环使用颜色
-                shininess: 30,
-              });
-              meshIndex += 1;
-              child.castShadow = true;
-              child.receiveShadow = true;
-            }
-          });
           this.addModelToScene(obj);
         },
         (progress) => {
@@ -313,6 +293,10 @@ export default {
       );
     },
 
+    /**
+     * 将模型添加到场景中并调整相机位置
+     * @param {THREE.Object3D} model - 3D模型对象
+     */
     addModelToScene(model) {
       // 计算模型的包围盒,用于自动调整相机位置
       const box = new THREE.Box3().setFromObject(model);
@@ -353,6 +337,9 @@ export default {
       }
     },
 
+    /**
+     * 窗口大小变化处理函数
+     */
     onWindowResize() {
       const container = this.$refs.three;
       this.isFullScreen = document.fullscreenElement !== null;
@@ -403,6 +390,9 @@ export default {
       }
     },
 
+    /**
+     * 切换全屏模式
+     */
     handleToggleFullScreen() {
       if (!this.$refs.three) return;
       if (!this.loaded) {
@@ -410,7 +400,6 @@ export default {
         return;
       }
       toggleFullScreen(this.$refs.three);
-      // 延迟一帧来确保DOM已经更新
       this.$nextTick(() => {
         setTimeout(() => {
           this.onWindowResize();

+ 0 - 3
src/views/book/courseware/preview/components/article/Voicefullscreen.vue

@@ -819,9 +819,6 @@ export default {
         } else if (e.keyCode === 40) {
           this.nextSentence();
         } else if (e.keyCode === 13) {
-          this.$nextTick(() => {
-            this.$refs.Soundrecorddiff.microphone();
-          });
         }
       }
     });

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

@@ -897,4 +897,4 @@ export default {
     width: auto !important;
   }
 }
-</style>
+</style>

+ 34 - 0
src/views/book/courseware/preview/components/common/AudioPlay.vue

@@ -36,6 +36,21 @@
         </div>
       </div>
     </div>
+    <!-- <div v-else-if="'middle' === viewSize" class="audio-middle">
+      <div v-if="fileNameDisplay == 'true'" class="audio-name">{{ audioIndex + 1 }}. {{ fileName }}</div>
+      <div class="slider-area">
+        <SvgIcon :icon-class="iconClass" size="18" :color="topicColor" @click="playAudio" />
+        <span class="audio-time">{{ secondFormatConversion(audio.current_time) }}</span>
+        <el-slider
+          v-model="play_value"
+          class="audio-slider"
+          :format-tooltip="formatProcessToolTip"
+          :style="{ '--slider-color': topicColor }"
+          @change="changeCurrentTime"
+        />
+        <span class="audio-time">{{ audio_allTime }}</span>
+      </div>
+    </div> -->
     <div v-else-if="'middle' === viewSize" class="audio-middle">
       <div v-if="fileNameDisplay == 'true'" class="audio-name">{{ audioIndex + 1 }}. {{ fileName }}</div>
       <span class="icon-box" :style="{ borderColor: topicColor }">
@@ -297,6 +312,21 @@ export default {
     }
   }
 
+  .audio-middle1 {
+    width: 280px;
+    padding: 12px 16px;
+    border-radius: 4px;
+
+    .audio-name {
+      padding: 8px;
+      margin-bottom: 8px;
+      font-size: 14px;
+      text-align: left;
+      background-color: #eee;
+      border-radius: 4px;
+    }
+  }
+
   .audio-middle {
     display: flex;
     flex-direction: column;
@@ -323,6 +353,9 @@ export default {
       border: 4px solid #076aff;
       border-radius: 20px;
 
+      // .svg-icon {
+      //   color: #fff;
+      // }
     }
   }
 
@@ -351,6 +384,7 @@ export default {
     padding: 8px 24px 8px 16px;
 
     &.active {
+      //background-color: #ebb572;
       background-color: var(--assist-color, #f4f9ff);
     }
 

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

@@ -102,7 +102,6 @@ const mixin = {
     getAnswer() {
       return this.answer;
     },
-
     /**
      * 显示答案
      * @param {boolean} isJudgingRightWrong 是否判断对错

+ 18 - 2
src/views/book/courseware/preview/components/dialogue_article/PhraseModelChs.vue

@@ -210,6 +210,10 @@
                               >
                               <span
                                 class="NNPE-chs"
+                                :class="[
+                                  newWordList.indexOf(item.wordsList[pIndex + 1].chs) > -1 ? 'newActive' : '',
+                                  item.wordsList[pIndex + 1].words ? 'newActive' : '',
+                                ]"
                                 style="text-align: left"
                                 :style="{
                                   fontFamily: item.wordsList[pIndex + 1].config.fontFamily,
@@ -217,7 +221,6 @@
                                   borderBottom:
                                     item.wordsList[pIndex + 1].config.border === 'dotted' ? '1px dotted' : '',
                                   fontWeight: item.wordsList[pIndex + 1].config.fontWeight,
-                                  color: item.wordsList[pIndex + 1].config.color,
                                   height:
                                     attrib && attrib.font_size
                                       ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
@@ -227,6 +230,12 @@
                                     attrib && attrib.font_size
                                       ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
                                       : '28px',
+                                  color:
+                                    newWordList.indexOf(pItem.chs) > -1 || pItem.words
+                                      ? attrib
+                                        ? attrib.topic_color
+                                        : item.wordsList[pIndex + 1].config.color
+                                      : item.wordsList[pIndex + 1].config.color,
                                 }"
                                 @click.stop="
                                   viewNotes(
@@ -304,6 +313,8 @@
                                   curTime <= item.timeList[pItem.sentIndex].ed
                                     ? 'wordActive'
                                     : '',
+                                  newWordList.indexOf(item.wordsList[pIndex + 2].chs) > -1 ? 'newActive' : '',
+                                  item.wordsList[pIndex + 2].words ? 'newActive' : '',
                                 ]"
                                 :style="{
                                   fontFamily: item.wordsList[pIndex + 2].config.fontFamily,
@@ -311,7 +322,6 @@
                                   borderBottom:
                                     item.wordsList[pIndex + 2].config.border === 'dotted' ? '1px dotted' : '',
                                   fontWeight: item.wordsList[pIndex + 2].config.fontWeight,
-                                  color: item.wordsList[pIndex + 2].config.color,
                                   height:
                                     attrib && attrib.font_size
                                       ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
@@ -321,6 +331,12 @@
                                     attrib && attrib.font_size
                                       ? attrib.font_size.replace('pt', '') * 1.4 + 'pt'
                                       : '28px',
+                                  color:
+                                    newWordList.indexOf(pItem.chs) > -1 || pItem.words
+                                      ? attrib
+                                        ? attrib.topic_color
+                                        : item.wordsList[pIndex + 2].config.color
+                                      : item.wordsList[pIndex + 2].config.color,
                                 }"
                                 @click.stop="
                                   viewNotes(

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

@@ -14,7 +14,7 @@
             :style="{ borderColor: data.unified_attrib?.topic_color }"
             :class="['option-content', computedIsJudgeRight(mark)]"
           >
-            <span class="serial-number">{{ convertNumberToLetter(i) }}.</span>
+            <span class="serial-number">{{ computedOptionNumber(i) }}.</span>
             <PinyinText
               v-if="isEnable(data.property.view_pinyin)"
               class="content"
@@ -60,9 +60,11 @@
 </template>
 
 <script>
-import { getJudgeData, option_type_list, isEnable } from '@/views/book/courseware/data/judge';
 import PreviewMixin from '../common/PreviewMixin';
 
+import { getJudgeData, option_type_list, isEnable } from '@/views/book/courseware/data/judge';
+import { serialNumberTypeList, computeOptionMethods } from '@/views/book/courseware/data/common';
+
 export default {
   name: 'JudgePreview',
   mixins: [PreviewMixin],
@@ -104,9 +106,10 @@ export default {
     },
   },
   methods: {
-    // 将数字转换为小写字母
-    convertNumberToLetter(number) {
-      return String.fromCharCode(97 + number);
+    computedOptionNumber(number) {
+      let type = serialNumberTypeList.find((item) => item.value === this.data.property.option_serial_type)?.value;
+      if (!type) return number + 1;
+      return computeOptionMethods[type](number);
     },
 
     isAnswer(mark, option_type) {

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

@@ -212,7 +212,7 @@
                     </p>
                   </template>
                 </div>
-                <span v-if="showLang" class="multilingual">
+                <span v-if="showLang" class="multilingual" :style="[tdStyle, computedRichStyle(col.content)]">
                   {{
                     multilingualTextList[getLang()] &&
                     multilingualTextList[getLang()][i] &&
@@ -308,22 +308,20 @@ export default {
       this.data.col_width.forEach((item) => {
         this.table_width += Number(item.value);
       });
-      if (this.showLang) {
-        this.data.multilingual.forEach((item) => {
-          let trans_arr = item.translation.split('\n');
-          let chunkSize = this.data.property.column_count;
-          let chunkedArr = trans_arr.reduce((acc, curr, index) => {
-            // 当索引是chunkSize的倍数时,开始一个新的子数组
-            if (index % chunkSize === 0) {
-              acc.push([curr]); // 开始新的子数组并添加当前元素
-            } else {
-              acc[acc.length - 1].push(curr); // 将当前元素添加到最后一个子数组中
-            }
-            return acc;
-          }, []);
-          this.$set(this.multilingualTextList, item.type, chunkedArr);
-        });
-      }
+      this.data.multilingual.forEach((item) => {
+        let trans_arr = item.translation.split('\n');
+        let chunkSize = this.data.property.column_count;
+        let chunkedArr = trans_arr.reduce((acc, curr, index) => {
+          // 当索引是chunkSize的倍数时,开始一个新的子数组
+          if (index % chunkSize === 0) {
+            acc.push([curr]); // 开始新的子数组并添加当前元素
+          } else {
+            acc[acc.length - 1].push(curr); // 将当前元素添加到最后一个子数组中
+          }
+          return acc;
+        }, []);
+        this.$set(this.multilingualTextList, item.type, chunkedArr);
+      });
       if (!this.isJudgingRightWrong) {
         this.answer.answer_list = this.data.answer_list;
       }
@@ -450,6 +448,7 @@ $border-color: #e6e6e6;
       align-items: end;
 
       p {
+        flex-grow: 1; // 为了常规模式时设置居中显示撑满整个单元格
         max-width: 100%;
         margin: 0;
       }
@@ -532,6 +531,7 @@ $border-color: #e6e6e6;
     }
 
     .multilingual {
+      display: block;
       word-break: break-word;
     }
 

+ 3 - 4
src/views/book/courseware/preview/components/video/VideoPreview.vue

@@ -183,11 +183,10 @@ export default {
               w = entry.contentRect.width;
               h = entry.contentRect.height - 30;
             }
-            // eslint-disable-next-line no-negated-condition
-            if (this.elementWidth !== w) {
-              this.elementWidth = w;
-            } else {
+            if (this.elementWidth === w) {
               this.elementHeight = h;
+            } else {
+              this.elementWidth = w;
             }
           });
         }