emis-frontend/src/components/PdfViewer/viewer.vue
2024-06-29 11:34:07 +08:00

578 lines
15 KiB
Vue

<template>
<!-- v-if="pdfUrl" -->
<div class="pdf-viewer grid2" >
<div style="position: relative;">
<div class="pdf-bar">
<div class="zoom-minus" @click="zoomIn">
<i class="el-icon-minus" title="缩小"></i>
</div>
<div class="scale" >
<el-input v-model.number="scale" size="mini" @keyup.enter.native="setScaleValue">
<template slot="append">%</template>
</el-input>
</div>
<div class="zoom-plus" @click="zoomOut">
<i class="el-icon-plus" title="放大"></i>
</div>
<div class="prev" @click="prePage">
上一页
</div>
<div class="page">
<el-input-number
v-model.number="currentPage"
:min="0"
:max="pageCount"
size="mini"
/>
</div>
<div class="page-count">/ {{ pageCount }}</div>
<div class="next" @click="nextPage">
下一页
</div>
<div class="tools">
<div style="width:100%;text-align: right;">
<!-- <el-button title="" type="primary" v-if="isA4" @click="showSinglePage(1)">切换单页</el-button> -->
<!-- <el-button title="" type="primary" v-else @click="showA4Page(1)">切换A4</el-button> -->
<el-button title="" type="primary" icon="el-icon-download" size="mini" style="padding: 5px 10px;" @click="downPDF">下载</el-button>
<el-button title="" type="primary" icon="el-icon-printer" size="mini" style="padding: 5px 10px;" @click="printPDF">打印</el-button>
</div>
</div>
</div>
<div style="min-height:1600px;margin: 0 auto;overflow: auto;">
<!-- <el-scrollbar>
<div style="min-height:600px;margin: 0 auto;overflow: auto;"> -->
<div class="pdf-wrapper">
<pdf
:src="currentPdfUrl"
:page="page"
@num-pages="thisPageCount = $event"
@page-loaded="onPageLoaded($event)"
ref="pdf"
></pdf>
</div>
<!-- </div> -->
<!-- </el-scrollbar> -->
</div>
</div>
</div>
</template>
<script>
import printJS from 'print-js'
// npm i pdfjs-dist@2.5.207
// npm i vue-pdf@4.2.0
import pdf from 'xd-vue-pdf'
import PdfUtils from '@/utils/pdfUtils.js';
export default {
components: { pdf },
props: {
pdfList:{
type: Array,
},
startPage: {
type: Number,
default: 1
},
isLoadComplete:{
type: Boolean,
default: false
}
},
data() {
return {
loading:false,
resetCountKey:1,
loadPdfUrl:null,
// 单页模式
cacheFileList:this.pdfList,
isA4:false,
page: 1,
currentPage: 1,
pageCount: this.pdfList.length,
// A4 模式
cacheA4FileList:[],
thisPageCount:1,
scale: 25,
preScale: 25,
pdfUtils:new PdfUtils(),
loading: false,
}
},
computed: {
currentPdfUrl() {
if (!this.loadPdfUrl) return ''
const publicPath ='/static/'
return pdf.createLoadingTask({
url: this.loadPdfUrl,
cMapUrl: `${publicPath}cmaps/`,
cMapPacked: true
})
}
},
watch: {
pdfList(){
this.cacheFileList=this.pdfList;
this.initA4List();
this.showFirstFile();
},
thisPageCount(value) {
if(this.isA4){
this.doScale(75);
}else{
this.doScale(25);
}
},
isLoadComplete(newVal,oldVal){
if(this.isA4){
this.showA4Page(1);
}else{
this.showSinglePage(1);
}
}
},
mounted() {
this.initA4List();
this.showFirstFile();
window.addEventListener('mousewheel', this.handleScroll, { passive: false })
},
methods: {
initA4List(){
this.cacheA4FileList=[];
const newList = Object.assign([], this.pdfList);
if(newList&&newList.length>4){
let spliteArr = (array = [], subGroupLength = 0) => {
let index = 0;
const newArray = [];
while (index < array.length) {
newArray.push(array.slice(index, index += subGroupLength));
}
return newArray;
}
const subPdfList=spliteArr(newList, 4);
for (let i=0; i < subPdfList.length;i++){
let fileItem={
"billCode":"page-"+(i+1),
"pdfUrl":null,
"childreen":subPdfList[i]
}
this.cacheA4FileList.push(fileItem);
}
}else{
let fileItem={
"billCode":"page-"+1,
"pdfUrl":null,
"childreen":newList
}
this.cacheA4FileList.push(fileItem);
}
},
showFirstFile(){
if(this.isA4){
this.scale=75;
this.preScale=75;
this.pageCount=this.cacheA4FileList.length;
setTimeout( this.showA4Page(this.cacheA4FileList.length),3000);
}else{
this.scale=25;
this.preScale=25;
this.pageCount=this.cacheFileList.length;
setTimeout( this.showSinglePage(1),2000);
}
this.resetCountKey++;
},
addPdfUrl(billCode,pdfUrl){
let item={
"billCode":billCode,
"pdfUrl":pdfUrl
}
this.pdfList.push(item);
},
handleScroll(e){
// 判断是不是按下ctrl键
if(e.ctrlKey) {
// 取消浏览器默认的放大缩小网页行为
e.preventDefault()
// 判断是向上滚动还是向下滚动
if (e.deltaY > 0) {
// 放大重写,业务代码
this.zoomOut()
} else {
// 缩小重写,业务代码
this.zoomIn()
}
}
},
onPageLoaded(currentPage){
if(this.isA4){
// this.page = 1;
// this.doScale(75);
}else{
// this.currentPage=1;
// this.page = 1;
// this.doScale(20);
}
},
doScale(val){
if (isNaN(val)) {
this.scale = this.preScale
return
}
if (val< 10) {
this.scale = 10
}
if (val > 200) {
this.scale = 200
}
this.preScale = this.scale
this.$refs.pdf.$el.style.width = parseInt(this.scale) + '%';
this.$refs.pdf.$el.style.margin = "0 auto";
},
setScaleValue(e) {
this.doScale(e.target.value)
},
zoomIn() {
if (this.scale > 20) {
this.scale += -5
this.doScale(this.scale );
}
},
zoomOut() {
if (this.scale < 200) {
this.scale += 5
this.doScale(this.scale );
}
},
prePage() {
if(!this.isA4){
let fileNo = this.currentPage - 1
if (fileNo < 1) return
this.currentPage = fileNo
this.showSinglePage(this.currentPage)
}
else{
let fileNo = this.currentPage - 1
if (fileNo < 1) return
this.currentPage = fileNo
this.showA4Page(this.currentPage)
}
},
nextPage() {
if(!this.isA4){
let fileNo = this.currentPage + 1
if (fileNo > this.pageCount) return
this.currentPage = fileNo
this.showSinglePage(this.currentPage)
}
else{
let fileNo = this.currentPage + 1
if (fileNo > this.pageCount) return
this.currentPage = fileNo
this.showA4Page(this.currentPage)
}
},
// 下载
downPDF() {
// 下载所有预览的PDF文件
if(this.isA4){
if(this.cacheA4FileList&&this.cacheA4FileList.length>0){
this.cacheA4FileList.forEach(async item=>{
if(!item.pdfUrl){
let thisSubList=item.childreen;
let needGenList=[];
for(let j=0;j<thisSubList.length;j++){
needGenList.push(thisSubList[j].pdfBase64)
}
item.pdfUrl=await this.pdfUtils.mergePdfToA4(needGenList);
this.downPdfFile(item.pdfUrl);
}else{
this.downPdfFile(item.pdfUrl);
}
});
}
}else{
if(this.cacheFileList&&this.cacheFileList.length>0){
// this.cacheFileList.forEach( item=>{
// if(!item.pdfUrl){
// item.pdfUrl = this.pdfUtils.getBlobUrl(item.pdfBase64);
// }
// this.downPdfFile(item.billCode,item.pdfUrl);
// });
this.$prompt('请输入保存文件名', "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
closeOnClickModal: false,
inputValidator (value) {
if (value) {
return true;
}else{
return false;
}
},
inputErrorMessage: "文件名不能为空"
}).then(async ({ value }) => {
let fileName=value+".pdf";
let needGenList=[];
for(let j=0;j<this.cacheFileList.length;j++){
needGenList.push(this.cacheFileList[j].pdfBase64)
}
this.pdfUtils.mergePdf3(needGenList,fileName,true);
}).catch(() => {});
}
}
},
downPdfFile(pdfUrl){
this.$prompt('请输入保存文件名', "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
closeOnClickModal: false,
inputValidator (value) {
if (value) {
return true;
}else{
return false;
}
},
inputErrorMessage: "文件名不能为空"
}).then(async ({ value }) => {
let fileName=value+".pdf";
var tempLink = document.createElement('a')
tempLink.style.display = 'none'
tempLink.href = pdfUrl
tempLink.setAttribute('download', fileName+'.pdf')
if (typeof tempLink.download === 'undefined') {
tempLink.setAttribute('target', '_blank')
}
document.body.appendChild(tempLink)
tempLink.click()
document.body.removeChild(tempLink)
}).catch(() => {});
},
// 打印
printPDF() {
if(this.isA4){
printJS(this.loadPdfUrl);
}else{
this.$emit("onPrint");
}
},
showSinglePage(page){
this.isA4 = false;
if(!page || Object.is(page, NaN)){
page=1;
}
this.pageCount=this.cacheFileList.length;
const thisPage=this.cacheFileList[page-1];
const pdfBase64 = thisPage.pdfBase64;
this.loadPdfUrl = this.pdfUtils.getBlobUrl(pdfBase64);
this.doScale(20);
this.resetCountKey++;
},
async showA4Page(page){
this.isA4 = true
if(!page || Object.is(page, NaN)){
page=1;
}
if(!this.cacheA4FileList || this.cacheA4FileList.length==0){
return;
}
const loadingInstance = this.$loading({
text: '正在生成A4预览...'
})
this.pageCount=this.cacheA4FileList.length;
// 分页生成文件,只渲染第一页
let thisSubList=this.cacheA4FileList[page-1].childreen;
let needGenList=[];
for(let j=0;j<thisSubList.length;j++){
needGenList.push(thisSubList[j].pdfBase64)
}
let pdfUrl=await this.pdfUtils.mergePdfToA4(needGenList);
if(pdfUrl){
loadingInstance.close();
this.cacheA4FileList[page-1].pdfUrl=pdfUrl;
this.loadPdfUrl=this.cacheA4FileList[page-1].pdfUrl;
}
this.resetCountKey++;
},
}
}
</script>
<style lang="scss" scoped>
.grid1 {
height: 200px;
background-image:
linear-gradient(45deg, #8d8b8b 25%, transparent 0),
linear-gradient(-45deg, #8d8b8b 25%, transparent 0),
linear-gradient(45deg, transparent 75%, #8d8b8b 0),
linear-gradient(-45deg, transparent 75%, #8d8b8b 0);
background-position: 0 0, 0 10px, 10px -10px, -10px 0;
background-size: 20px 20px;
}
.grid2 {
height: 200px;
// background-image: radial-gradient(circle, rgb(165, 165, 165) 1px, #fff 1px);
background-image: radial-gradient(circle, #4a4a4a 1px, #3f3f3f 1px);
background-size: 15px 15px;
background-position: center center;
}
.grid3 {
height: 200px;
background-image:
linear-gradient(to right, rgb(203 213 225) 1px, transparent 1px),
linear-gradient(to bottom, rgb(203 213 225) 1px, transparent 1px);
background-size: 20px 20px;
background-position: center center;
}
.grid4 {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40'%3E%3Crect width='40' height='40' fill='%23fff' /%3E%3Crect x='50%' width='1' height='100%' fill='rgb(203 213 225)' /%3E%3Crect y='50%' width='100%' height='1' fill='rgb(203 213 225)' /%3E%3C/svg%3E%0A");
}
.showA4 {
position: absolute;
left: 100%;
width: 1920px;
overflow: visible;
}
.pdf-viewer {
position: relative;
width: 100%;
height: 100%;
// overflow: hidden;
overflow: auto;
// height: 100%;
font-family: PingFang SC;
// width: 100%;
// position: relative;
// background: #eeeeee;
// background: #dcdcdc;
.tools {
display: flex;
z-index: 9999;
position: absolute;
top: 0px;
right: 30px;
}
.pdf-bar {
position: absolute;
// position: fixed;
top: 0px;
left: 0px;
z-index: 1;
width: 100%;
height: 36px;
border: 1px solid #ddd;
box-sizing: border-box;
text-align: center;
background: #f8f8f8;
display: flex;
justify-content: center;
align-items: center;
& > div {
min-width: 36px;
height: 36px;
line-height: 36px;
color: #666;
cursor: pointer;
i {
width: 100%;
height: 100%;
color: #666;
font-weight: bold;
}
:deep .el-input--mini {
input {
padding: 0 8px;
width: 50px;
text-align: center;
}
.el-input-group__append {
padding: 0 6px;
color: #666;
cursor: auto;
}
}
:deep .el-input-number {
margin-left: 10px;
width: 40px;
.el-input-number__decrease {
display: none;
}
.el-input-number__increase {
display: none;
}
.el-input--mini {
input {
width: 40px;
text-align: center;
}
}
}
}
.prev {
margin-left: 16px;
}
.scale,
.page {
display: flex;
align-items: center;
}
.page-count {
margin-right: 10px;
}
}
.pdf-wrapper {
position: absolute;
margin-top: 70px;
width: 100%;
height: calc(100% - 70px);
overflow: auto;
}
}
</style>