This commit is contained in:
linfso 2024-06-03 08:29:12 +08:00
parent e63a5c3606
commit ffd8359f96
7 changed files with 411 additions and 83 deletions

View File

@ -69,6 +69,7 @@
"jsbarcode": "^3.11.5",
"jsencrypt": "3.2.1",
"jsmind": "^0.4.11",
"jspdf": "^2.5.1",
"lodash": "^4.17.21",
"luckyexcel": "^1.0.1",
"marked": "^4.3.0",

View File

@ -209,4 +209,5 @@
</div>
</div>
</body>
</html>

View File

@ -14,64 +14,57 @@
<div class="zoom-plus" @click="zoomOut">
<i class="el-icon-plus" title="放大"></i>
</div>
<div class="prev" @click="preFile">
<div class="prev" @click="prePage">
上一页
</div>
<div class="page">
<el-input-number
v-model.number="currentNum"
v-model.number="currentPage"
:min="0"
:max="fileCount"
:max="pageCount"
size="mini"
/>
</div>
<div class="page-count">/ {{ fileCount }}</div>
<div class="next" @click="nextFile">
<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" icon="el-icon-download" @click="downPDF">下载</el-button>
<el-button title="打印" type="primary" icon="el-icon-printer" @click="printPDF">打印</el-button>
<el-button title="" type="primary" v-if="isA4" @click="showSinglePage">切换单页</el-button>
<el-button title="" type="primary" v-else @click="showA4Page">切换A4</el-button>
<el-button title="" type="primary" icon="el-icon-download" @click="downPDF">下载</el-button>
<el-button title="" type="primary" icon="el-icon-printer" @click="printPDF">打印</el-button>
</div>
</div>
</div>
<div style="min-height:1600px;margin: 0 auto;overflow: auto;">
<div v-if="showList" class="pdf-wrapper">
<pdf
v-for="item in pdfList"
:key="item.billCode"
:src="item.pdfUrl"
:page="page"
@num-pages="pageCount = $event"
@page-loaded="onPageLoaded($event)"
ref="pdf"
></pdf>
</div>
<div v-else class="pdf-wrapper">
<!-- v-for="item in pdfList"
:key="item.billCode"
:src="item.pdfUrl" -->
<!-- :key="resetCountKey" -->
<!-- <el-scrollbar style="height:800px"> -->
<!-- </el-scrollbar> -->
<div class="pdf-wrapper">
<pdf
:src="currentPdfUrl"
:src="currentPdfUrl"
:page="page"
@num-pages="pageCount = $event"
@num-pages="thisPageCount = $event"
@page-loaded="onPageLoaded($event)"
ref="pdf"
></pdf>
<!-- v-drag -->
</div>
</div>
</div>
</div>
</template>
<script>
/**
* vue-pdf最好别用4.2,因为他依赖的pdfjs为2.5,
* 但他会下载最新的2.16版本,这个版本和原来的2.5结构目录不一样,
* 一定要使用4.2的话就手动装一下pdfjs-dist的2.5.207 ==> npm i pdfjs-dist@2.5.207
*/
import pdf from 'vue-pdf'
import { jsPDF } from "jspdf";
import PdfUtils from '@/utils/pdfUtils.js';
export default {
components: { pdf },
props: {
@ -81,25 +74,43 @@ export default {
startPage: {
type: Number,
default: 1
},
isInitA4:{
type: Boolean,
default: false
}
},
data() {
return {
resetCountKey:1,
cacheFileList:this.pdfList,
loadPdfUrl:null,
// 单页模式
cacheFileList:this.pdfList,
isA4:this.isInitA4,
page: 1,
currentPage: 0,
pageCount: 0,
currentPage: 1,
pageCount: this.pdfList.length,
showList:false,
// A4 模式
cacheA4FileList:[],
thisPageCount:1,
currentNum:1,
fileCount: this.pdfList.length,
scale: 20,
preScale: 20,
// // A4
// newPrototype: [],
// newPrototypeValue: [],
// imgFiles: [], //pdf页数列表
// filelist: [], //列表
// //img
// realWidth: 720,
// dpr: '',
loading: false,
}
},
computed: {
@ -119,12 +130,24 @@ export default {
watch: {
pdfList(){
this.cacheFileList=this.pdfList;
this.fileCount=this.cacheFileList.length;
this.pageCount=this.cacheFileList.length;
if(this.isA4){
setTimeout( this.showA4Page(),2000);
}
// console.log(this.loadPdfUrl);
// this.showFirstFile();
},
pageCount(value) {
if (value) this.page = this.startPage
thisPageCount(value) {
// console.log("============11111====================")
// console.log(this.isA4)
if(this.isA4){
// if (value) this.page = 1
this.doScale(75);
}else{
this.doScale(20);
}
}
},
mounted() {
@ -134,14 +157,20 @@ export default {
},
methods: {
showFirstFile(){
if(this.cacheFileList&& this.cacheFileList.length>0){
this.loadPdfUrl =this.pdfList[0].pdfUrl;
this.resetCountKey++;
if(this.isA4){
// this.isA4=false;
setTimeout( this.showA4Page(),2000);
}else{
if(this.cacheFileList&& this.cacheFileList.length>0){
this.loadPdfUrl =this.pdfList[0].pdfUrl;
this.resetCountKey++;
}
}
},
addPdfUrl(ydSn,pdfUrl){
addPdfUrl(billCode,pdfUrl){
let item={
"ydSn":ydSn,
"billCode":billCode,
"pdfUrl":pdfUrl
}
this.pdfList.push(item);
@ -161,9 +190,20 @@ export default {
}
}
},
onPageLoaded(){
this.doScale(20);
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
@ -176,9 +216,6 @@ export default {
this.scale = 200
}
this.preScale = this.scale
// this.$refs.pdf.$el.style.width = parseInt(this.scale) + '%';
// this.$refs.pdf.$el.style.margin = "0 auto";
this.$refs.pdf.$el.style.width = parseInt(this.scale) + '%';
this.$refs.pdf.$el.style.margin = "0 auto";
},
@ -188,50 +225,60 @@ export default {
zoomIn() {
if (this.scale > 20) {
this.scale += -5
this.$refs.pdf.$el.style.width = parseInt(this.scale) + '%'
this.$refs.pdf.$el.style.margin = "0 auto";
this.doScale(this.scale );
}
},
zoomOut() {
if (this.scale < 200) {
this.scale += 5
this.$refs.pdf.$el.style.width = parseInt(this.scale) + '%'
this.$refs.pdf.$el.style.margin = "0 auto";
this.doScale(this.scale );
}
},
preFile(){
let count = this.currentNum - 1
if (count < 1) return
this.currentNum = count
this.loadPdfUrl =this.cacheFileList[this.currentNum-1].pdfUrl;
this.resetCountKey++;
},
nextFile() {
// console.log('this.fileCount', this.currentNum, this.fileCount)
let count = this.currentNum + 1
if (count > this.fileCount) return
this.currentNum = count
this.loadPdfUrl =this.cacheFileList[this.currentNum-1].pdfUrl;
this.resetCountKey++;
},
prePage() {
let count = this.currentPage - 1
if (count < 1) return
this.page = count
console.log('this.pageCount', this.currentPage, this.pageCount)
if(!this.isA4){
let fileNo = this.currentPage - 1
if (fileNo < 1) return
this.currentPage = fileNo
this.loadPdfUrl =this.cacheFileList[this.currentPage-1].pdfUrl;
}
else{
let fileNo = this.currentPage - 1
if (fileNo < 1) return
this.currentPage = fileNo
this.loadPdfUrl =this.cacheA4FileList[this.currentPage-1].pdfUrl;
}
},
nextPage() {
// console.log('this.pageCount', this.currentPage, this.pageCount)
let count = this.currentPage + 1
if (count > this.pageCount) return
this.page = count
console.log('this.pageCount', this.currentPage, this.pageCount)
if(!this.isA4){
let fileNo = this.currentPage + 1
if (fileNo > this.pageCount) return
this.currentPage = fileNo
this.loadPdfUrl =this.cacheFileList[this.currentPage-1].pdfUrl;
}
else{
let fileNo = this.currentPage + 1
if (fileNo > this.pageCount) return
this.currentPage = fileNo
this.loadPdfUrl =this.cacheA4FileList[this.currentPage-1].pdfUrl;
}
},
// 下载
downPDF() {
// 下载所有预览的PDF文件
if(this.cacheFileList&&this.cacheFileList.length>0){
this.cacheFileList.forEach(item=>{
this.downPdfFile(item.billCode,item.pdfUrl);
});
if(this.isA4){
if(this.cacheA4FileList&&this.cacheA4FileList.length>0){
this.cacheA4FileList.forEach(item=>{
this.downPdfFile(item.billCode,item.pdfUrl);
});
}
}else{
if(this.cacheFileList&&this.cacheFileList.length>0){
this.cacheFileList.forEach(item=>{
this.downPdfFile(item.billCode,item.pdfUrl);
});
}
}
},
downPdfFile(fileName,pdfUrl){
@ -249,9 +296,159 @@ export default {
// 打印
printPDF() {
// this.$refs.pdf.print()
this.$emit("onPrint");
if(this.isA4){
if(this.cacheA4FileList&&this.cacheA4FileList.length>0){
this.cacheA4FileList.forEach(item=>{
this.loadPdfUrl=item.pdfUrl;
this.$refs.pdf.print();
});
}
}else{
this.$emit("onPrint");
}
},
// initA4() {
// //此功能是为了pdf.js内部,有时候会报for....in的错误,原因是原型方法被其他地方改变,这里需要改回来
// for (let key in Array.prototype) {
// if (!Array.prototype.hasOwnProperty(key)) continue;
// this.newPrototype.push(key);
// }
// // 存放原始键 原始方法
// this.newPrototypeValue = this.newPrototype.map((v) => ({ [v]: Array.prototype[v] }));
// // 删除直接属性
// this.newPrototype.forEach((v) => delete Array.prototype[v]);
// },
showSinglePage(){
this.showFirstFile();
this.isA4 = false;
this.doScale(20);
this.resetCountKey++;
},
async showA4Page(){
// this.initA4();
this.isA4 = true
this.doScale(75);
this.cacheA4FileList=[];
let pdfUtils= new PdfUtils();
let that = this;
const loadingInstance = this.$loading({
text: '正在生成A4预览...'
})
try {
let imgFiles = [];
for(let i=0;i<this.cacheFileList.length;i++){
let file=this.cacheFileList[i].pdfUrl;
const img = await pdfUtils.pdfToImg(file);
imgFiles.push(img);
}
// 每4个图片放一页
if(imgFiles&&imgFiles.length>0){
// 数组拆分
const subImageList=imgFiles.reduce((acc, curr, index) => {
if (index % 4 === 0) acc.push([])
acc[Math.floor(index / 4)].push(curr)
return acc
}, [])
// 分页生成文件
for(let i=0; i<subImageList.length;i++){
let thisSubList=subImageList[i];
let pageNo = i+1;
console.log(thisSubList);
let pdfUrl=this.mergeImgToPdf(pdfUtils,thisSubList,pageNo);
if(pdfUrl){
let fileItem={
"billCode":"page-"+pageNo,
"pdfUrl":pdfUrl,
}
this.cacheA4FileList.push(fileItem);
if(i==0){
loadingInstance.close();
this.loadPdfUrl=this.cacheA4FileList[0].pdfUrl;
this.pageCount=1;
this.page=1;
}
}
}
this.loadPdfUrl=this.cacheA4FileList[0].pdfUrl;
this.pageCount=this.cacheA4FileList.length;
this.currentPage=1;
this.page=1;
this.resetCountKey++;
}
// 自行更改要保存的pdf名字
// doc.save('a4pdf.pdf')
} catch (error) {
console.log(error);
}
},
mergeImgToPdf(pdfUtils,imgFiles,pageNo){
try {
var doc = new jsPDF('', 'pt', 'a4')
// doc.addPage() //添加页
//a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
// var imgWidth = 595.28
// var imgHeight = (592.28 / contentWidth) * contentHeight
// let imgRealWidth=595.28/2-50;
let imgRealHeight=841.89/2-50;
let x=0;
let y=0;
for(let i=0;i<imgFiles.length;i++){
let imgItem=imgFiles[i];
// 计算宽高比
let whRate=imgItem.imgWidth/imgItem.imgHeight;
var contentWidth = imgRealHeight*whRate
var contentHeight = imgRealHeight;
// imgRealWidth/whRate
var imgUrl = imgItem.url;
let left=40*(x+1)+x*contentWidth;
let top=40*(y+1)+y*contentHeight;
// console.log("top:"+top+",left:"+left);
doc.addImage(imgUrl, 'PNG',left, top, contentWidth, contentHeight)
// 0,0 1,0
// 0,1 1,1
// console.log("x:"+x+",y:"+y);
if(x==0){
x++;
}
else if(x==1){
x=0;
y++;
}
if(y>1){
x=0;
y=0;
// doc.addPage() //添加页
}
}
var pdfBase64 = doc.output('datauristring')
let pdfUrl=URL.createObjectURL(pdfUtils.base64ToBlob(pdfBase64));
return pdfUrl;
} catch (error) {
console.log(error);
return null;
}
}
}
}
</script>
@ -288,6 +485,13 @@ export default {
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%;

View File

@ -490,6 +490,10 @@ class TanexPrinterSdk {
// if (console && // console.log) {
// // console.log(ex);
// }
setTimeout(() => {
this.connect()
}, 1000 )
}
}

View File

@ -1,6 +1,8 @@
import { degrees, PDFDocument, rgb, StandardFonts } from 'pdf-lib';
import axios from "axios";
import Buffer from 'vue-buffer'
import * as pdfjs from 'pdfjs-dist';
import * as pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry';
/**
* 浏览器打开新页签预览pdf
@ -215,6 +217,117 @@ export default class PdfUtils {
// console.log(+new Date() - startTime)
}
getPdfnum(url) {
return new Promise((resolve, reject) => {
const imgFiles = [];
//文件读取成功完成时触发
const loadingTask = pdfjs.getDocument(url);
loadingTask.promise.then(async (pdf) => {
const pageNum = pdf.numPages;
if (pageNum > 10) {
reject('此pdf页数过多,请线下合并');
}
//准备图片
for (let i = 1; i <= pageNum; i++) {
imgFiles.push(i);
}
resolve(imgFiles);
});
});
}
/**
* pdf保存图片
* @param {Array} newimgList - canvas列表
* @param {Number} x - 页面展示宽
* @param {Number} y - 页面展示高
* @param {String}fileName - 文件名称
*/
savePdfImage(newimgList, x, y, fileName) {
return new Promise((resolve, reject) => {
// 创建canvas
var canvas = document.createElement("canvas");
canvas.width = 100;
canvas.height = 100;
canvas.id='d_canvas0';
document.body.appendChild(canvas)
let allcanvas = document.getElementById('d_canvas0');
let allctx = allcanvas.getContext('2d');
allcanvas.style.width = x;
allcanvas.style.height = y * newimgList.length;
const subCanvasWidth = newimgList[0].width;
const subCanvasHeight = newimgList[0].height;
allcanvas.width = subCanvasWidth;
allcanvas.height = subCanvasHeight * newimgList.length;
newimgList.forEach((subCanvas, index) => {
// 计算当前图片在Canvas中的垂直位置
const yPosition = index * subCanvasHeight;
// 绘制图片
// 使用drawImage的四个参数版本来指定绘制的源图像区域和目标区域
allctx.drawImage(subCanvas, 0, yPosition, subCanvasWidth, subCanvasHeight);
if (index === newimgList.length - 1) {
let pngData = allcanvas.toDataURL('image/png');
let obj = {
url: pngData,
name: fileName,
imgWidth:subCanvasWidth,
imgHeight:subCanvasHeight
};
document.body.removeChild(canvas)
resolve(obj);
}
});
});
}
pdfToImg(pdfUrl) {
return new Promise((resolve, reject) => {
const newimgList = [];
const fileName = new Date().getTime()+"";
const loadingTask = pdfjs.getDocument(pdfUrl);
loadingTask.promise.then(async (pdf) => {
let pageNum = pdf.numPages;
console.log(pageNum);
// 处理
for (let i = 1; i <= pageNum; i++) {
// 创建canvas
var canvas = document.createElement("canvas");
canvas.width = 100;
canvas.height = 100;
canvas.id='vmpdf_canvas_' + i;
document.body.appendChild(canvas)
let canvasItem = '';
pdf.getPage(i).then(async (page) => {
const canvas = document.getElementById('vmpdf_canvas_' + i);
const ctx = canvas.getContext('2d');
const viewport = page.getViewport({ scale: 4 });
canvas.height = viewport.height;
canvas.width = viewport.width;
const destWidth = 298;
const destheight = destWidth * (viewport.height / viewport.width);
canvas.style.width = destWidth + 'px';
canvas.style.height = destWidth * (viewport.height / viewport.width) + 'px';
newimgList.push(canvas);
await page.render({ canvasContext: ctx, viewport });
// 使用file对象进行后续操作
if (i === pageNum) {
document.body.removeChild(canvas)
setTimeout(async () => {
const res = await this.savePdfImage(newimgList, destWidth, destheight, fileName);
resolve(res);
}, 500);
}
});
}
});
});
}
getBlobUrl(base64Str, fileName,isNeedDown=false) {
let blob = this.base64ToBlobNoHeader(base64Str);

View File

@ -18,7 +18,7 @@
</span>
</template>
<el-option
v-for="item in printTplList"
v-for="item in printTplList.filter(el=>isInitA4?(el.tplCode == 'A4-EP'?true:false):true)"
:key="item.tplCode"
:label="item.tplName"
:value="item.tplCode"
@ -59,7 +59,7 @@
<el-dialog class="printPreDialog" title="预览" :visible.sync="openPdfView" width="95%" height="100%" :close-on-click-modal="false" :destroy-on-close="true" v-dialogDrag append-to-body @close="handleClose">
<div style="height:100%;width:100%;overflow: auto;">
<PdfViewer ref="pdfViewer" v-if="openPdfView" :pdfList="pdfList" @onPrint="onPdfViewPrint" ></PdfViewer>
<PdfViewer ref="pdfViewer" v-if="openPdfView" :pdfList="pdfList" :isInitA4="isInitA4" @onPrint="onPdfViewPrint" ></PdfViewer>
</div>
</el-dialog>
@ -102,7 +102,12 @@
type: [Boolean],
required: false,
default: false
}
},
isInitA4:{
type: [Boolean],
required: false,
default: false
},
},
data() {
return {

View File

@ -138,7 +138,7 @@
</el-col>
<el-col :span="1.5">
<TanexPrinterPanel ref="tanexPrinter" :isShowPinters="true" :isShowTpl="true" :isShowSubBillRange="true" @onPrint="handleBatchPrint"></TanexPrinterPanel>
<TanexPrinterPanel ref="tanexPrinter" :isShowPinters="true" :isShowTpl="true" :isInitA4="true" :isShowSubBillRange="true" @onPrint="handleBatchPrint"></TanexPrinterPanel>
</el-col>