前端生成 PDF 求指教 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
qingshui33
0.01D
V2EX    前端开发

前端生成 PDF 求指教

  •  
  •   qingshui33 333 天前 1700 次点击
    这是一个创建于 333 天前的主题,其中的信息可能已经有所发展或是发生改变。

    vue2 中有没有将 HTML 转成 PDF 的类库,要实现的功能是:页面中一半是固定的图片,一半的数据是动态的,并且包含图表、表格类,同时需要有目录,目录中每栏对应各自的页码。

    网上查了下,前端做的话,基本上都是使用 html2canvas + jsPDF 来实现,有帅哥美女用过这种方案吗,不知道是否能满足上面的功能?

    我个人理解是这种功能,放后端做会更好点,但是后端说这个涉及到图表之类的,后端实现起来会比较麻烦,所以想看看前端能不能做

    13 条回复    2025-07-10 16:51:58 +08:00
    pythagorasd
        1
    pythagorasd  
       333 天前
    问 gpt-4o 就行吧,我复制你的问题问的

    在 Vue2 中,可以使用以下类库和工具组合将 HTML 页面转为 PDF ,同时满足你的需求:

    1. html2canvas 和 jspdf

    html2canvas 用于将 HTML 元素渲染为 Canvas 图像。
    jspdf 用于生成 PDF 文件,可以支持动态内容、图片、表格和目录。

    安装

    npm install html2canvas jspdf

    示例代码

    以下是实现页面一半固定图片,一半动态数据(包含图表、表格)以及目录的示例代码:

    import html2canvas from "html2canvas";
    import jsPDF from "jspdf";

    export default {
    methods: {
    async generatePDF() {
    const pdf = new jsPDF("p", "mm", "a4"); // 创建 A4 纸张的 PDF
    let yOffset = 10; // PDF 初始 Y 位置

    // 添加固定的图片
    const imgElement = document.getElementById("fixed-image");
    const imgCanvas = await html2canvas(imgElement);
    const imgData = imgCanvas.toDataURL("image/png");
    pdf.addImage(imgData, "PNG", 10, yOffset, 90, 40); // 图片位置和大小
    yOffset += 50;

    // 添加动态内容
    const cOntentElement= document.getElementById("dynamic-content");
    const cOntentCanvas= await html2canvas(contentElement);
    const cOntentData= contentCanvas.toDataURL("image/png");
    pdf.addImage(contentData, "PNG", 10, yOffset, 190, 80); // 动态内容

    yOffset += 90;

    // 添加表格、图表
    const chartElement = document.getElementById("chart-content");
    const chartCanvas = await html2canvas(chartElement);
    const chartData = chartCanvas.toDataURL("image/png");
    pdf.addImage(chartData, "PNG", 10, yOffset, 190, 80);
    yOffset += 90;

    // 添加目录
    pdf.addPage(); // 新页面
    pdf.text("目录", 10, 10);
    pdf.text("1. 固定图片...............1", 10, 20);
    pdf.text("2. 动态数据...............2", 10, 30);
    pdf.text("3. 图表、表格.............3", 10, 40);

    // 保存 PDF
    pdf.save("document.pdf");
    },
    },
    };

    关键点

    固定图片部分:将图片元素渲染为 Canvas ,并嵌入 PDF 。
    动态数据部分:支持动态表格、图表等通过 HTML 转换。
    目录功能:手动添加目录页,并标记每个部分的页码。

    2. pdfmake

    如果你的数据主要是动态的,可以考虑使用 pdfmake 。它允许使用 JSON 格式定义 PDF 内容,支持表格、图表、目录等。

    安装

    npm install pdfmake

    示例代码

    import pdfMake from "pdfmake/build/pdfmake";
    import pdfFonts from "pdfmake/build/vfs_fonts";

    pdfMake.vfs = pdfFonts.pdfMake.vfs;

    export default {
    methods: {
    generatePDF() {
    const docDefinition = {
    content: [
    { text: "目录", style: "header", toc: { title: { text: "目录", style: "subheader" } } },
    { text: "固定图片", style: "subheader" },
    { image: "data:image/png;base64,...", width: 400 },
    { text: "动态数据", style: "subheader" },
    {
    table: {
    body: [
    ["列 1", "列 2", "列 3"],
    ["数据 1", "数据 2", "数据 3"],
    ],
    },
    },
    { text: "图表", style: "subheader" },
    { text: "此处插入图表..." },
    ],
    styles: {
    header: { fontSize: 22, bold: true },
    subheader: { fontSize: 18, bold: true },
    },
    };

    pdfMake.createPdf(docDefinition).download("document.pdf");
    },
    },
    };

    3. puppeteer (服务器端解决方案)

    如果需要更精确的排版和打印效果,推荐使用 puppeteer 在服器端生成 PDF 。这种方式可以直接将整个 HTML 页面渲染为 PDF 。

    安装

    npm install puppeteer

    示例代码

    const puppeteer = require("puppeteer");

    async function generatePDF() {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();

    await page.goto("http://localhost:8080", { waitUntil: "networkidle2" });
    await page.pdf({
    path: "output.pdf",
    format: "A4",
    printBackground: true,
    });

    await browser.close();
    }

    generatePDF();

    选择方案

    1. 前端生成( html2canvas + jspdf ):适合小型、动态页面。
    2. 动态内容为主( pdfmake ):适合需要大量动态生成内容(如表格、目录)。
    3. 高精度排版( puppeteer ):适合对 PDF 排版有较高要求的场景。
    carlton
        2
    carlton  
       333 天前
    看你写的这个复杂度,纯前端实现会有各种细节问题,更好的可能还是无头浏览器服务端生成,比如 puppeteer
    wyhooo
        3
    wyhooo  
       333 天前
    复杂页面用 puppeteer / playwright 是效果最好的。
    shadowyue
        4
    shadowyue  
       333 天前
    实话跟你说,前端生成 pdf 的工具,简单的内容还想,复杂的会有各种问题。pdf 格式太复杂了。
    SkywalkerJi
        5
    SkywalkerJi  
       333 天前 via Android
    这后端太水了吧。
    duanxianze
        6
    duanxianze  
       333 天前
    固定格式的,提前设计模板的还可以,比如各种合同或者申请书什么的,完全自定义的,肯定 bug 一大堆
    zcf0508
        7
    zcf0508  
       333 天前
    之前做过,生成很慢很慢,不过没做过目录。可以参考一下

    https://juejin.cn/post/7330203936286359578
    Puteulanus
        8
    Puteulanus  
       333 天前
    后端实现起来会比较麻烦

    前端实现起来更麻烦啊。。
    qingshui33
        9
    qingshui33  
    OP
       333 天前
    @pythagorasd 这个我也搜过,主要是害怕这些方案不靠谱,最后花了时间也没实现

    @carlton @wyhooo 好的,感谢
    youyouzi
        10
    youyouzi  
       333 天前
    html2canvas + jsPDF 确实是用这个方案,但是你说的好像还有额外的东西
    danbai
        11
    danbai  
    PRO
       333 天前
    我建议你们俩一起实现 你拿 html+css 填图 他来负责 转换
    qingshui33
        12
    qingshui33  
    OP
       333 天前
    @shadowyue @SkywalkerJi @duanxianze @zcf0508 @Puteulanus @youyouzi 明白了,看起来还是后端做起来更好一点

    @danbai 目前还有一些其他位置生成 PDF 的,不过没有这么复杂,但确实是这样,前端来写对应模板,后端负责转换,我也无力吐槽了,后端 Java 说他不会 HTML ,CSS ,然后前端去写 thymeleaf 模板
    lmq1919
        13
    lmq1919  
       99 天前
    可以看我的全新解决方案,前端直接将 dom 节点转成真正的 pdf ,而不是 canvas 图片式的,体积更小,可编辑,高清晰度,无需服务器 https://github.com/lmn1919/dompdf.js[呲牙]
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2852 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 08:04 PVG 16:04 LAX 01:04 JFK 04:04
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86