lsc преди 3 дни
родител
ревизия
40884a24c6

+ 1 - 0
src/views/Screen/BaseView.vue

@@ -10,6 +10,7 @@
       @touchstart="$event => touchStartListener($event)"
       @touchend="$event => touchEndListener($event)"
       v-contextmenu="contextmenus"
+      :slideIndex="slideIndex"
     />
 
     <SlideThumbnails 

+ 1 - 0
src/views/Screen/PresenterView.vue

@@ -30,6 +30,7 @@
           @touchstart="$event => touchStartListener($event)"
           @touchend="$event => touchEndListener($event)"
           v-contextmenu="contextmenus"
+          :slideIndex="slideIndex"
         />
         <WritingBoardTool 
           :slideWidth="slideWidth"

+ 2 - 0
src/views/Screen/ScreenElement.vue

@@ -15,6 +15,7 @@
     <component
       :is="currentElementComponent"
       :elementInfo="elementInfo"
+      :is-visible="isVisible"
     ></component>
   </div>
 </template>
@@ -42,6 +43,7 @@ const props = defineProps<{
   animationIndex: number
   turnSlideToId: (id: string) => void
   manualExitFullscreen: () => void
+  isVisible: boolean
 }>()
 
 const currentElementComponent = computed<unknown>(() => {

+ 2 - 0
src/views/Screen/ScreenSlide.vue

@@ -16,6 +16,7 @@
       :animationIndex="animationIndex"
       :turnSlideToId="turnSlideToId"
       :manualExitFullscreen="manualExitFullscreen"
+      :is-visible="isVisible"
     />
   </div>
 </template>
@@ -36,6 +37,7 @@ const props = defineProps<{
   animationIndex: number
   turnSlideToId: (id: string) => void
   manualExitFullscreen: () => void
+  isVisible: boolean
 }>()
 
 const { viewportRatio, viewportSize } = storeToRefs(useSlidesStore())

+ 13 - 11
src/views/Screen/ScreenSlideList.vue

@@ -32,6 +32,7 @@
           :animationIndex="animationIndex"
           :turnSlideToId="turnSlideToId"
           :manualExitFullscreen="manualExitFullscreen"
+          :is-visible="slideIndex === index"
         />
       </div>
     </div>
@@ -39,13 +40,13 @@
 </template>
 
 <script lang="ts" setup>
-import { computed, provide } from "vue";
-import { storeToRefs } from "pinia";
-import { useSlidesStore } from "@/store";
-import { injectKeySlideScale } from "@/types/injectKey";
-import useSlidesWithTurningMode from "./hooks/useSlidesWithTurningMode";
+import { computed, provide } from 'vue'
+import { storeToRefs } from 'pinia'
+import { useSlidesStore } from '@/store'
+import { injectKeySlideScale } from '@/types/injectKey'
+import useSlidesWithTurningMode from './hooks/useSlidesWithTurningMode'
 
-import ScreenSlide from "./ScreenSlide.vue";
+import ScreenSlide from './ScreenSlide.vue'
 
 const props = defineProps<{
   slideWidth: number;
@@ -53,14 +54,15 @@ const props = defineProps<{
   animationIndex: number;
   turnSlideToId: (id: string) => void;
   manualExitFullscreen: () => void;
-}>();
+  slideIndex: number;
+}>()
 
-const { slideIndex, viewportSize } = storeToRefs(useSlidesStore());
+const { slideIndex, viewportSize } = storeToRefs(useSlidesStore())
 
-const { slidesWithTurningMode } = useSlidesWithTurningMode();
+const { slidesWithTurningMode } = useSlidesWithTurningMode()
 
-const scale = computed(() => props.slideWidth / viewportSize.value);
-provide(injectKeySlideScale, scale);
+const scale = computed(() => props.slideWidth / viewportSize.value)
+provide(injectKeySlideScale, scale)
 </script>
 
 <style lang="scss" scoped>

+ 4 - 4
src/views/Student/index.vue

@@ -79,11 +79,11 @@
 
             <ScreenElement v-for="(element, index) in elementList" :key="element.id" :elementInfo="element"
               :elementIndex="index + 1" :animationIndex="0" :turnSlideToId="() => { }"
-              :manualExitFullscreen="() => { }" />
+              :manualExitFullscreen="() => { }" :is-visible="slideIndex === index" />
           </div>
 
           <ScreenSlideList :slideWidth="slideWidth * canvasScale" :slideHeight="slideHeight * canvasScale"
-            :animationIndex="0" :turnSlideToId="() => { }" :manualExitFullscreen="() => { }" />
+            :animationIndex="0" :turnSlideToId="() => { }" :manualExitFullscreen="() => { }"  :slideIndex="slideIndex"/>
         </div>
         <!-- 全屏时的左右下角工具按钮 -->
         <div v-if="isFullscreen && (!isFollowModeActive || props.type == '1')" class="tools-left">
@@ -1136,7 +1136,7 @@ const handleHomeworkSubmit = async () => {
             comments.forEach(comment => {
               comment?.parentNode?.removeChild(comment)
             })
-            
+
             iframeBody = iframeElement.contentWindow.document.body as HTMLElement
           }
           else {
@@ -1217,7 +1217,7 @@ const handleHomeworkSubmit = async () => {
                 ctx.fillText('iframe内容', canvas.width / 2, canvas.height / 2 + 20)
                 
                 // 绘制URL
-                const src = iframeElement.src
+                const src = iframeElement.srcf
                 if (src) {
                   ctx.font = '14px Arial'
                   ctx.fillStyle = '#6c757d'

+ 74 - 23
src/views/components/element/FrameElement/BaseFrameElement.vue

@@ -12,30 +12,11 @@
       :style="{ transform: `rotate(${elementInfo.rotate}deg)` }"
     >
       <div class="element-content">
-        <!-- <iframe 
-          :key="`html-${iframeKey}`"
-          :srcdoc="elementInfo.url" 
-          v-if="elementInfo.isHTML"
-          :width="elementInfo.width"
-          :height="elementInfo.height"
-          :frameborder="0" 
-          :allowfullscreen="true"
-          allow="camera *; microphone *; display-capture; midi; encrypted-media; fullscreen; geolocation; clipboard-read; clipboard-write; accelerometer; autoplay; gyroscope; payment; picture-in-picture; usb; xr-spatial-tracking;"
-        ></iframe>
-        <iframe 
-          :key="`src-${iframeKey}`"
-          v-else
-          :src="elementInfo.url"
-          :width="elementInfo.width"
-          :height="elementInfo.height"
-          :frameborder="0" 
-          :allowfullscreen="true"
-          allow="camera *; microphone *; display-capture; midi; encrypted-media; fullscreen; geolocation; clipboard-read; clipboard-write; accelerometer; autoplay; gyroscope; payment; picture-in-picture; usb; xr-spatial-tracking;"
-        ></iframe> -->
+        <!-- 延迟加载iframe:只有在可见且不是缩略图时才加载 -->
         <iframe 
           :key="`html-${iframeKey}`"
           :srcdoc="elementInfo.url" 
-          v-if="elementInfo.isHTML && !isThumbnail"
+          v-if="elementInfo.isHTML && !isThumbnail && isVisible"
           :width="elementInfo.width"
           :height="elementInfo.height"
           :frameborder="0" 
@@ -44,7 +25,7 @@
         ></iframe>
         <iframe 
           :key="`src-${iframeKey}`"
-          v-else-if="!isThumbnail"
+          v-else-if="!isThumbnail && isVisible"
           :src="elementInfo.url"
           :width="elementInfo.width"
           :height="elementInfo.height"
@@ -52,6 +33,15 @@
           :allowfullscreen="true"
           allow="camera *; microphone *; display-capture; midi; encrypted-media; fullscreen; geolocation; clipboard-read; clipboard-write; accelerometer; autoplay; gyroscope; payment; picture-in-picture; usb; xr-spatial-tracking;"
         ></iframe>
+        <!-- 占位符:当不可见时显示 -->
+        <div v-else-if="!isThumbnail && !isVisible" class="iframe-placeholder">
+          <div class="placeholder-content">
+            <div class="placeholder-icon">🌐</div>
+            <div class="placeholder-text">互动工具</div>
+            <div class="placeholder-type">({{ getTypeLabel(Number(elementInfo.toolType)) }})</div>
+          </div>
+        </div>
+        <!-- 缩略图模式 -->
         <div v-else-if="isThumbnail" class="thumbnail-content">
           <div class="thumbnail-content-inner">
             <div>互动工具</div>
@@ -79,6 +69,10 @@ const props = defineProps({
     type: Boolean,
     default: false,
   },
+  isVisible: {
+    type: Boolean,
+    default: false,
+  },
 })
 
 // 用于强制刷新iframe的key
@@ -131,7 +125,6 @@ const getTypeLabel = (type: number) => {
   background-color: #fff;
 }
 
-
 .thumbnail-content-inner {
   width: 100%;
   height: 100%;
@@ -146,4 +139,62 @@ const getTypeLabel = (type: number) => {
   flex-direction: column;
   gap: 50px;
 }
+
+/* iframe占位符样式 */
+.iframe-placeholder {
+  width: 100%;
+  height: 100%;
+  background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
+  border: 2px solid #dee2e6;
+  border-radius: 8px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  position: relative;
+  overflow: hidden;
+}
+
+.placeholder-content {
+  text-align: center;
+  color: #6c757d;
+  font-family: Arial, sans-serif;
+}
+
+.placeholder-icon {
+  font-size: 48px;
+  margin-bottom: 12px;
+  opacity: 0.7;
+}
+
+.placeholder-text {
+  font-size: 16px;
+  font-weight: 600;
+  margin-bottom: 4px;
+}
+
+.placeholder-type {
+  font-size: 12px;
+  opacity: 0.8;
+}
+
+/* 添加加载动画效果 */
+.iframe-placeholder::before {
+  content: '';
+  position: absolute;
+  top: 0;
+  left: -100%;
+  width: 100%;
+  height: 100%;
+  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
+  animation: shimmer 2s infinite;
+}
+
+@keyframes shimmer {
+  0% {
+    left: -100%;
+  }
+  100% {
+    left: 100%;
+  }
+}
 </style>