vue-pdf 实现pdf预览、高亮、分页、定位功能
vue-pdf 实现pdf预览、高亮、分页、定位功能(基于vue2.0!!!)
前言
vue-pdf 实现pdf预览、高亮、分页、定位功能(基于vue2.0!!! )
找了一圈 vue-pdf 高亮,举步维艰,只能自己实现了
效果图:
一、实现步骤
1.引入库
npm install --save vue-pdf
2.示例代码
3.触发高亮事件
<template>
<el-container>
<el-main>
<el-row type='flex'>
<el-col :span='14'>
<!-- 文件列表表格 -->
<el-table :data='dataList'
v-loading='loading'
ref='table'
border
row-key='id'
@row-dblclick='editChunk'
@row-click='rowClick'
@selection-change='tableSelectChange'
>
<el-table-column
type='selection'
reserve-selection
width='55'>
</el-table-column>
<el-table-column prop='name' label='块'>
</el-table-column>
</el-table>
</el-col>
<el-col :span='10' >
<!-- <pdf-viewer v-if='isPdf' ref='pdf' url='http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf'></pdf-viewer>-->
<!-- 带分页的高亮 -->
<pdf-page-viewer v-if='isPdf' ref='pdf' url='http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf'></pdf-page-viewer>
</el-col>
</el-row>
</el-main>
</el-container>
</template>
<script>
import PdfViewer from './PdfViewer.vue'
import ParseChunkDialog from './ParseChunkDialog.vue'
import VuePdfViewer from './VuePdfViewer.vue'
import PdfPageViewer from './PdfPageViewer.vue'
export default {
name: 'Chunk',
components: { PdfPageViewer, VuePdfViewer, ParseChunkDialog, PdfViewer },
props: {
docId: {
type: String,
default: ''
}
},
data () {
return {
dataList: [
{
'id': '1',
'name': 'test',
// 后端给出位置,参数分别是 page, left, right, top, bottom
'positions': [
[
2,
79,
518,
106,
193
],
[
2,
82,
520,
296,
314
],
[
2,
330,
768,
296,
314
]
]
}
],
selectedIds: [], // 已选择列表
doc: {},
loading: false,
loaded: false,
totalCount: 0,
queryParams: {
doc_id: '',
keywords: '',
size: 10,
page: 1
}
}
},
created () {
},
computed: {
isPdf () {
return true
}
},
methods: {
goBack () {
this.$emit('goBack')
},
// 表格多选
tableSelectChange (val) {
this.selectedIds = val.map(item => item.id)
},
editChunk (data) {
},
rowClick (data) {
this.$refs.pdf && this.$refs.pdf.highlight(data.positions)
},
// 获取id集
getIds (id) {
return id ? [id] : this.selectedIds
}
}
}
</script>
4.分页高亮
- PdfPageViewer.vue
<template>
<div v-loading='loading'
:element-loading-text="'拼命加载中'+percentage"
element-loading-spinner='el-icon-loading'
>
<pdf ref='pdf'
:src='url'
:page='pageNum'
:rotate='pageRotate'
@progress='loadedRatio = $event'
@page-loaded='pageLoaded($event)'
@loaded='loaded'
@num-pages='pageTotalNum=$event'
@error='pdfError($event)'
id='pdfID'>
</pdf>
<div class='tools' v-show='pageTotalNum'>
<el-button type='primary' @click='prePage'>
<i class='el-icon-arrow-left'></i>上一页
</el-button>
{{ pageNum }}/{{ pageTotalNum }}
<el-button type='primary' @click='nextPage'>
下一页<i class='el-icon-arrow-right'></i>
</el-button>
</div>
</div>
</template>
<script>
import pdf from 'vue-pdf'
export default {
name: 'PdfPageViewer',
components: {
pdf
},
props: {
url: {
type: String,
default: ''
}
},
data () {
return {
loading: true,
pageNum: 1,
pageTotalNum: 1,
pageRotate: 0,
loadedRatio: 0,
pdfWidth: 595,
zoom: 1,
tid: null,
positions: [],
highlightDivs: [] // 用于存储高亮显示的 div 元素
}
},
computed: {
percentage () {
return parseFloat((this.loadedRatio * 100).toFixed(2)) + '%'
}
},
mounted () {
console.log('PDF mounted url:', this.url)
window.addEventListener('resize', this.updateHighlights)
},
methods: {
loaded () {
this.loading = false
console.log('loaded', this.$refs.pdf.pdf)
this.$refs.pdf.pdf.getPage().then((pdf) => {
console.log('aaaaaaaaaaa', pdf.view[2])
this.pdfWidth = pdf.view[2]
console.log('pdfWidth', this.pdfWidth)
this.updateHighlights()
})
},
pageLoaded (page) {
this.loading = false
console.log('page loaded', page)
},
pdfError (error) {
console.error(error)
},
prePage () {
this.clearHighlights()
var page = this.pageNum
page = page > 1 ? page - 1 : this.pageTotalNum
this.pageNum = page
},
nextPage () {
this.clearHighlights()
var page = this.pageNum
page = page < this.pageTotalNum ? page + 1 : 1
this.pageNum = page
},
clearHighlights () {
this.highlightDivs.forEach(div => {
if (div.parentNode) {
div.parentNode.removeChild(div)
}
})
this.highlightDivs = []
},
highlight (positions) {
if (!this.positions) return
console.log('positions', positions)
this.clearHighlights()
// 为每个高亮区域创建新的 div 元素
positions.forEach(highlight => {
const [page, left, right, top, bottom] = highlight
this.pageNum = page || 1
const highlightDiv = document.createElement('div')
const highlightSize = {}
highlightSize.top = top * this.zoom
highlightSize.left = left * this.zoom
highlightSize.height = (bottom - top) * this.zoom
highlightSize.width = (right - left) * this.zoom
console.log(highlightSize)
highlightDiv.setAttribute('style', `
top:${highlightSize.top}px;
left:${highlightSize.left}px;
height:${highlightSize.height}px;
width:${highlightSize.width}px;
`)
highlightDiv.className = 'highlight__part'
// 将新的高亮显示元素添加到 PDF 容器中
document.getElementById('pdfID').appendChild(highlightDiv)
// 将新的 div 元素添加到 highlightDivs 数组中以便管理
this.highlightDivs.push(highlightDiv)
this.positions = positions
})
},
updateHighlights () {
let clientWidth = this.$refs.pdf.$el.clientWidth
this.zoom = clientWidth / this.pdfWidth
console.log(this.zoom, clientWidth, this.pdfWidth)
clearTimeout(this.tid)
this.tid = setTimeout(() => {
this.highlight(this.positions)
}, 300)
}
},
// 在 beforeDestroy 钩子中移除事件监听器
beforeDestroy () {
window.removeEventListener('resize', this.updateHighlights)
}
}
</script>
<style>
#pdfID {
overflow-x: hidden;
overflow-y: hidden;
}
.tools {
text-align: center;
}
.highlight__part {
position: absolute;
background: #ffe28f;
opacity: 0.5;
transition: background .3s;
}
</style>
5.跳转指定页面并高亮(不分页)
- PdfViewer.vue
<template>
<div ref='pdfDiv' id='pdfDiv'>
<vue-pdf-viewer v-for="page in numPages" @loaded='loaded' :ref='"pdf"+page' :page="page" :url="url" ></vue-pdf-viewer>
</div>
</template>
<script>
import pdf from 'vue-pdf'
import VuePdfViewer from './VuePdfViewer.vue'
export default {
components: {
VuePdfViewer,
pdf
},
props: {
url: {
type: String,
default: ''
}
},
data () {
return {
numPages: 1,
loadedPage: 0,
pdfWidth: 595,
pdfHeight: 800,
clientWidth: 595,
clientHeight: 800,
tid: null,
widthZoom: 1,
heightZoom: 1,
positions: [],
highlightDivs: [] // 用于存储高亮显示的 div 元素
}
},
mounted () {
console.log('PDF loaded', this.url)
this.getNumPages()
console.log(this.$refs.pdfDiv)
window.addEventListener('resize', this.updateHighlights)
},
methods: {
getNumPages () {
var loadingTask = pdf.createLoadingTask(this.url)
console.log(loadingTask, 'pdf 获取总页数成功')
loadingTask.promise.then(pdf => {
this.numPages = pdf.numPages
}).catch(() => {
console.error('pdf 获取总页数失败,重新获取...')
setTimeout(() => {
this.getNumPages()
}, 300)
})
},
scrollTo (page) {
this.$nextTick(() => {
let pdfDiv = this.$refs.pdfDiv
pdfDiv.scrollTop = this.clientHeight * (page - 1) * this.heightZoom
})
},
loaded (width, pdfHeight, page) {
console.log('pdf 第 ' + page + ' 加载完成')
this.$nextTick(() => {
this.loadedPage = page
this.pdfWidth = width
this.pdfHeight = pdfHeight
setTimeout(() => {
this.updateHighlights()
}, 1500)
})
},
highlight (positions) {
if (!this.positions) return
this.clearHighlights()
// 为每个高亮区域创建新的 div 元素
positions.forEach(highlight => {
const [page, left, right, top, bottom] = highlight
if (!page) return
this.scrollTo(page)
const highlightDiv = document.createElement('div')
const highlightSize = {}
highlightSize.top = top * this.widthZoom
highlightSize.left = left * this.widthZoom
highlightSize.height = (bottom - top) * this.widthZoom
highlightSize.width = (right - left) * this.widthZoom
highlightDiv.setAttribute('style', `
top:${highlightSize.top}px;
left:${highlightSize.left}px;
height:${highlightSize.height}px;
width:${highlightSize.width}px;
`)
highlightDiv.className = 'highlight__part'
// 将新的高亮显示元素添加到 PDF 容器中
document.getElementById('pdf' + page).appendChild(highlightDiv)
// 将新的 div 元素添加到 highlightDivs 数组中以便管理
this.highlightDivs.push(highlightDiv)
this.positions = positions
})
},
/**
* 分辨率发生变化重新计算缩放并重新绘制高亮区域
*/
updateHighlights () {
clearTimeout(this.tid)
// 防抖
this.tid = setTimeout(() => {
this.clientHeight = document.getElementById('pdf' + (this.loadedPage || 1))?.clientHeight
this.heightZoom = this.clientHeight / this.pdfHeight
this.clientWidth = this.$refs.pdfDiv.clientWidth
this.widthZoom = this.clientWidth / this.pdfWidth
this.highlight(this.positions)
}, 300)
},
clearHighlights () {
this.highlightDivs.forEach(div => {
if (div.parentNode) {
div.parentNode.removeChild(div)
}
})
this.highlightDivs = []
}
},
// 在 beforeDestroy 钩子中移除事件监听器
beforeDestroy () {
window.removeEventListener('resize', this.updateHighlights)
}
}
</script>
<style>
#pdfDiv{
overflow-y: auto;
height: calc(100vh - 420px);
}
</style>
子组件(不分页需用到)
- VuePdfViewer.vue
<template>
<div v-loading='loading'
:element-loading-text="'拼命加载中'+percentage"
element-loading-spinner='el-icon-loading'
>
<pdf :ref='pdfRef'
class='overflow-none'
:key="page"
:src="url"
:page="page"
@progress='loadedRatio = $event'
@loaded='loaded'
:id='pdfId'>
</pdf>
</div>
</template>
<script>
import pdf from 'vue-pdf'
export default {
components: {
pdf
},
props: {
url: {
type: String,
default: ''
},
page: {
type: Number,
default: 1
}
},
data () {
return {
loading: true,
tid: null,
loadedRatio: 0
}
},
computed: {
pdfRef () {
return 'ref' + this.page
},
pdfId () {
return 'pdf' + this.page
},
percentage () {
return parseFloat((this.loadedRatio * 100).toFixed(2)) + '%'
}
},
mounted () {
this.reloadCheck()
},
methods: {
loaded () {
this.loading = false
this.$refs[this.pdfRef].pdf.getPage().then((pdf) => {
let pdfWidth = pdf.view[2]
let pdfHeight = pdf.view[3]
this.$emit('loaded', pdfWidth, pdfHeight, this.page)
})
},
reloadCheck () {
if (!this.loading && this.loadedRatio === 1) {
console.log('pdf 加载失败,重新加载...')
this.$refs[this.pdfRef].pdf.loadDocument(this.url)
setTimeout(() => {
this.reloadCheck()
}, 3000)
}
}
}
}
</script>
<style scoped>
.overflow-none {
overflow-x: hidden;
overflow-y: hidden;
}
</style>
参考
笔记(重要)
如果需要实现高亮需要增强 \node_modules\vue-pdf\src\pdfjsWrapper.js 在288行后面增加:
this.getPage = function() {
return pdfDoc.getPage(1)
}
提示无法加载组件或者页面出不来,修改 \node_modules\vue-pdf\src\vuePdfNoSss.vue 替换
<style src="./annotationLayer.css"></style>
<script>
import componentFactory from './componentFactory.js'
import PdfjsWorker from 'pdfjs-dist/es5/build/pdf.worker.js'
if ( process.env.VUE_ENV !== 'server' ) {
var pdfjsWrapper = require('./pdfjsWrapper.js').default;
var PDFJS = require('pdfjs-dist/es5/build/pdf.js');
if ( typeof window !== 'undefined' && 'Worker' in window && navigator.appVersion.indexOf('MSIE 10') === -1 ) {
// var PdfjsWorker = require('worker-loader!pdfjs-dist/es5/build/pdf.worker.js');
PDFJS.GlobalWorkerOptions.workerPort = new PdfjsWorker();
}
var component = componentFactory(pdfjsWrapper(PDFJS));
} else {
var component = componentFactory({});
}
export default component;
</script>
并在 vue.config.js 中的 chainWebpack: config => { 加入以下代码
config.module
.rule('worker')
.test(/\.worker\.js$/)
.use('worker-loader')
.loader('worker-loader')
.options({
inline: true,
fallback: false
})
.end()
总结
只能说vue版本太老了…
原文地址:https://blog.csdn.net/qq_42033668/article/details/142524910
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!