Prechádzať zdrojové kódy

feat(ThumbnailSlide): 新增缩略图幻灯片组件

添加缩略图幻灯片组件,支持动态调整尺寸和比例,包含背景样式处理、元素渲染和点击事件处理
lsc 5 dní pred
rodič
commit
c5b8800535

+ 1 - 1
package.json

@@ -22,7 +22,7 @@
     "clipboard": "^2.0.11",
     "crypto-js": "^4.2.0",
     "dexie": "^4.0.11",
-    "echarts": "^5.5.1",
+    "echarts": "^5.6.0",
     "echarts-wordcloud": "^2.1.0",
     "file-saver": "^2.0.5",
     "hfmath": "^0.0.2",

+ 3 - 6
src/views/components/ThumbnailSlide/index.vue

@@ -2,14 +2,11 @@
   <div
     class="thumbnail-slide"
     :style="{
-      width: 120 + 'px',
-      height: 67.5 + 'px',
+      width: size + 'px',
+      height: size * 0.5625 + 'px',
     }"
   >
-      <!-- :style="{
-      width: size + 'px',
-      height: size * viewportRatio + 'px',
-    }" -->
+      <!-- viewportRatio -->
     <div
       class="elements"
       :style="{

+ 116 - 0
src/views/components/ThumbnailSlide/index2.vue

@@ -0,0 +1,116 @@
+<template>
+  <div
+    class="thumbnail-slide"
+    :style="{
+      width: 120 + 'px',
+      height: 67.5 + 'px',
+    }"
+  >
+      <!-- :style="{
+      width: size + 'px',
+      height: size * viewportRatio + 'px',
+    }" -->
+    <div
+      class="elements"
+      :style="{
+        width: viewportSize + 'px',
+        height: viewportSize * viewportRatio + 'px',
+        transform: `scale(${scale})`,
+        position: 'absolute',
+      }"
+      v-if="visible"
+    >
+      <div class="background" :style="backgroundStyle"></div>
+      <ThumbnailElement
+        v-for="(element, index) in slide.elements"
+        :key="element.id"
+        :scale="scale"
+        :slide="slide"
+        :elementInfo="element"
+        :elementIndex="index + 1"
+      />
+
+      <!-- 遮罩层:确保iframe不会阻止缩略图点击事件 -->
+      <div class="thumbnail-mask" @click="handleMaskClick"></div>
+    </div>
+    <div class="placeholder" v-else>{{ lang.ssLoading }}</div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { computed, provide } from "vue";
+import { storeToRefs } from "pinia";
+import { useSlidesStore } from "@/store";
+import type { Slide } from "@/types/slides";
+import { injectKeySlideScale } from "@/types/injectKey";
+import useSlideBackgroundStyle from "@/hooks/useSlideBackgroundStyle";
+import { lang } from "@/main";
+
+import ThumbnailElement from "./ThumbnailElement.vue";
+
+const props = withDefaults(
+  defineProps<{
+    slide: Slide;
+    size: number;
+    visible?: boolean;
+  }>(),
+  {
+    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);
+const { backgroundStyle } = useSlideBackgroundStyle(background);
+
+const scale = computed(() => props.size / viewportSize.value);
+provide(injectKeySlideScale, scale);
+</script>
+
+<style lang="scss" scoped>
+.thumbnail-slide {
+  background-color: #fff;
+  overflow: hidden;
+  user-select: none;
+  position: relative;
+}
+.elements {
+  transform-origin: 0 0;
+}
+.background {
+  width: 100%;
+  height: 100%;
+  background-position: center;
+  position: absolute;
+}
+.placeholder {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  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>