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", "jsbarcode": "^3.11.5",
"jsencrypt": "3.2.1", "jsencrypt": "3.2.1",
"jsmind": "^0.4.11", "jsmind": "^0.4.11",
"jspdf": "^2.5.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"luckyexcel": "^1.0.1", "luckyexcel": "^1.0.1",
"marked": "^4.3.0", "marked": "^4.3.0",

View File

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

View File

@ -14,64 +14,57 @@
<div class="zoom-plus" @click="zoomOut"> <div class="zoom-plus" @click="zoomOut">
<i class="el-icon-plus" title="放大"></i> <i class="el-icon-plus" title="放大"></i>
</div> </div>
<div class="prev" @click="preFile"> <div class="prev" @click="prePage">
上一页 上一页
</div> </div>
<div class="page"> <div class="page">
<el-input-number <el-input-number
v-model.number="currentNum" v-model.number="currentPage"
:min="0" :min="0"
:max="fileCount" :max="pageCount"
size="mini" size="mini"
/> />
</div> </div>
<div class="page-count">/ {{ fileCount }}</div> <div class="page-count">/ {{ pageCount }}</div>
<div class="next" @click="nextFile"> <div class="next" @click="nextPage">
下一页 下一页
</div> </div>
<div class="tools"> <div class="tools">
<div style="width:100%;text-align: right;"> <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" v-if="isA4" @click="showSinglePage">切换单页</el-button>
<el-button title="打印" type="primary" icon="el-icon-printer" @click="printPDF">打印</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>
</div> </div>
<div style="min-height:1600px;margin: 0 auto;overflow: auto;"> <div style="min-height:1600px;margin: 0 auto;overflow: auto;">
<div v-if="showList" class="pdf-wrapper"> <!-- <el-scrollbar style="height:800px"> -->
<pdf <!-- </el-scrollbar> -->
v-for="item in pdfList" <div class="pdf-wrapper">
: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" -->
<pdf <pdf
:src="currentPdfUrl" :src="currentPdfUrl"
:page="page" :page="page"
@num-pages="pageCount = $event" @num-pages="thisPageCount = $event"
@page-loaded="onPageLoaded($event)" @page-loaded="onPageLoaded($event)"
ref="pdf" ref="pdf"
></pdf> ></pdf>
<!-- v-drag --> <!-- v-drag -->
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <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 pdf from 'vue-pdf'
import { jsPDF } from "jspdf";
import PdfUtils from '@/utils/pdfUtils.js';
export default { export default {
components: { pdf }, components: { pdf },
props: { props: {
@ -81,25 +74,43 @@ export default {
startPage: { startPage: {
type: Number, type: Number,
default: 1 default: 1
},
isInitA4:{
type: Boolean,
default: false
} }
}, },
data() { data() {
return { return {
resetCountKey:1, resetCountKey:1,
cacheFileList:this.pdfList,
loadPdfUrl:null, loadPdfUrl:null,
// 单页模式
cacheFileList:this.pdfList,
isA4:this.isInitA4,
page: 1, page: 1,
currentPage: 0, currentPage: 1,
pageCount: 0, pageCount: this.pdfList.length,
showList:false, // A4 模式
cacheA4FileList:[],
thisPageCount:1,
currentNum:1,
fileCount: this.pdfList.length,
scale: 20, scale: 20,
preScale: 20, preScale: 20,
// // A4
// newPrototype: [],
// newPrototypeValue: [],
// imgFiles: [], //pdf页数列表
// filelist: [], //列表
// //img
// realWidth: 720,
// dpr: '',
loading: false,
} }
}, },
computed: { computed: {
@ -119,12 +130,24 @@ export default {
watch: { watch: {
pdfList(){ pdfList(){
this.cacheFileList=this.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); // console.log(this.loadPdfUrl);
// this.showFirstFile(); // this.showFirstFile();
}, },
pageCount(value) { thisPageCount(value) {
if (value) this.page = this.startPage // console.log("============11111====================")
// console.log(this.isA4)
if(this.isA4){
// if (value) this.page = 1
this.doScale(75);
}else{
this.doScale(20);
}
} }
}, },
mounted() { mounted() {
@ -134,14 +157,20 @@ export default {
}, },
methods: { methods: {
showFirstFile(){ showFirstFile(){
if(this.cacheFileList&& this.cacheFileList.length>0){ if(this.isA4){
this.loadPdfUrl =this.pdfList[0].pdfUrl; // this.isA4=false;
this.resetCountKey++; 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={ let item={
"ydSn":ydSn, "billCode":billCode,
"pdfUrl":pdfUrl "pdfUrl":pdfUrl
} }
this.pdfList.push(item); 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){ doScale(val){
if (isNaN(val)) { if (isNaN(val)) {
this.scale = this.preScale this.scale = this.preScale
@ -176,9 +216,6 @@ export default {
this.scale = 200 this.scale = 200
} }
this.preScale = this.scale 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.width = parseInt(this.scale) + '%';
this.$refs.pdf.$el.style.margin = "0 auto"; this.$refs.pdf.$el.style.margin = "0 auto";
}, },
@ -188,50 +225,60 @@ export default {
zoomIn() { zoomIn() {
if (this.scale > 20) { if (this.scale > 20) {
this.scale += -5 this.scale += -5
this.$refs.pdf.$el.style.width = parseInt(this.scale) + '%' this.doScale(this.scale );
this.$refs.pdf.$el.style.margin = "0 auto";
} }
}, },
zoomOut() { zoomOut() {
if (this.scale < 200) { if (this.scale < 200) {
this.scale += 5 this.scale += 5
this.$refs.pdf.$el.style.width = parseInt(this.scale) + '%' this.doScale(this.scale );
this.$refs.pdf.$el.style.margin = "0 auto";
} }
}, },
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() { prePage() {
let count = this.currentPage - 1 console.log('this.pageCount', this.currentPage, this.pageCount)
if (count < 1) return if(!this.isA4){
this.page = count 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() { nextPage() {
// console.log('this.pageCount', this.currentPage, this.pageCount) console.log('this.pageCount', this.currentPage, this.pageCount)
let count = this.currentPage + 1 if(!this.isA4){
if (count > this.pageCount) return let fileNo = this.currentPage + 1
this.page = count 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() { downPDF() {
// 下载所有预览的PDF文件 // 下载所有预览的PDF文件
if(this.cacheFileList&&this.cacheFileList.length>0){ if(this.isA4){
this.cacheFileList.forEach(item=>{ if(this.cacheA4FileList&&this.cacheA4FileList.length>0){
this.downPdfFile(item.billCode,item.pdfUrl); 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){ downPdfFile(fileName,pdfUrl){
@ -249,9 +296,159 @@ export default {
// 打印 // 打印
printPDF() { printPDF() {
// this.$refs.pdf.print() // 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> </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"); 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 { .pdf-viewer {
position: relative; position: relative;
width: 100%; width: 100%;

View File

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

View File

@ -1,6 +1,8 @@
import { degrees, PDFDocument, rgb, StandardFonts } from 'pdf-lib'; import { degrees, PDFDocument, rgb, StandardFonts } from 'pdf-lib';
import axios from "axios"; import axios from "axios";
import Buffer from 'vue-buffer' import Buffer from 'vue-buffer'
import * as pdfjs from 'pdfjs-dist';
import * as pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry';
/** /**
* 浏览器打开新页签预览pdf * 浏览器打开新页签预览pdf
@ -215,6 +217,117 @@ export default class PdfUtils {
// console.log(+new Date() - startTime) // 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) { getBlobUrl(base64Str, fileName,isNeedDown=false) {
let blob = this.base64ToBlobNoHeader(base64Str); let blob = this.base64ToBlobNoHeader(base64Str);

View File

@ -18,7 +18,7 @@
</span> </span>
</template> </template>
<el-option <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" :key="item.tplCode"
:label="item.tplName" :label="item.tplName"
:value="item.tplCode" :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"> <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;"> <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> </div>
</el-dialog> </el-dialog>
@ -102,7 +102,12 @@
type: [Boolean], type: [Boolean],
required: false, required: false,
default: false default: false
} },
isInitA4:{
type: [Boolean],
required: false,
default: false
},
}, },
data() { data() {
return { return {

View File

@ -138,7 +138,7 @@
</el-col> </el-col>
<el-col :span="1.5"> <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> </el-col>