V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
qingshui33
V2EX  ›  前端开发

前端生成 PDF 求指教

  •  
  •   qingshui33 · 21 小时 44 分钟前 · 398 次点击

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

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

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

    12 条回复    2024-11-18 22:27:53 +08:00
    pythagorasd
        1
    pythagorasd  
       21 小时 37 分钟前
    问 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  
       21 小时 31 分钟前
    看你写的这个复杂度,纯前端实现会有各种细节问题,更好的可能还是无头浏览器服务端生成,比如 puppeteer
    wyhooo
        3
    wyhooo  
       21 小时 30 分钟前
    复杂页面用 puppeteer / playwright 是效果最好的。
    shadowyue
        4
    shadowyue  
       21 小时 29 分钟前
    实话跟你说,前端生成 pdf 的工具,简单的内容还想,复杂的会有各种问题。pdf 格式太复杂了。
    SkywalkerJi
        5
    SkywalkerJi  
       21 小时 27 分钟前 via Android
    这后端太水了吧。
    duanxianze
        6
    duanxianze  
       21 小时 26 分钟前
    固定格式的,提前设计模板的还可以,比如各种合同或者申请书什么的,完全自定义的,肯定 bug 一大堆
    zcf0508
        7
    zcf0508  
       21 小时 26 分钟前
    之前做过,生成很慢很慢,不过没做过目录。可以参考一下

    https://juejin.cn/post/7330203936286359578
    Puteulanus
        8
    Puteulanus  
       21 小时 25 分钟前
    ——后端实现起来会比较麻烦

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

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

    @danbai 目前还有一些其他位置生成 PDF 的,不过没有这么复杂,但确实是这样,前端来写对应模板,后端负责转换,我也无力吐槽了,后端 Java 说他不会 HTML ,CSS ,然后前端去写 thymeleaf 模板 😂
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5341 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 07:25 · PVG 15:25 · LAX 23:25 · JFK 02:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.