预览pdf(url格式和blob格式)

发布于:2025-06-04 ⋅ 阅读:(28) ⋅ 点赞:(0)
<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;

                // 彻底清理前一个PDF
                await this.cleanupPreviousPdf();

                // 准备新的PDF源
                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/\'',
                        // 'https://fastly.jsdelivr.net/npm/pdfjs-dist@2.11.338/cmaps/',
                        cMapPacked: true
                    })
                    : pdf.createLoadingTask(this.createBlobUrl(source));
                this.pdfInstance = this.activeLoadingTask;

                // 监听加载完成
                const pdfDocument = await this.activeLoadingTask.promise;
                this.numPages = pdfDocument.numPages;

                // 成功加载后增加实例key
                this.pdfInstanceKey++;
            } catch (err) {
                console.error('PDF加载失败:', err);
                this.handleLoadError(err);
            } finally {
                this.loading = false;
            }
        },

        createBlobUrl(fileObj) {
            // 释放之前的Blob URL
            if (this.currentBlobUrl) {
                URL.revokeObjectURL(this.currentBlobUrl);
            }
            this.currentBlobUrl = URL.createObjectURL(fileObj.originFileObj);
            return this.currentBlobUrl;
        },

        async cleanupPreviousPdf() {
            // 清理加载任务
            if (this.activeLoadingTask) {
                try {
                    // 先取消可能存在的promise
                    if (this.activeLoadingTask._transport &&
                        this.activeLoadingTask._transport.destroy) {
                        this.activeLoadingTask._transport.destroy();
                    }
                    // 销毁worker
                    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>

网站公告

今日签到

点亮在社区的每一天
去签到