PdfPreviewPage.vue 3.29 KB
<!--
 * @Date: 2024-01-17
 * @Description: PDF预览页面
-->
<template>
    <div class="pdf-preview-page bg-white min-h-screen flex flex-col">
        <!-- 顶部导航栏 -->
        <div class="flex items-center justify-between p-4 bg-white border-b sticky top-0 z-10">
            <div class="flex items-center space-x-2">
                <font-awesome-icon icon="file-pdf" class="text-red-500 text-xl" />
                <span class="text-gray-900 font-medium">{{ title }}</span>
            </div>
            <div class="flex items-center space-x-4">
                <a :href="pdfUrl" target="_blank" class="text-blue-600 hover:text-blue-800">
                    <font-awesome-icon icon="external-link-alt" class="mr-1" />
                    新窗口打开
                </a>
                <button @click="goBack" class="text-gray-500 hover:text-gray-700">
                    <font-awesome-icon icon="times" />
                </button>
            </div>
        </div>

        <!-- PDF内容区域 -->
        <div class="flex-1 overflow-y-auto bg-gray-100 p-4">
            <div v-for="pageNum in pageNums" :key="pageNum" class="mb-4" ref="pageRefs">
                <VuePdfEmbed
                    v-if="pageVisibility[pageNum]"
                    :source="{ url: pdfUrl }"
                    :page="pageNum"
                    :scale="1.5"
                    :render-text="true"
                    style="width: 100%;"
                />
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref, computed, onMounted, onBeforeUnmount, nextTick } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import VuePdfEmbed, { useVuePdfEmbed } from 'vue-pdf-embed';

const route = useRoute();
const router = useRouter();

// 获取路由参数
const title = ref(route.query.title || 'PDF预览');
const pdfUrl = ref(route.query.url || '');

// PDF页面相关
const pageRefs = ref([]);
const pageVisibility = ref({});
let pageIntersectionObserver;

// 使用PDF嵌入组件
const { doc } = useVuePdfEmbed({
    source: { url: pdfUrl.value },
});

// 计算总页数
const pageNums = computed(() =>
    doc.value
        ? [...Array(doc.value.numPages + 1).keys()].slice(1)
        : []
);

// 重置页面交叉观察器
const resetPageIntersectionObserver = () => {
    pageIntersectionObserver?.disconnect();
    pageIntersectionObserver = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
            if (entry.isIntersecting) {
                const index = pageRefs.value.indexOf(entry.target);
                const pageNum = pageNums.value[index];
                pageVisibility.value[pageNum] = true;
            }
        });
    });
    pageRefs.value.forEach((element) => {
        if (element) {
            pageIntersectionObserver.observe(element);
        }
    });
};

// 返回上一页
const goBack = () => {
    router.back();
};

// 监听页数变化
watch(pageNums, (newPageNums) => {
    if (newPageNums.length > 0) {
        pageVisibility.value = { [newPageNums[0]]: true };
        nextTick(resetPageIntersectionObserver);
    }
});

// 组件卸载前清理
onBeforeUnmount(() => {
    pageIntersectionObserver?.disconnect();
});
</script>

<style lang="less" scoped>
.pdf-preview-page {
    .pdf-content {
        background-color: #f3f4f6;
    }
}
</style>