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

350
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> <div class="page-tool">
<div id="vue-pdf-view"> <div class="page-tool-item" @click="prevPage">上一页</div>
<div id="page-view"> <div class="page-tool-item">{{state.pageNum}}/{{state.numPages}}</div>
<vue-pdf-embed v-if="pdfState.isShow" :source="pdfState.pdfSource" :src="pdfState.pdfSource.url" :page="pdfState.pageNum" /> <div class="page-tool-item" @click="nextPage">下一页</div>
</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> </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 { createLoadingTask } from "vue3-pdfjs";
import {baseUrl} from "@/config/env"; import {baseUrl} from "@/config/env";
import {createLoadingTask} from "vue3-pdfjs"; 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 {useMy} = $globalStore; const loadingTask: any = ref(null)
const route = useRoute(); const pdfInit = () => {
const pdfState = reactive({ loadingTask.value = createLoadingTask(state.source);
isShow : false, loadingTask.value.promise.then((pdf: { numPages: number }) => {
pdfSource: { state.numPages = pdf.numPages;
url: ref("http://localhost:85/profile//2025/06/04/a1f9937950780c9c5b5acb5acaa8478f.pdf") });
},
// 当前页
pageNum: 1,
// 总页数
numPages: 1,
})
const scaleData = ref(1.0);
const pageHeight = ref('100%');
// 放大
function zoomIn() {
scaleData.value += 0.1;
pageHeight.value = (parseInt(pageHeight.value) - 5.0) + '%';
}
// 缩小
function zoomOut() {
scaleData.value -= 0.1;
pageHeight.value = (parseInt(pageHeight.value) + 5.0) + '%';
}
// 页面 放大/缩小 还原默认值
function pageRest() {
scaleData.value = 1.0;
pageHeight.value = '100%';
} }
// 滚动到指定页面
let isManualNavigation = false;
const scrollToPage = (page: number) => {
// 设置手动导航标志
isManualNavigation = true;
// 先立即更新页码状态
const validPage = Math.max(1, Math.min(page, state.numPages));
state.pageNum = validPage;
// 执行滚动
const element = document.getElementById(`pdf-page-${validPage}`);
if (element) {
element.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
// 滚动结束后清除标志
setTimeout(() => {
isManualNavigation = false;
}, 1000);
};
// 上一页
const prevPage = (): void => {
if (state.pageNum > 1) {
scrollToPage(state.pageNum - 1);
}
};
// 下一页
const nextPage = (): void => {
const nextPage = state.pageNum + 1;
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;
}
});
// 只有当页码确实变化时才更新
if (currentPage !== state.pageNum) {
state.pageNum = currentPage;
}
}, 150) as unknown as number; // 适当增加防抖时间
};
// 组件挂载时
onMounted(() => { onMounted(() => {
console.log("获取路由参数:", route?.query?.url); state.source = baseUrl +route?.query?.url;
pdfState.pdfSource.url = baseUrl +route?.query?.url; // 检查浏览器PDF支持
pdfState.isShow=true; 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 goBack = () => { // 移除滚动事件监听
route.go(-1); 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%;
text-align: center; background-color: #fff;
border-radius: 4px;
overflow: hidden;
/* 内容溢出显示滚动条 */ .pdf-header {
overflow: scroll; display: flex;
/* 隐藏X、y轴滚动条*/ justify-content: space-between;
overflow-y: hidden; align-items: center;
overflow-x: hidden; padding: 10px 16px;
} background-color: #fff;
border-bottom: 1px solid #e0e0e0;
/* 设置滚动栏的宽、高*/ .back-button {
#vue-pdf-view::-webkit-scrollbar { cursor: pointer;
width: 8px; display: inline-block;
height: 8px; padding: 5px 5px 0px;
} border-radius: 4px;
#vue-pdf-view::-webkit-scrollbar-thumb { transition: background-color 0.2s;
border-radius: 15px;
background-color: #eee;
}
</style> &: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;
padding: 0 5px;
}
}
}
.total-pages {
margin-left: 5px;
color: #606266;
}
}
}
.pdf-content {
flex: 1;
overflow: auto;
/* 自定义滚动条样式 */
&::-webkit-scrollbar {
width: 6px;
height: 6px;
}
&::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.05);
border-radius: 3px;
}
&::-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