业务场景
后台管理系统,希望实现将表格中每条数据打印成标签贴纸的功能,每排一张标签,有几条数据,就打印几张标签。标签类型分两种,一种普通的仅文字内容,一种是文字+二维码。
效果图如下(这里使用的思源黑体,但是字体有点模糊,项目中换成文泉驿微米黑了)
实现思路
使用jspdf生成pdf,调用浏览器的打印功能,二维码使用qrcode包生成。
- 生成pdf基础配置:纸张大小、方向,标签贴纸大小、字号大小等。
- 判断标签类型,若有二维码则先生成二维码,再生成pdf。
- 页面中调用。
具体步骤
1、安装并引入依赖包
npm install jspdf qrcode
//printLable.js文件中
import jsPDF from "jspdf"; //生成pdf
var QRCode = require("qrcode"); //生成二维码
2、生成pdf基础配置
//标签大小为50mm×30mm,对应此处的一页的宽高
const printConfig = {
orientation: "l", // 纸张方向 l:横向 p:纵向
unit: "mm", // 单位 ("pt","mm","cm","m","in"或"px")
pageWidth: 50, // 一页宽度
pageHeight: 30, // 一页高度
left: 20, // 标签左边距
picWidth: 17, // 二维码宽度
picHeight: 17, // 二维码高度
fontSize: 6, // 字号
titleFontSize: 8 // 标题字号
};
3、设置pdf
引入中文
jspdf不支持中文,所以需要自己引入中文字体文件资源。常见的免费商用字体有思源黑体、思源宋体等,但是实际使用思源黑体下来,发现有些汉字笔画深浅不一,效果模糊。考虑到标签字号较小,换成了文泉驿微米黑字体。
网上搜索下载字体的ttf文件。
- 方法一:通过 rawgit.com/MrRio/jsPDF… ,将字体ttf文件转为js文件,用记事本(用vscode打开会卡崩溃)打开如下图:
可以看到该js文件实际已经通过addFileToVFS()和addFont()方法注册并添加上字体了,所以js文件中引入即可使用。
//printLabel.js文件中
import "./wqhm-normal.js"
pdf.setFont("wqhm", "normal"); //此处第一个参数要同addFont()方法中的第二个参数
这种方法将字体文件直接存放在前端项目代码中,由于我司前端部署时有大小限制,因此实际使用的是下面的方法二。
- 方法二:将字体的TTF文件存放到服务端(这里用的是华为云服务器),后端提供文件地址,前端请求获取到ttf文件,将其从arrayBuffer类型转换为base64字符串,再注册添加该字体 (此方法需要给服务器响应头中的access-control-allow-origin设为允许跨域)。
//store/modules/printFont.js 文件
const state = {
printFont: null
};
const mutations = {
SET_PRINT_FONT(state, font) {
state.printFont = font;
}
};
const actions = {
async loadPrintFont({ commit }) {
try {
const url = "https://huaweicloud.com/wqwmh.ttf"; //服务器存放文件地址
const res = await fetch(url);
const arrayBuffer = await res.arrayBuffer();
const printFont = await new Promise((resolve, reject) => {
const blob = new Blob([arrayBuffer], { type: "application/octet-stream" });
const reader = new FileReader();
reader.onload = () => {
const dataUrl = reader.result;
const base64 = dataUrl.split(",")[1];
resolve(base64);
};
reader.onerror = () => reject();
reader.readAsDataURL(blob);
});
commit("SET_PRINT_FONT", printFont);
} catch (error) {
console.error("字体加载错误:", error);
}
}
};
export default {
namespaced: true,
state,
mutations,
actions
};
//store/index.js
import printFont from "./modules/printFont";
Vue.use(Vuex);
const store = new Vuex.Store({
modules: {
printFont
}
});
export default store;
//App.vue 页面中触发 即一进入页面就下载字体文件资源并保存到vuex中
mounted() {
this.$store.dispatch("printFont/loadPrintFont");
}
生成pdf
完整代码
//printLabel.js
import jsPDF from "jspdf";
import QRCode from "qrcode";
// 生成二维码的函数(异步)
const generateQRCodeList = async data => {
try {
const qrPromises = data.map(item => QRCode.toDataURL(item.sysNum));
return await Promise.all(qrPromises);
} catch (err) {
console.error("二维码生成失败:", err);
throw err;
}
};
// 生成PDF文档
const generatePDF = (data, imageList, config, base64Font) => {
const { orientation, pageWidth, pageHeight, picWidth, picHeight, left, fontSize, titleFontSize } = config;
const pdf = new jsPDF(orientation, "mm", [pageWidth, pageHeight]);
// 添加中文字体支持
pdf.addFileToVFS("normal", base64Font);
pdf.addFont("normal", "wqhm", "normal");
pdf.setFont("wqhm", "normal");
const processPage = (item, base64, index) => {
if (index > 0) {
pdf.addPage([pageWidth, pageHeight], orientation);
}
pdf.setFontSize(titleFontSize);
pdf.text("我是标题", 7, 5);
if (base64) {
pdf.addImage(base64, "JPEG", 2, 6, picWidth, picHeight, "", "FAST");
} else {
pdf.setFontSize(fontSize);
pdf.text("LOGO", 2, 3);
}
pdf.setFontSize(fontSize);
pdf.text(`管理编号:${item.sysNum}`, left, 10);
pdf.text(`资产名称:${item.deviceName}`, left, 15);
pdf.text(`规格型号:${item.deviceModel}`, left, 20);
pdf.text(`购置时间:${item.buyTime} 使用单位:${item.useUnit}`, 2, 26);
};
data.forEach((item, index) => {
processPage(item, imageList?.[index], index);
});
return pdf;
};
// 主入口函数
export const generatePrint = async (data, hasQRcode = false, base64Font) => {
// 打印配置
const printConfig = {
orientation: "l",
unit: "mm",
pageWidth: 50,
pageHeight: 30,
left: 20,
picWidth: 17,
picHeight: 17,
fontSize: 6,
titleFontSize: 8
};
try {
const imageList = hasQRcode ? await generateQRCodeList(data) : null;
const pdf = generatePDF(data, imageList, printConfig, base64Font);
// 在新窗口打开并打印
window.open(pdf.output("bloburl"), "_blank").print();
} catch (error) {
console.error("打印生成失败:", error);
alert("打印生成失败,请查看控制台日志");
}
};
页面中调用
//index.vue 文件
import { generatePrint } from "@/utils/printLabel";
printLabel(row, hasQRcode) {
generatePrint(row, hasQRcode, this.base64Font);
},