業(yè)務(wù)需求:財(cái)務(wù)ERP系統(tǒng)會(huì)計(jì)憑證的打印,科目打印需自動(dòng)拼接上輔助核算,如科目有外幣需打印外幣,對(duì)于長(zhǎng)憑證一張A4紙打印2張憑證,每張憑證固定5行。
大致實(shí)現(xiàn)思路:先把需要打印的頁(yè)面用canva畫(huà)轉(zhuǎn)成pdf,傳給后端保存,之后下載PDF預(yù)覽打印結(jié)果。
需要打印的憑證:

打印結(jié)果預(yù)覽:


有外幣:


**實(shí)現(xiàn)步驟:**
1. 安裝2個(gè)依賴 npm install jspdf html2canvas -D
2. 基于jspdf與html2canvas封裝一個(gè)全局打印方法,查詢憑證拼湊憑證數(shù)據(jù)(父組件),打印的模板(子組件)。具體實(shí)現(xiàn)代碼如下:
```
import html2Canvas from 'html2canvas'
import JsPDF from 'jspdf'
import axios from 'axios' //把pdf傳給后臺(tái)
import qs from 'qs'//需要轉(zhuǎn)換 用JOSN.stringify()不行
export default {
? install(Vue, options) {
? ? Vue.prototype.getPdf = function (dom) {
? ? ? html2Canvas(document.querySelector(dom), {
? ? ? ? allowTaint: true
? ? ? }).then(function (canvas) {
? ? ? ? let contentWidth = canvas.width
? ? ? ? let contentHeight = canvas.height
? ? ? ? //一頁(yè)pdf顯示html頁(yè)面生成的canvas高度;
? ? ? ? let pageHeight = contentWidth / 595.28 * 841.89
? ? ? ? //未生成pdf的html頁(yè)面高度
? ? ? ? let leftHeight = contentHeight
? ? ? ? //頁(yè)面偏移
? ? ? ? let position = 0
? ? ? ? //a4紙的尺寸[595.28,841.89],html頁(yè)面生成的canvas在pdf中圖片的寬高
? ? ? ? let imgWidth = 595.28
? ? ? ? let imgHeight = 592.28 / contentWidth * contentHeight
? ? ? ? let pageData = canvas.toDataURL('image/jpeg', 1.0)
? ? ? ? // 三個(gè)參數(shù),第一個(gè)方向,第二個(gè)尺寸,第三個(gè)尺寸格式
? ? ? ? let PDF = new JsPDF('', 'pt', 'a4')
? ? ? ? //有兩個(gè)高度需要區(qū)分,一個(gè)是html頁(yè)面的實(shí)際高度,和生成pdf的頁(yè)面高度(841.89)
? ? ? ? //當(dāng)內(nèi)容未超過(guò)pdf一頁(yè)顯示的范圍,無(wú)需分頁(yè)
? ? ? ? if (leftHeight < pageHeight) {
? ? ? ? ? PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
? ? ? ? } else {
? ? ? ? ? while (leftHeight > 0) {
? ? ? ? ? ? PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
? ? ? ? ? ? leftHeight -= pageHeight
? ? ? ? ? ? position -= 841.89
? ? ? ? ? ? //避免添加空白頁(yè)
? ? ? ? ? ? if (leftHeight > 0) {
? ? ? ? ? ? ? PDF.addPage()
? ? ? ? ? ? }
? ? ? ? ? }
? ? ? ? }
? ? ? ? var instance = axios.create({
? ? ? ? ? timeout: 10000,
? ? ? ? ? headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' }
? ? ? ? });
? ? ? ? instance.post(axios.defaults.baseURL + '/file/savepdf', qs.stringify({
? ? ? ? ? //output輸出文件流,PDF.output('datauristring')輸出base64文件流。
? ? ? ? ? file: PDF.output('datauristring')//base64的文件流
? ? ? ? }))
? ? ? ? ? .then(function (response) {
? ? ? ? ? ? window.open(axios.defaults.baseURL + '/file/showpdf/' + response.data.data.uri)//在新的窗口打開(kāi)
? ? ? ? ? ? // let dom=document.createElement('a');
? ? ? ? ? ? // dom.setAttribute('href',axios.defaults.baseURL+'/file/showpdf/'+response.data.data.uri);
? ? ? ? ? ? // dom.setAttribute('target','_blank');
? ? ? ? ? ? // dom.click();//模擬新的窗口打開(kāi)
? ? ? ? ? })
? ? ? ? ? .catch(function (error) {
? ? ? ? ? });
? ? ? })
? ? }
? }
}
```
3. 調(diào)用function
```
//打印憑證
printVoucher() {
? //日期
? let Voucherdate = new Date(this.pickerOptionsValue);
? let tableData = this.childData; //整個(gè)視圖數(shù)據(jù)
? let trData = this.childData.trData; //列數(shù)據(jù)
? //拼湊數(shù)據(jù)供打印使用,憑證頭,尾信息
? this.voucherHFdata = {
? ? voucherTitle: "記賬憑證", //記賬憑證
? ? billNum: this.billNum, //附件數(shù)
? ? accbookName: this.$store.state.accbookName, //賬套名
? ? date:Voucherdate.getFullYear() + "-" +(Voucherdate.getMonth() + 1) +"-" +Voucherdate.getDate(), //日期
? ? voucherNumber: this.voucherName + "-" + this.voucherNumberValue, //憑證號(hào):大-16
? ? lotal: tableData.DeCr_Total, //合計(jì)
? ? supervisor: "", //主管
? ? cashier: "", //出納
? ? auditor: "", //審核
? ? producer: this.$store.state.voucherProducer //制單
? };
? //需打印的列數(shù)據(jù)過(guò)濾
? // let listData = [];
? trData.forEach((n, i) => {
? ? //科目是否有輔助核算,有輔助核算,拼接上輔助核算eg:1001 庫(kù)存現(xiàn)金_銷(xiāo)售部
? ? let subjectVal =
? ? ? n.subject.val +
? ? ? (n.subject.auxData.length > 0 ? "_" : "") +
? ? ? n.subject.auxData
? ? ? ? .map(item1 => {
? ? ? ? ? return (
? ? ? ? ? ? item1.options.filter(item2 => {
? ? ? ? ? ? ? if (item1.value === item2.uid) return item2;
? ? ? ? ? ? })[0].name || ""
? ? ? ? ? );
? ? ? ? })
? ? ? ? .join("_");
? ? //是否有外幣,沒(méi)有外幣顯示空。有外幣匹配過(guò)濾出外幣名稱eg:RMB
? ? let curName =
? ? ? n.currency.currencyOptions
? ? ? ? .filter(item => {
? ? ? ? ? if (item.value === n.currency.currencyValue) return item;
? ? ? ? })
? ? ? ? .map(item => {
? ? ? ? ? return item.label;
? ? ? ? })[0] || "";
? ? this.listData[i] = {
? ? ? abstract: n.abstract.val, //摘要
? ? ? subject: subjectVal, //科目是否有輔助核算,有輔助核算,拼接上輔助核算
? ? ? currencyName: curName, //是否有外幣,沒(méi)有外幣顯示空。有外幣匹配過(guò)濾出外幣名稱eg:RMB
? ? ? showCur: n.currency.show,//false不顯示
? ? ? exchangeRate: n.currency.exchangeRate, //匯率
? ? ? original: n.currency.original, //原幣
? ? ? deVal: n.DeCr.De_val, //借方金額
? ? ? crVal: n.DeCr.Cr_val//貸方金額
? ? };
? });
? this.$refs.print.printvoucher(); //打印調(diào)動(dòng)子子組件的方法***
},
```
**模板:printVoucher.vue**
```
<!-- 打印模板 -->
? ? <print-voucher :voucherHFdata='voucherHFdata' :listData='listData' ref="print"></print-voucher>
<!-- 打印模板 -->
```
```
export default {
? name: "printVoucher",
? props: ["voucherHFdata", "listData"],//父組件的憑證頭尾和列集合數(shù)據(jù)
? data() {
? ? return {
? ? ? // htmlTitle: "voucher"
? ? ? tableData:[],
? ? ? hascur:false,//是否有外幣,操作2種模板
? ? };
? },
? mounted() {},
? methods: {
? ? printvoucher() {
? ? ? this.voucher5tr(
? ? ? //解決異步
? ? ? ? setTimeout(()=>{
? ? ? ? ? this.getPdf("#printVoucher");
? ? ? ? },1000)
? ? ? );
? ? },
? ? //每5列切成一張憑證
? voucher5tr() {
? ? ? let tr5 = []; //[[{},{},{},{},{}]]一維變多維
? ? ? let tr = this.listData;
? ? ? let index = 0;
? ? ? tr.forEach((n, i) => {
? ? ? ? if (!tr5[index]) {
? ? ? ? ? tr5[index] = [];
? ? ? ? }
? ? ? ? tr5[index].push(n);
? ? ? ? if (tr5[index].length === 5) {
? ? ? ? ? index++;
? ? ? ? }
? ? ? });
? ? ? let last = tr5[tr5.length - 1];
? ? ? for (let i = 0,l = 5 - last.length; i < l; i++) {
? ? ? ? last.push({
? ? ? ? ? abstract: "", //摘要
? ? ? ? ? showCur:false,//外幣不顯示
? ? ? ? ? subject: "", //科目是否有輔助核算,有輔助核算,拼接上輔助核算
? ? ? ? ? currencyName: "", //是否有外幣,沒(méi)有外幣顯示空。有外幣匹配過(guò)濾出外幣名稱eg:RMB
? ? ? ? ? exchangeRate: "", //匯率
? ? ? ? ? original: "", //原幣
? ? ? ? ? crVal: "", //貸方金額
? ? ? ? ? deVal: "" //借方金額
? ? ? ? });
? ? ? }
? ? ? this.tableData=tr5;
? ? ? this.tableData.forEach(n=>{
? ? ? ? n.forEach(n1=>{
? ? ? ? ? if(n1.showCur===true){
? ? ? ? ? ? this.hascur=true;
? ? ? ? ? }
? ? ? ? })
? ? ? })
? ? }
? }
};
```