|
|
@@ -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>
|