jack 2 هفته پیش
والد
کامیت
f6691534de
2فایلهای تغییر یافته به همراه287 افزوده شده و 276 حذف شده
  1. 2 0
      src/views/Screen/ScreenSlide.vue
  2. 285 276
      src/views/components/element/FrameElement/BaseFrameElement.vue

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

@@ -60,6 +60,7 @@ const hasIframe = computed(() => {
 
 // 计算scale:如果是iframe界面且scale大于1就按1,否则按原scale
 const iframeScale = computed(() => {
+  /*
   if (hasIframe.value) {
     // return Math.min(props.scale, 1)
     // return props.scale;
@@ -70,6 +71,7 @@ const iframeScale = computed(() => {
     }
 
   }
+  */
   return {
     width: viewportSize.value + 'px',
     height: viewportSize.value * viewportRatio.value + 'px',

+ 285 - 276
src/views/components/element/FrameElement/BaseFrameElement.vue

@@ -5,307 +5,316 @@
         height: elementInfo.height + 'px',
   
   -->
-    <div class="base-element-frame"
-      :style="{
-        top: elementInfo.top + 'px',
-        left: elementInfo.left + 'px',
-        width: '100%',
-        height: '100%',
-      }"
+  <div
+    class="base-element-frame"
+    :style="{
+      top: elementInfo.top + 'px',
+      left: elementInfo.left + 'px',
+      width: elementInfo.width + 'px',
+      height: elementInfo.height + 'px',
+      overflow: 'hidden',
+    }"
+  >
+    <div
+      class="rotate-wrapper"
+      :style="{ transform: `rotate(${elementInfo.rotate}deg)` }"
     >
-      <div
-        class="rotate-wrapper"
-        :style="{ transform: `rotate(${elementInfo.rotate}deg)` }"
-      >
-        <div class="element-content">
-          <!-- 视频类型(type 74):使用 video 标签 -->
-          <video 
-            v-if="elementInfo.toolType === 74 && !isThumbnail && isVisible"
-            :key="`video-${iframeKey}`"
-            :src="elementInfo.url"
-            :width="elementInfo.width"
-            :height="elementInfo.height"
-            controls
-            :style="{ width: '100%', height: '100%', objectFit: 'contain' }"
-          ></video>
-          <!-- B站视频类型(type 75):使用 iframe -->
-          <iframe 
-            v-else-if="elementInfo.toolType === 75 && !isThumbnail && isVisible"
-            :key="`bilibili-${iframeKey}`"
-            :src="elementInfo.url"
-            :style="{
-              width: '100%',
-              height: '100%',
-            }"
-            :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;"
-            @load="handleIframeLoad"
-          ></iframe>
-          <!-- 延迟加载iframe:只有在可见且不是缩略图时才加载 -->
-          <iframe 
-            :key="`html-${iframeKey}`"
-            :srcdoc="elementInfo.url" 
-            v-else-if="elementInfo.isHTML && !isThumbnail && isVisible"
-            :style="{
-              width: '100%',
-              height: '100%',
-            }"
-            :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;"
-            @load="handleIframeLoad"
-          ></iframe>
-          <iframe 
-            :key="`src-${iframeKey}`"
-            v-else-if="!isThumbnail && isVisible"
-            :src="elementInfo.url"
-            :style="{
-              width: '100%',
-              height: '100%',
-            }"
-            :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;"
-            @load="handleIframeLoad"
-          ></iframe>
-          <!-- 占位符:当不可见时显示 -->
-          <div v-else-if="!isThumbnail && !isVisible" class="iframe-placeholder">
-            <div class="placeholder-content">
-              <div class="placeholder-icon">🌐</div>
-              <div class="placeholder-text">{{ lang.ssInteract }}</div>
-              <div class="placeholder-type">({{ getTypeLabel(Number(elementInfo.toolType)) }})</div>
+      <div class="element-content">
+        <!-- 视频类型(type 74):使用 video 标签 -->
+        <video
+          v-if="elementInfo.toolType === 74 && !isThumbnail && isVisible"
+          :key="`video-${iframeKey}`"
+          :src="elementInfo.url"
+          :width="elementInfo.width"
+          :height="elementInfo.height"
+          controls
+          :style="{ width: '100%', height: '100%', objectFit: 'contain' }"
+        ></video>
+        <!-- B站视频类型(type 75):使用 iframe -->
+        <iframe
+          v-else-if="elementInfo.toolType === 75 && !isThumbnail && isVisible"
+          :key="`bilibili-${iframeKey}`"
+          :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;"
+          @load="handleIframeLoad"
+        ></iframe>
+        <!-- 延迟加载iframe:只有在可见且不是缩略图时才加载 -->
+        <iframe
+          :key="`html-${iframeKey}`"
+          :srcdoc="elementInfo.url"
+          v-else-if="elementInfo.isHTML && !isThumbnail && isVisible"
+          :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;"
+          @load="handleIframeLoad"
+        ></iframe>
+        <iframe
+          :key="`src-${iframeKey}`"
+          v-else-if="!isThumbnail && isVisible"
+          :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;"
+          @load="handleIframeLoad"
+        ></iframe>
+        <!-- 占位符:当不可见时显示 -->
+        <div v-else-if="!isThumbnail && !isVisible" class="iframe-placeholder">
+          <div class="placeholder-content">
+            <div class="placeholder-icon">🌐</div>
+            <div class="placeholder-text">{{ lang.ssInteract }}</div>
+            <div class="placeholder-type">
+              ({{ getTypeLabel(Number(elementInfo.toolType)) }})
             </div>
           </div>
-          <!-- 缩略图模式 -->
-          <div v-else-if="isThumbnail" class="thumbnail-content">
-            <div class="thumbnail-content-inner">
-              <div>{{ lang.ssInteract }}</div>
-              <div>({{ getTypeLabel(Number(elementInfo.toolType)) }})</div>
-            </div>
+        </div>
+        <!-- 缩略图模式 -->
+        <div v-else-if="isThumbnail" class="thumbnail-content">
+          <div class="thumbnail-content-inner">
+            <div>{{ lang.ssInteract }}</div>
+            <div>({{ getTypeLabel(Number(elementInfo.toolType)) }})</div>
           </div>
-          <!-- 在放映模式下不显示遮罩层,允许用户与iframe交互 -->
-          <div class="mask" v-if="false"></div>
         </div>
+        <!-- 在放映模式下不显示遮罩层,允许用户与iframe交互 -->
+        <div class="mask" v-if="false"></div>
       </div>
     </div>
-  </template>
+  </div>
+</template>
   
   <script lang="ts" setup>
-  import type { PropType } from 'vue'
-  import type { PPTFrameElement } from '@/types/slides'
-  import { lang } from '@/main'
-  import { ref, watch, nextTick } from 'vue'
-  
-  const props = defineProps({
-    elementInfo: {
-      type: Object as PropType<PPTFrameElement>,
-      required: true,
-    },
-    isThumbnail: {
-      type: Boolean,
-      default: false,
-    },
-    isVisible: {
-      type: Boolean,
-      default: false,
-    },
-  })
-  
-  // 用于强制刷新iframe的key
-  const iframeKey = ref(0)
-  
-  // 监听elementInfo.url的变化
-  watch(() => props.elementInfo.url, (newUrl, oldUrl) => {
+import type { PropType } from "vue";
+import type { PPTFrameElement } from "@/types/slides";
+import { lang } from "@/main";
+import { ref, watch, nextTick } from "vue";
+
+const props = defineProps({
+  elementInfo: {
+    type: Object as PropType<PPTFrameElement>,
+    required: true,
+  },
+  isThumbnail: {
+    type: Boolean,
+    default: false,
+  },
+  isVisible: {
+    type: Boolean,
+    default: false,
+  },
+});
+
+// 用于强制刷新iframe的key
+const iframeKey = ref(0);
+
+// 监听elementInfo.url的变化
+watch(
+  () => props.elementInfo.url,
+  (newUrl, oldUrl) => {
     if (newUrl !== oldUrl) {
       // 通过改变key来强制刷新iframe
-      iframeKey.value++
-    }
-  })
-  
-  // 获取类型标签
-  const getTypeLabel = (type: number): string => {
-    const typeMap: Record<number, keyof typeof lang> = {
-      45: 'ssChoiceQ',
-      15: 'ssEssayQ',
-      72: 'ssAIApp',
-      73: 'ssH5Page',
-      74: 'ssVideo',
-      75: lang.lang == 'cn' ? 'ssBiliVideo' : 'ssYouTube',
-      76: 'ssCreateSpace'
+      iframeKey.value++;
     }
-    const key = typeMap[type]
-    return (key ? lang[key] : lang.ssUnknown) as string
   }
-  
-  // 处理iframe加载完成事件
-  const handleIframeLoad = async (event: Event) => {
-    const iframe = event.target as HTMLIFrameElement
-    
-    try {
-      // 等待iframe完全加载
-      await nextTick()
-      setTimeout(async () => {
-        // 检查iframe是否可访问(同源检查)
-        if (iframe.contentWindow && iframe.contentDocument) {
-          const iframeDoc = iframe.contentDocument
-          const iframeHead = iframeDoc.head || iframeDoc.getElementsByTagName('head')[0]
-          
-          if (iframeHead) {
-            // 使用动态导入获取JS文件内容
-            const jsFiles = [
-              { id: 'aws-sdk', importPath: () => import('./aws-sdk-2.235.1.min.js?raw') },
-              { id: 'jquery', importPath: () => import('./jquery-3.6.0.min.js?raw') },
-              { id: 'jietu', importPath: () => import('./jietu.js?raw') }
-            ]
-            
-            for (const jsFile of jsFiles) {
-              try {
-                // 检查是否已经注入过
-                if (!iframeDoc.getElementById(jsFile.id)) {
-                  const jsModule = await jsFile.importPath()
-                  const jsContent = jsModule.default || jsModule
-                  
-                  const scriptElement = iframeDoc.createElement('script')
-                  scriptElement.id = jsFile.id
-                  scriptElement.textContent = jsContent
-                  iframeHead.appendChild(scriptElement)
-                  
-                  console.log(`已注入 ${jsFile.id} 到iframe中`)
-                }
-              }
-              catch (fetchError) {
-                console.error(`获取 ${jsFile.id} 失败:`, fetchError)
+);
+
+// 获取类型标签
+const getTypeLabel = (type: number): string => {
+  const typeMap: Record<number, keyof typeof lang> = {
+    45: "ssChoiceQ",
+    15: "ssEssayQ",
+    72: "ssAIApp",
+    73: "ssH5Page",
+    74: "ssVideo",
+    75: lang.lang == "cn" ? "ssBiliVideo" : "ssYouTube",
+    76: "ssCreateSpace",
+  };
+  const key = typeMap[type];
+  return (key ? lang[key] : lang.ssUnknown) as string;
+};
+
+// 处理iframe加载完成事件
+const handleIframeLoad = async (event: Event) => {
+  const iframe = event.target as HTMLIFrameElement;
+
+  try {
+    // 等待iframe完全加载
+    await nextTick();
+    setTimeout(async () => {
+      // 检查iframe是否可访问(同源检查)
+      if (iframe.contentWindow && iframe.contentDocument) {
+        const iframeDoc = iframe.contentDocument;
+        const iframeHead =
+          iframeDoc.head || iframeDoc.getElementsByTagName("head")[0];
+
+        if (iframeHead) {
+          // 使用动态导入获取JS文件内容
+          const jsFiles = [
+            {
+              id: "aws-sdk",
+              importPath: () => import("./aws-sdk-2.235.1.min.js?raw"),
+            },
+            {
+              id: "jquery",
+              importPath: () => import("./jquery-3.6.0.min.js?raw"),
+            },
+            { id: "jietu", importPath: () => import("./jietu.js?raw") },
+          ];
+
+          for (const jsFile of jsFiles) {
+            try {
+              // 检查是否已经注入过
+              if (!iframeDoc.getElementById(jsFile.id)) {
+                const jsModule = await jsFile.importPath();
+                const jsContent = jsModule.default || jsModule;
+
+                const scriptElement = iframeDoc.createElement("script");
+                scriptElement.id = jsFile.id;
+                scriptElement.textContent = jsContent;
+                iframeHead.appendChild(scriptElement);
+
+                console.log(`已注入 ${jsFile.id} 到iframe中`);
               }
+            } catch (fetchError) {
+              console.error(`获取 ${jsFile.id} 失败:`, fetchError);
             }
-            
-            // 可选:在iframe中执行一些初始化代码
-            try {
-              iframe.contentWindow.eval(`
+          }
+
+          // 可选:在iframe中执行一些初始化代码
+          try {
+            iframe.contentWindow.eval(`
                 console.log('iframe中的JS环境已准备就绪');
                 // 这里可以添加一些初始化代码
-              `)
-            }
-            catch (evalError) {
-              console.warn('无法在iframe中执行代码:', evalError)
-            }
+              `);
+          } catch (evalError) {
+            console.warn("无法在iframe中执行代码:", evalError);
           }
         }
-        else {
-          console.warn('无法访问iframe内容,可能是跨域限制')
-        }
-      }, 1000)
-    }
-    catch (error) {
-      console.error('注入JS到iframe失败:', error)
-    }
+      } else {
+        console.warn("无法访问iframe内容,可能是跨域限制");
+      }
+    }, 1000);
+  } catch (error) {
+    console.error("注入JS到iframe失败:", error);
   }
-  </script>
+};
+</script>
   
   <style lang="scss" scoped>
-  .base-element-frame {
-    position: absolute;
-  }
-  .element-content {
-    width: 100%;
-    height: 100%;
-    overflow: hidden;
-    
-    video {
-      width: 100%;
-      height: 100%;
-      object-fit: contain;
-      background-color: #000;
-    }
-  }
-  .mask {
-    position: absolute;
-    top: 0;
-    bottom: 0;
-    left: 0;
-    right: 0;
-  }
-  .rotate-wrapper {
-    width: 100%;
-    height: 100%;
-    overflow: hidden;
-  }
-  .thumbnail-content {
-    width: 100%;
-    height: 100%;
-    background-color: #fff;
-  }
-  
-  .thumbnail-content-inner {
+.base-element-frame {
+  position: absolute;
+}
+.element-content {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+
+  video {
     width: 100%;
     height: 100%;
-    color: #3681fc;
-    font-size: 110px;
-    font-weight: 600;
-    text-align: center;
-    line-height: 100%;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    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;
+    object-fit: contain;
+    background-color: #000;
   }
-  
-  .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;
+}
+.mask {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+}
+.rotate-wrapper {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+.thumbnail-content {
+  width: 100%;
+  height: 100%;
+  background-color: #fff;
+}
+
+.thumbnail-content-inner {
+  width: 100%;
+  height: 100%;
+  color: #3681fc;
+  font-size: 110px;
+  font-weight: 600;
+  text-align: center;
+  line-height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  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%;
-    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%;
-    }
+  100% {
+    left: 100%;
   }
-  </style>
+}
+</style>