upate
This commit is contained in:
parent
d3f48ca8cd
commit
8240c5e447
@ -7,7 +7,8 @@
|
||||
"postcss": "^8.4.49",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"vue": "^3.3.0",
|
||||
"vue-router": "^4.5.0"
|
||||
"vue-router": "^4.5.0",
|
||||
"pdfjs-dist": "^4.0.269"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
1973
pnpm-lock.yaml
generated
Normal file
1973
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
287
src/components/PdfViewer.vue
Normal file
287
src/components/PdfViewer.vue
Normal file
@ -0,0 +1,287 @@
|
||||
<template>
|
||||
<div class="pdf-container">
|
||||
<div v-if="errorMessage" class="error-message">{{ errorMessage }}</div>
|
||||
|
||||
<!-- 美观的加载动画 -->
|
||||
<div v-if="isLoading" class="loading-container">
|
||||
<div class="loading-spinner">
|
||||
<div class="spinner-circle"></div>
|
||||
<div class="spinner-circle-inner"></div>
|
||||
</div>
|
||||
<div class="loading-text">加载中...</div>
|
||||
</div>
|
||||
|
||||
<!-- 使用iframe直接嵌入PDF -->
|
||||
<iframe
|
||||
v-if="!isLoading && !errorMessage && pdfUrl"
|
||||
:src="pdfUrl"
|
||||
class="pdf-iframe"
|
||||
frameborder="0"
|
||||
@load="iframeLoaded"
|
||||
@error="iframeError"
|
||||
></iframe>
|
||||
|
||||
<!-- 保留原始canvas渲染方式作为备选 -->
|
||||
<div v-if="useCanvas && !isLoading && !errorMessage" ref="pdfContainer" class="canvas-container">
|
||||
<canvas ref="pdfCanvas"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
export default {
|
||||
name: 'PdfViewer',
|
||||
props: {
|
||||
pdfUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const route = useRoute()
|
||||
const pdfContainer = ref(null)
|
||||
const pdfCanvas = ref(null)
|
||||
const isLoading = ref(true)
|
||||
const errorMessage = ref('')
|
||||
const useCanvas = ref(false) // 默认使用iframe方式
|
||||
|
||||
let pdfDoc = null
|
||||
|
||||
// 获取URL参数中的PDF地址
|
||||
const getPdfUrl = () => {
|
||||
let url = props.pdfUrl || route.query.url || ''
|
||||
|
||||
// 处理URL格式,移除可能的@前缀
|
||||
if (url.startsWith('@')) {
|
||||
url = url.substring(1)
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
// iframe加载完成
|
||||
const iframeLoaded = () => {
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
// iframe加载错误
|
||||
const iframeError = () => {
|
||||
errorMessage.value = '无法加载PDF文档,尝试使用备选方式加载'
|
||||
useCanvas.value = true // 尝试使用canvas方式
|
||||
loadPdfWithCanvas()
|
||||
}
|
||||
|
||||
// 使用canvas方式加载PDF
|
||||
const loadPdfWithCanvas = async () => {
|
||||
const url = getPdfUrl()
|
||||
|
||||
if (!url) {
|
||||
errorMessage.value = '未提供PDF文件URL'
|
||||
isLoading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
isLoading.value = true
|
||||
errorMessage.value = ''
|
||||
|
||||
try {
|
||||
// 导入PDF.js库
|
||||
const pdfjs = await import('pdfjs-dist')
|
||||
|
||||
// 设置worker路径
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.js`
|
||||
|
||||
// 加载PDF文档
|
||||
pdfDoc = await pdfjs.getDocument(url).promise
|
||||
|
||||
// 渲染第一页
|
||||
renderPage(1)
|
||||
|
||||
isLoading.value = false
|
||||
} catch (error) {
|
||||
console.error('加载PDF失败:', error)
|
||||
errorMessage.value = '无法加载PDF文档,请检查URL是否正确'
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染指定页面
|
||||
const renderPage = async (pageNum) => {
|
||||
if (!pdfDoc) return
|
||||
|
||||
try {
|
||||
// 获取页面
|
||||
const page = await pdfDoc.getPage(pageNum)
|
||||
|
||||
// 获取原始视口
|
||||
const viewport = page.getViewport({ scale: 1.0 })
|
||||
|
||||
// 设置canvas尺寸
|
||||
const canvas = pdfCanvas.value
|
||||
const context = canvas.getContext('2d')
|
||||
canvas.height = viewport.height
|
||||
canvas.width = viewport.width
|
||||
|
||||
// 渲染PDF页面到canvas
|
||||
const renderContext = {
|
||||
canvasContext: context,
|
||||
viewport: viewport
|
||||
}
|
||||
|
||||
await page.render(renderContext).promise
|
||||
} catch (error) {
|
||||
console.error('渲染PDF页面失败:', error)
|
||||
errorMessage.value = '渲染PDF页面失败'
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
const init = () => {
|
||||
isLoading.value = true
|
||||
errorMessage.value = ''
|
||||
|
||||
// 使用iframe方式
|
||||
if (!useCanvas.value) {
|
||||
setTimeout(() => {
|
||||
if (isLoading.value) {
|
||||
isLoading.value = false
|
||||
}
|
||||
}, 3000) // 3秒后自动结束加载状态
|
||||
}
|
||||
}
|
||||
|
||||
// 监听URL变化
|
||||
watch(() => getPdfUrl(), () => {
|
||||
init()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
|
||||
return {
|
||||
pdfContainer,
|
||||
pdfCanvas,
|
||||
isLoading,
|
||||
errorMessage,
|
||||
useCanvas,
|
||||
pdfUrl: getPdfUrl(),
|
||||
iframeLoaded,
|
||||
iframeError
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pdf-container {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: red;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 5px;
|
||||
padding: 15px 20px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
max-width: 80%;
|
||||
}
|
||||
|
||||
/* 加载动画容器 */
|
||||
.loading-container {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
.loading-spinner {
|
||||
position: relative;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.spinner-circle {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: #FFCC00;
|
||||
border-radius: 50%;
|
||||
animation: spin 1.5s linear infinite;
|
||||
}
|
||||
|
||||
.spinner-circle-inner {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: #FF9900;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite reverse;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.pdf-iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
display: block;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.canvas-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
canvas {
|
||||
background: transparent;
|
||||
}
|
||||
</style>
|
||||
@ -8,6 +8,7 @@ import CommunityRequest from '../components/CommunityRequest.vue'
|
||||
import PartnerRequest from '../components/PartnerRequest.vue'
|
||||
import ImageRecognition from '../components/ImageRecognition.vue'
|
||||
import SystemHealth from '../components/SystemHealth.vue'
|
||||
import PdfViewer from '../components/PdfViewer.vue'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@ -79,6 +80,15 @@ const routes = [
|
||||
title: '系统健康状态',
|
||||
requiresAuth: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/pdf-viewer',
|
||||
name: 'PdfViewer',
|
||||
component: PdfViewer,
|
||||
meta: {
|
||||
title: 'PDF查看器',
|
||||
requiresAuth: false
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user