lsc hace 1 semana
padre
commit
80f4dca952

+ 9 - 4
src/hooks/useCreateElement.ts

@@ -316,14 +316,19 @@ export default () => {
    * @param url 网页链接地址
    */
   const createFrameElement = (url: string) => {
+    console.log(viewportSize)
+    const width = viewportSize.value
+    const height = width * viewportRatio.value
     createElement({
       type: 'frame',
       id: nanoid(10),
       url,
-      width: 800,
-      height: 480,
-      left: (viewportSize.value - 800) / 2,
-      top: (viewportSize.value * viewportRatio.value - 480) / 2,
+      width: width,
+      height: height,
+      // left: (viewportSize.value - 800) / 2,
+      // top: (viewportSize.value * viewportRatio.value - 480) / 2,
+      left: 0,
+      top: 0,
       rotate: 0,
     })
   }

+ 17 - 0
src/hooks/useExport.ts

@@ -87,6 +87,23 @@ export default () => {
     saveAs(blob, `${title.value}.json`)
   }
 
+  // 导出JSON文件
+  const exportJSON2 = () => {
+    const json = {
+      title: title.value,
+      width: viewportSize.value,
+      height: viewportSize.value * viewportRatio.value,
+      theme: theme.value,
+      slides: slides.value,
+    }
+    return json
+  }
+
+  // 暴露到window对象
+  if (typeof window !== 'undefined') {
+    (window as any).exportJSON = exportJSON2
+  }
+
   // 格式化颜色值为 透明度 + HexString,供pptxgenjs使用
   const formatColor = (_color: string) => {
     if (!_color) {

+ 40 - 0
src/hooks/useImport.ts

@@ -67,6 +67,45 @@ export default () => {
     reader.readAsText(file)
   }
 
+  // 直接读取JSON功能,暴露到window.readJSON
+  const readJSON = (jsonData: string | any, cover = false) => {
+    try {
+      let slides
+      if (typeof jsonData === 'string') {
+        const parsed = JSON.parse(jsonData)
+        slides = parsed.slides || parsed
+      }
+      else {
+        slides = jsonData.slides || jsonData
+      }
+      
+      if (cover) {
+        slidesStore.updateSlideIndex(0)
+        slidesStore.setSlides(slides)
+        addHistorySnapshot()
+      }
+      else if (isEmptySlide.value) {
+        slidesStore.setSlides(slides)
+        addHistorySnapshot()
+      }
+      else {
+        addSlidesFromData(slides)
+      }
+      
+      return { success: true, slides }
+    }
+    catch (error) {
+      const errorMsg = '无法正确读取 / 解析该JSON数据'
+      message.error(errorMsg)
+      return { success: false, error: errorMsg, details: error }
+    }
+  }
+
+  // 暴露到window对象
+  if (typeof window !== 'undefined') {
+    (window as any).readJSON = readJSON
+  }
+
   // 导入pptist文件
   const importSpecificFile = (files: FileList, cover = false) => {
     const file = files[0]
@@ -768,6 +807,7 @@ export default () => {
     importSpecificFile,
     importJSON,
     importPPTXFile,
+    readJSON,
     exporting,
   }
 }

+ 6 - 2
src/views/Screen/SlideThumbnails.vue

@@ -9,9 +9,13 @@
         :class="{ 'active': index === slideIndex }"
         v-for="(slide, index) in slides" 
         :key="slide.id"
-        @click="turnSlide(index)"
       >
-        <ThumbnailSlide :slide="slide" :size="150" :visible="index < slidesLoadLimit" />
+        <ThumbnailSlide 
+          :slide="slide" 
+          :size="150" 
+          :visible="index < slidesLoadLimit"
+          @click="turnSlide(index)"
+        />
       </div>
     </div>
   </div>

+ 27 - 0
src/views/components/ThumbnailSlide/index.vue

@@ -21,6 +21,9 @@
         :elementInfo="element"
         :elementIndex="index + 1"
       />
+      
+      <!-- 遮罩层:确保iframe不会阻止缩略图点击事件 -->
+      <div class="thumbnail-mask" @click="handleMaskClick"></div>
     </div>
     <div class="placeholder" v-else>加载中 ...</div>
   </div>
@@ -44,6 +47,18 @@ const props = withDefaults(defineProps<{
   visible: true,
 })
 
+const emit = defineEmits<{
+  (event: 'click'): void
+}>()
+
+// 处理遮罩层点击事件
+const handleMaskClick = (e: MouseEvent) => {
+  e.stopPropagation()
+  emit('click')
+}
+
+
+
 const { viewportRatio, viewportSize } = storeToRefs(useSlidesStore())
 
 const background = computed(() => props.slide.background)
@@ -75,4 +90,16 @@ provide(injectKeySlideScale, scale)
   justify-content: center;
   align-items: center;
 }
+
+.thumbnail-mask {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: transparent;
+  pointer-events: auto; /* 拦截所有点击事件 */
+  z-index: 9999; /* 确保在所有元素之上,包括iframe */
+  cursor: pointer;
+}
 </style>

+ 3 - 3
src/views/components/element/FrameElement/index.vue

@@ -41,11 +41,11 @@
 </template>
 
 <script lang="ts" setup>
-import { PropType } from 'vue'
+import type { PropType } from 'vue'
 import { storeToRefs } from 'pinia'
 import { useMainStore } from '@/store'
-import { PPTFrameElement } from '@/types/slides'
-import { ContextmenuItem } from '@/components/Contextmenu/types'
+import type { PPTFrameElement } from '@/types/slides'
+import type { ContextmenuItem } from '@/components/Contextmenu/types'
 
 const props = defineProps({
   elementInfo: {

+ 0 - 51
src/views/components/element/WebpageElement/BaseWebpageElement.vue

@@ -1,51 +0,0 @@
-<template>
-  <div class="base-element-webpage"
-    :class="{ 'lock': elementInfo.lock }"
-    :style="{
-      top: elementInfo.top + 'px',
-      left: elementInfo.left + 'px',
-      width: elementInfo.width + 'px',
-      height: elementInfo.height + 'px',
-    }"
-  >
-    <div
-      class="rotate-wrapper"
-      :style="{ transform: `rotate(${elementInfo.rotate}deg)` }"
-    >
-      <iframe
-        :src="elementInfo.src"
-        :width="elementInfo.width"
-        :height="elementInfo.height"
-        :allowfullscreen="elementInfo.allowFullscreen"
-        :sandbox="elementInfo.sandbox"
-        frameborder="0"
-        scrolling="auto"
-        class="webpage-iframe"
-      ></iframe>
-    </div>
-  </div>
-</template>
-
-<script lang="ts" setup>
-import type { PPTWebpageElement } from '@/types/slides'
-
-const props = defineProps<{
-  elementInfo: PPTWebpageElement
-}>()
-</script>
-
-<style lang="scss" scoped>
-.base-element-webpage {
-  position: absolute;
-}
-.rotate-wrapper {
-  width: 100%;
-  height: 100%;
-}
-.webpage-iframe {
-  width: 100%;
-  height: 100%;
-  border: 1px solid #ddd;
-  border-radius: 4px;
-}
-</style> 

+ 0 - 50
src/views/components/element/WebpageElement/ScreenWebpageElement.vue

@@ -1,50 +0,0 @@
-<template>
-  <div class="screen-element-webpage"
-    :style="{
-      top: elementInfo.top + 'px',
-      left: elementInfo.left + 'px',
-      width: elementInfo.width + 'px',
-      height: elementInfo.height + 'px',
-    }"
-  >
-    <div
-      class="rotate-wrapper"
-      :style="{ transform: `rotate(${elementInfo.rotate}deg)` }"
-    >
-      <iframe
-        :src="elementInfo.src"
-        :width="elementInfo.width"
-        :height="elementInfo.height"
-        :allowfullscreen="elementInfo.allowFullscreen"
-        :sandbox="elementInfo.sandbox"
-        frameborder="0"
-        scrolling="auto"
-        class="webpage-iframe"
-      ></iframe>
-    </div>
-  </div>
-</template>
-
-<script lang="ts" setup>
-import type { PPTWebpageElement } from '@/types/slides'
-
-const props = defineProps<{
-  elementInfo: PPTWebpageElement
-}>()
-</script>
-
-<style lang="scss" scoped>
-.screen-element-webpage {
-  position: absolute;
-}
-.rotate-wrapper {
-  width: 100%;
-  height: 100%;
-}
-.webpage-iframe {
-  width: 100%;
-  height: 100%;
-  border: 1px solid #ddd;
-  border-radius: 4px;
-}
-</style> 

+ 0 - 117
src/views/components/element/WebpageElement/index.vue

@@ -1,117 +0,0 @@
-<template>
-  <div class="editable-element-webpage"
-    :class="{ 'lock': elementInfo.lock }"
-    :style="{
-      top: elementInfo.top + 'px',
-      left: elementInfo.left + 'px',
-      width: elementInfo.width + 'px',
-      height: elementInfo.height + 'px',
-    }"
-  >
-    <div
-      class="rotate-wrapper"
-      :style="{ transform: `rotate(${elementInfo.rotate}deg)` }"
-    >
-      <div 
-        class="element-content" 
-        v-contextmenu="contextmenus" 
-        @mousedown="$event => handleSelectElement($event, false)"
-        @touchstart="$event => handleSelectElement($event, false)"
-      >
-        <iframe
-          :src="elementInfo.src"
-          :width="elementInfo.width"
-          :height="elementInfo.height"
-          :allowfullscreen="elementInfo.allowFullscreen"
-          :sandbox="elementInfo.sandbox"
-          frameborder="0"
-          scrolling="auto"
-          class="webpage-iframe"
-        ></iframe>
-        <div 
-          :class="['handler-border', item]" 
-          v-for="item in ['t', 'b', 'l', 'r']" 
-          :key="item"
-          @mousedown="$event => handleSelectElement($event)"
-          @touchstart="$event => handleSelectElement($event)"
-        ></div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script lang="ts" setup>
-import { storeToRefs } from 'pinia'
-import { useMainStore } from '@/store'
-import type { PPTWebpageElement } from '@/types/slides'
-import type { ContextmenuItem } from '@/components/Contextmenu/types'
-
-const props = defineProps<{
-  elementInfo: PPTWebpageElement
-  selectElement: (e: MouseEvent | TouchEvent, element: PPTWebpageElement, canMove?: boolean) => void
-  contextmenus: () => ContextmenuItem[] | null
-}>()
-
-const { canvasScale } = storeToRefs(useMainStore())
-
-const handleSelectElement = (e: MouseEvent | TouchEvent, canMove = true) => {
-  if (props.elementInfo.lock) return
-  e.stopPropagation()
-
-  props.selectElement(e, props.elementInfo, canMove)
-}
-</script>
-
-<style lang="scss" scoped>
-.editable-element-webpage {
-  position: absolute;
-
-  &.lock .handler-border {
-    cursor: default;
-  }
-}
-.rotate-wrapper {
-  width: 100%;
-  height: 100%;
-}
-.element-content {
-  width: 100%;
-  height: 100%;
-  position: relative;
-}
-.webpage-iframe {
-  width: 100%;
-  height: 100%;
-  border: 1px solid #ddd;
-  border-radius: 4px;
-}
-.handler-border {
-  position: absolute;
-  cursor: move;
-
-  &.t {
-    width: 100%;
-    height: 20px;
-    top: 0;
-    left: 0;
-  }
-  &.b {
-    width: 100%;
-    height: 5px;
-    bottom: 0;
-    left: 0;
-  }
-  &.l {
-    width: 10px;
-    height: 100%;
-    left: 0;
-    top: 0;
-  }
-  &.r {
-    width: 10px;
-    height: 100%;
-    right: 0;
-    top: 0;
-  }
-}
-</style>