<template>
|
<div class="pdf-viewer-modern">
|
<div v-if="loading" class="loading-wrapper">
|
<div class="loading-content">
|
<div class="spinner"></div>
|
<p class="loading-text">正在加载PDF...</p>
|
</div>
|
</div>
|
<div v-else-if="error" class="error-wrapper">
|
<div class="error-content">
|
<div class="error-icon">⚠️</div>
|
<p class="error-text">{{ error }}</p>
|
<button @click="loadPdf" class="retry-button">重新加载</button>
|
</div>
|
</div>
|
<div v-else class="viewer-container">
|
<iframe
|
:src="pdfSrc"
|
class="pdf-iframe"
|
frameborder="0"
|
@load="onPdfLoad"
|
@error="onPdfError"
|
></iframe>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
export default {
|
name: 'PdfViewerModern',
|
props: {
|
pdfUrl: {
|
type: String,
|
required: true,
|
default: ''
|
}
|
},
|
data() {
|
return {
|
loading: true,
|
error: null,
|
pdfSrc: ''
|
}
|
},
|
mounted() {
|
this.loadPdf()
|
},
|
watch: {
|
pdfUrl() {
|
this.loadPdf()
|
}
|
},
|
computed: {
|
finalPdfUrl() {
|
if (!this.pdfUrl) return ''
|
|
let url = this.pdfUrl
|
|
// 如果URL不包含http,需要添加基础URL
|
if (!url.startsWith('http')) {
|
const APIUrl = require('@/axios/api.url').default
|
url = APIUrl.baseURL + (url.startsWith('/') ? url : '/' + url)
|
}
|
|
// 添加token
|
const token = typeof window !== 'undefined' && window.localStorage ? window.localStorage.getItem('USERTOKEN') || '' : ''
|
url = url + (url.indexOf('?') > -1 ? '&' : '?') + 'token=' + encodeURIComponent(token)
|
|
// 保留工具栏(包含缩放工具),但隐藏导航面板
|
url = url + '#toolbar=1&navpanes=0'
|
|
return url
|
}
|
},
|
methods: {
|
loadPdf() {
|
if (!this.pdfUrl) {
|
this.error = 'PDF地址为空'
|
this.loading = false
|
return
|
}
|
|
this.loading = true
|
this.error = null
|
|
// 设置iframe的src,浏览器会自动加载
|
this.pdfSrc = this.finalPdfUrl
|
|
// 延迟隐藏loading,给iframe一些加载时间
|
setTimeout(() => {
|
this.loading = false
|
}, 500)
|
},
|
onPdfLoad() {
|
this.loading = false
|
this.error = null
|
},
|
onPdfError() {
|
this.loading = false
|
this.error = 'PDF加载失败,请检查文件是否存在'
|
}
|
}
|
}
|
</script>
|
|
<style scoped>
|
.pdf-viewer-modern {
|
width: 100%;
|
height: 100%;
|
max-height: calc(100vh - 400px);
|
background: #f5f5f5;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.loading-wrapper,
|
.error-wrapper {
|
flex: 1;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
background: #f8f9fa;
|
}
|
|
.loading-content,
|
.error-content {
|
text-align: center;
|
padding: 40px;
|
}
|
|
.spinner {
|
width: 50px;
|
height: 50px;
|
margin: 0 auto 20px;
|
border: 4px solid rgba(102, 126, 234, 0.1);
|
border-top-color: #667eea;
|
border-radius: 50%;
|
animation: spin 0.8s linear infinite;
|
}
|
|
@keyframes spin {
|
to { transform: rotate(360deg); }
|
}
|
|
.loading-text {
|
color: #667eea;
|
font-size: 16px;
|
font-weight: 500;
|
}
|
|
.error-icon {
|
font-size: 48px;
|
margin-bottom: 16px;
|
}
|
|
.error-text {
|
color: #e74c3c;
|
font-size: 16px;
|
margin-bottom: 20px;
|
}
|
|
.retry-button {
|
padding: 12px 24px;
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
color: #fff;
|
border: none;
|
border-radius: 8px;
|
font-size: 14px;
|
font-weight: 500;
|
cursor: pointer;
|
transition: transform 0.2s, box-shadow 0.2s;
|
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
}
|
|
.retry-button:active {
|
transform: scale(0.95);
|
}
|
|
.viewer-container {
|
flex: 1;
|
display: flex;
|
flex-direction: column;
|
background: #f5f5f5;
|
overflow: hidden;
|
position: relative;
|
max-height: calc(100vh - 200px);
|
height: calc(100vh - 200px);
|
}
|
|
.pdf-iframe {
|
width: 100%;
|
height: 100%;
|
border: none;
|
background: #fff;
|
min-height: 0;
|
}
|
</style>
|