<template>
<div class="pdf-container">
<div v-if="loading" class="loading-state"><a-spin size="large" /></div>
<div v-else-if="error" class="loading-state">
加载失败,请关闭弹窗重新加载!
</div>
<div v-else class="pdf-viewer">
<pdf
v-for="i in numPages"
:key="`${pdfInstanceKey}-${i}`"
:src="pdfInstance"
:page="i"
class="pdf-page"
/>
</div>
</div>
</template>
<script>
import pdf from 'vue-pdf';
import { debounce } from 'lodash-es';
export default {
name: "PdfViewer",
components: { pdf },
props: {
currentPdfUrl: { type: [String, Object], required: true },
fileType: { type: Number, default: 1 }
},
data() {
return {
numPages: 0,
pdfInstance: null,
pdfInstanceKey: 0,
loading: false,
error: false,
activeLoadingTask: null,
currentBlobUrl: null
};
},
watch: {
currentPdfUrl: {
immediate: true,
deep: true,
handler: debounce(function(newVal) {
if (newVal) this.loadPdf(newVal);
}, 300)
}
},
methods: {
async loadPdf(source) {
try {
this.loading = true;
this.error = false;
await this.cleanupPreviousPdf();
const pdfSource = this.fileType === 1
? { url: source, withCredentials: false }
: this.createBlobUrl(source);
this.activeLoadingTask = this.fileType === 1
? pdf.createLoadingTask({
url: source,
withCredentials: false,
cMapUrl: '\'@/assets/cmaps/\'',
cMapPacked: true
})
: pdf.createLoadingTask(this.createBlobUrl(source));
this.pdfInstance = this.activeLoadingTask;
const pdfDocument = await this.activeLoadingTask.promise;
this.numPages = pdfDocument.numPages;
this.pdfInstanceKey++;
} catch (err) {
console.error('PDF加载失败:', err);
this.handleLoadError(err);
} finally {
this.loading = false;
}
},
createBlobUrl(fileObj) {
if (this.currentBlobUrl) {
URL.revokeObjectURL(this.currentBlobUrl);
}
this.currentBlobUrl = URL.createObjectURL(fileObj.originFileObj);
return this.currentBlobUrl;
},
async cleanupPreviousPdf() {
if (this.activeLoadingTask) {
try {
if (this.activeLoadingTask._transport &&
this.activeLoadingTask._transport.destroy) {
this.activeLoadingTask._transport.destroy();
}
this.activeLoadingTask.destroy();
} catch (e) {
console.warn('清理PDF worker时出错:', e);
}
this.activeLoadingTask = null;
}
this.pdfInstance = null;
this.numPages = 0;
},
handleLoadError(error) {
this.error = true;
this.numPages = 0;
if (error.name === 'PasswordException') {
console.warn('PDF需要密码');
} else if (error.name === 'InvalidPDFException') {
console.warn('无效的PDF文件');
}
},
retryLoading() {
this.loadPdf(this.currentPdfUrl).catch(()=>{
});
}
},
beforeDestroy() {
this.cleanupPreviousPdf();
if (this.currentBlobUrl) {
URL.revokeObjectURL(this.currentBlobUrl);
}
}
};
</script>
<style scoped lang="less">
.pdf-container {
width: 100%;
//height: 100%;
overflow: auto;
background-color: #f0f0f0;
.pdf-viewer {
display: flex;
flex-direction: column;
align-items: center;
.pdf-page {
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
background-color: white;
width: 100%;
&:last-child {
margin-bottom: 0;
}
}
}
}
.loading-state{
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
</style>