yanyan 1 year ago
parent
commit
572031b7cd
  1. 344
      src/views/common/pdfPreview.vue

344
src/views/common/pdfPreview.vue

@ -1,99 +1,301 @@
<template> <template>
<div class="header_body"> <div class="pdf-preview-container">
<header-nav <!-- 头部导航栏 -->
:slefBack="true" <div class="pdf-header">
@goBack="goBack" <div class="page-navigation">
> <div class="page-controls">
</header-nav>
<div class="page-tool">
<div class="page-tool-item" @click="prevPage">上一页</div>
<div class="page-tool-item">{{state.pageNum}}/{{state.numPages}}</div>
<div class="page-tool-item" @click="nextPage">下一页</div>
</div>
</div>
</div>
</div>
<!-- PDF预览区域 -->
<div class="pdf-content">
<!-- PDF内容显示区域 -->
<div class="pdf-display-area">
<div v-for="item in state.numPages" :key="item" class='pdf-item' :id="`pdf-page-${item}`">
<vue-pdf-embed :source="state.source" class="vue-pdf-embed" :page="item" />
</div>
</div> </div>
<div id="vue-pdf-view">
<div id="page-view">
<vue-pdf-embed v-if="pdfState.isShow" :source="pdfState.pdfSource" :src="pdfState.pdfSource.url" :page="pdfState.pageNum" />
</div> </div>
</div> </div>
</template> </template>
<script setup name="register">
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch, computed, reactive } from 'vue';
import VuePdfEmbed from "vue-pdf-embed"; import VuePdfEmbed from "vue-pdf-embed";
import {reactive, ref, nextTick} from "vue";
import {baseUrl} from "@/config/env";
import { createLoadingTask } from "vue3-pdfjs"; import { createLoadingTask } from "vue3-pdfjs";
import {baseUrl} from "@/config/env";
import {useRoute} from "vue-router";
const pdfFile = 'http://192.168.110.36:85/profile/upload/2024/03/29/40eb1a97ca27c3369370e90632d92db0.pdf'
const route = useRoute();
const state = reactive({
source: pdfFile, // 预览pdf文件地址
pageNum: 1, // 当前页面
scale: 1, // 缩放比例
numPages: 0, // 总页数
});
const loadingTask: any = ref(null)
const {useMy} = $globalStore; const pdfInit = () => {
loadingTask.value = createLoadingTask(state.source);
loadingTask.value.promise.then((pdf: { numPages: number }) => {
state.numPages = pdf.numPages;
});
}
const route = useRoute(); // 滚动到指定页面
const pdfState = reactive({ let isManualNavigation = false;
isShow : false, const scrollToPage = (page: number) => {
pdfSource: { // 设置手动导航标志
url: ref("http://localhost:85/profile//2025/06/04/a1f9937950780c9c5b5acb5acaa8478f.pdf") isManualNavigation = true;
},
// 当前页 // 先立即更新页码状态
pageNum: 1, const validPage = Math.max(1, Math.min(page, state.numPages));
// 总页数 state.pageNum = validPage;
numPages: 1,
}) // 执行滚动
const scaleData = ref(1.0); const element = document.getElementById(`pdf-page-${validPage}`);
const pageHeight = ref('100%'); if (element) {
element.scrollIntoView({
// 放大 behavior: 'smooth',
function zoomIn() { block: 'start'
scaleData.value += 0.1; });
pageHeight.value = (parseInt(pageHeight.value) - 5.0) + '%'; }
}
// 缩小 // 滚动结束后清除标志
function zoomOut() { setTimeout(() => {
scaleData.value -= 0.1; isManualNavigation = false;
pageHeight.value = (parseInt(pageHeight.value) + 5.0) + '%'; }, 1000);
} };
// 页面 放大/缩小 还原默认值
function pageRest() { // 上一页
scaleData.value = 1.0; const prevPage = (): void => {
pageHeight.value = '100%'; if (state.pageNum > 1) {
} scrollToPage(state.pageNum - 1);
}
};
//初始化参数,获取文件
onMounted(() => { // 下一页
console.log("获取路由参数:", route?.query?.url); const nextPage = (): void => {
pdfState.pdfSource.url = baseUrl +route?.query?.url; const nextPage = state.pageNum + 1;
pdfState.isShow=true; if (nextPage <= state.numPages) {
scrollToPage(nextPage);
}
};
// 跳转到指定页
const goToPage = (): void => {
// 确保输入是有效数字
let page = parseInt(String(state.pageNum));
if (isNaN(page) || page < 1) {
page = 1;
} else if (page > state.numPages) {
page = state.numPages;
}
// 更新输入框显示正确的页码
state.pageNum = page;
scrollToPage(page);
};
// 处理滚动事件,更新当前页码(带防抖)
let scrollTimeout: number | null = null;
const handleScroll = () => {
// 如果是手动导航触发的滚动,则不更新页码
if (isManualNavigation) return;
if (scrollTimeout) {
clearTimeout(scrollTimeout);
}
scrollTimeout = setTimeout(() => {
const container = document.querySelector('.pdf-content');
if (!container) return;
const scrollPosition = container.scrollTop;
const items = document.querySelectorAll('.pdf-item');
const containerHeight = container.clientHeight;
const threshold = containerHeight * 0.3; // 30%视口高度作为阈值
let currentPage = state.pageNum;
// 精确检测当前可见页面
items.forEach((item) => {
const pageNum = parseInt(item.id.replace('pdf-page-', ''));
const rect = item.getBoundingClientRect();
// 如果页面顶部在视口阈值范围内,则认为是当前页
if (rect.top >= -threshold && rect.top <= threshold) {
currentPage = pageNum;
}
}); });
// 返回 // 只有当页码确实变化时才更新
const goBack = () => { if (currentPage !== state.pageNum) {
route.go(-1); state.pageNum = currentPage;
}
}, 150) as unknown as number; // 适当增加防抖时间
}; };
// 组件挂载时
onMounted(() => {
state.source = baseUrl +route?.query?.url;
// 检查浏览器PDF支持
const isPdfSupported = 'application/pdf' in navigator.mimeTypes;
console.log('浏览器原生支持PDF:', isPdfSupported);
// 添加滚动事件监听
const contentEl = document.querySelector('.pdf-content');
if (contentEl) {
contentEl.addEventListener('scroll', handleScroll);
}
pdfInit();
});
onBeforeUnmount(() => {
// 移除滚动事件监听
const contentEl = document.querySelector('.pdf-content');
if (contentEl) {
contentEl.removeEventListener('scroll', handleScroll);
}
});
</script> </script>
<style scoped> <style lang="scss" scoped>
#vue-pdf-view{ .pdf-preview-container {
position: absolute; display: flex;
left: 50%; flex-direction: column;
transform: translatex(-50%) scale(1.0);
width: 70%;
height: 100%; height: 100%;
background-color: #fff;
border-radius: 4px;
overflow: hidden;
.pdf-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 16px;
background-color: #fff;
border-bottom: 1px solid #e0e0e0;
.back-button {
cursor: pointer;
display: inline-block;
padding: 5px 5px 0px;
border-radius: 4px;
transition: background-color 0.2s;
&:hover {
background-color: #f0f0f0;
}
}
.pdf-actions {
display: flex;
gap: 10px;
}
.page-navigation {
display: flex;
align-items: center;
.page-controls {
display: flex;
align-items: center;
padding: 2px 8px;
.page-input {
width: 50px;
margin: 0 5px;
:deep(.el-input__inner) {
text-align: center; text-align: center;
padding: 0 5px;
}
}
}
/* 内容溢出显示滚动条 */ .total-pages {
overflow: scroll; margin-left: 5px;
/* 隐藏X、y轴滚动条*/ color: #606266;
overflow-y: hidden;
overflow-x: hidden;
} }
}
}
.pdf-content {
flex: 1;
overflow: auto;
/* 设置滚动栏的宽、高*/ /* 自定义滚动条样式 */
#vue-pdf-view::-webkit-scrollbar { &::-webkit-scrollbar {
width: 8px; width: 6px;
height: 8px; height: 6px;
} }
#vue-pdf-view::-webkit-scrollbar-thumb {
border-radius: 15px; &::-webkit-scrollbar-track {
background-color: #eee; background: rgba(0, 0, 0, 0.05);
border-radius: 3px;
} }
</style> &::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.15);
border-radius: 3px;
transition: background 0.2s;
&:hover {
background: rgba(0, 0, 0, 0.25);
}
}
.pdf-loading,
.pdf-error {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.pdf-display-area {
.pdf-item {
margin: 16px 0px;
}
}
}
.page-tool {
position: absolute;
bottom: 35px;
padding-left: 15px;
padding-right: 15px;
display: flex;
align-items: center;
background: rgb(66, 66, 66);
color: white;
border-radius: 19px;
z-index: 100;
cursor: pointer;
margin-left: 50%;
transform: translateX(-50%);
}
.page-tool-item {
padding: 8px 15px;
padding-left: 10px;
cursor: pointer;
}
}
</style>

Loading…
Cancel
Save