前端标签贴纸打印功能详细版

488 阅读3分钟

业务场景

后台管理系统,希望实现将表格中每条数据打印成标签贴纸的功能,每排一张标签,有几条数据,就打印几张标签。标签类型分两种,一种普通的仅文字内容,一种是文字+二维码。

效果图如下(这里使用的思源黑体,但是字体有点模糊,项目中换成文泉驿微米黑了)

image.png

实现思路

使用jspdf生成pdf,调用浏览器的打印功能,二维码使用qrcode包生成。

  1. 生成pdf基础配置:纸张大小、方向,标签贴纸大小、字号大小等。
  2. 判断标签类型,若有二维码则先生成二维码,再生成pdf。
  3. 页面中调用。

具体步骤

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打开会卡崩溃)打开如下图: 微信图片_20250618174642.png

可以看到该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);
 },