需求:上传多份PDF,可以挨个展示预览
技术栈:vue3
ui:vant4
问题:
1,需要适配浏览器,安卓和iOS系统
2,可以放大缩小
实现:使用pdfh5插件
代码:
<template>
<van-swipe
v-if="imageList.length > 0"
ref="swipeRef"
:touchable="true"
:show-indicators="false"
@change="onSwipeChange"
>
<van-swipe-item v-for="(img, index) in imageList" :key="index">
<template v-if="isPdfFile(img)">
<div class="pdf-container" @click="onPreview(img)" >
<div :id="`pdfh5-${index}`" class="pdfh5-wrapper"></div>
</div>
</template>
<template v-else>
<img :src="img" alt="xxx" class="invoice-image" @click="onPreview(img)" />
</template>
</van-swipe-item>
</van-swipe>
</template>
<script setup>
import Pdfh5 from 'pdfh5'
import * as pdfjs from 'pdfjs-dist'
pdfjs.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.11.174/build/pdf.worker.min.js'
// PDF 渲染相关
const pdfLoading = ref(false)
const pdfError = ref('')
let pdfh5Instance = null
const prevImage = () => {
swipeRef.value?.prev()
}
const nextImage = () => {
swipeRef.value?.next()
}
// 滑动切换时渲染 PDF
const onSwipeChange = (index) => {
currentImageIndex.value = index + 1
nextTick(() => {
const currentImg = imageList.value[index]
if (currentImg && isPdfFile(currentImg)) {
renderPdf(currentImg, index)
}
})
}
// 判断是否为PDF文件
const isPdfFile = (url) => {
if (!url) return false
return url.toLowerCase().endsWith('.pdf') || url.includes('.pdf?')
}
// 使用 pdfh5 渲染 PDF
const renderPdf = (url, index) => {
if (!url) return
// 销毁旧实例
if (pdfh5Instance) {
pdfh5Instance.destroy()
pdfh5Instance = null
}
const containerId = `pdfh5-${index}`
// 延迟确保 DOM 元素已挂载
setTimeout(() => {
const container = document.getElementById(containerId)
if (!container) {
console.error('PDF 容器元素未找到:', containerId)
pdfError.value = 'PDF 容器加载失败'
return
}
try {
pdfLoading.value = true
pdfError.value = ''
// 直接传入 DOM 元素而非选择器字符串
pdfh5Instance = new Pdfh5(container, {
pdfurl: url,
lazy: false
})
// 监听准备开始渲染
pdfh5Instance.on('ready', () => {
console.log('PDF 总页数:', pdfh5Instance.totalNum)
})
// 监听加载完成
pdfh5Instance.on('complete', (status, msg, time) => {
console.log('PDF 加载完成:', status, msg, time)
if (status === 'success') {
pdfLoading.value = false
}
})
// 监听错误
pdfh5Instance.on('error', (err) => {
console.error('PDF 加载失败:', err)
pdfError.value = 'PDF 加载失败'
pdfLoading.value = false
})
} catch (err) {
console.error('PDFh5 初始化失败:', err)
pdfError.value = 'PDF 加载失败'
pdfLoading.value = false
}
}, 100)
}
// 预览图片或PDF
const previewVisible = ref(false)
const previewImage = ref('')
const previewPdf = ref('')
const onPreview = (img) => {
console.log('img', img)
window.open(img, '_blank')
// if (isPdfFile(img)) {
// window.open(img, '_blank')
// } else {
// previewImage.value = img
// previewVisible.value = true
// }
}
// 监听图片列表变化,渲染 PDF
watch(imageList, (newList) => {
if (newList.length > 0) {
nextTick(() => {
const currentImg = newList[currentImageIndex.value - 1]
const index = currentImageIndex.value - 1
if (currentImg && isPdfFile(currentImg)) {
renderPdf(currentImg, index)
}
})
}
}, { immediate: true })
// 组件卸载时销毁 pdfh5
onUnmounted(() => {
if (pdfh5Instance) {
pdfh5Instance.destroy()
pdfh5Instance = null
}
})
</script>
时间紧张,挑着把主要代码复制下来了~