【问题标题】:Create pdf from a react component从反应组件创建 pdf
【发布时间】:2020-11-18 14:04:24
【问题描述】:

我希望从我的 react 组件创建一个 pdf。反应组件基本上是一个购物车页面,我想通过邮件与用户共享 PDF 文件中的购物车。我查看了各种选项,例如 jsPdf,但这种方法的问题在于它首先转换为画布,然后转换为图像,这使得从渲染的 pdf 中进行选择是不可能的。我的用户希望能够从 PDF 中选择文本。提前致谢。

【问题讨论】:

  • 我在我的项目中使用 jsPdf 来生成基于 od redux-store 数据的简单报告,并使用doc.text 方法呈现为 PDF。生成报告后,我绝对可以从 PDF 中选择文本。
  • 谢谢,你的评论成功了。实际上,我的问题是我有一张桌子,我认为最好让 WSYIWYG 感觉我应该首先使用 html2canvas 来捕获视口。但是 doc.text 绝对对文本很有帮助。然而,对于桌子,它不适合。在那里我找到了另一种方法,即 autotable,我使用了 jspdf-autotable 包。

标签: reactjs pdf pdf-generation


【解决方案1】:

我不久前已经这样做了。我使用了 Handlebars 和 html2pdf。使用 Handlebars,您可以在 HTML 文件中设置数据。它允许您使用 If 语句和 HTML 文件中的每个语句。
首先,我用这样的把手创建了 HTML...

    <html>

<head>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
        integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous" />
    <style>
        @page {
            @bottom-right {
    content: counter(page) " of " counter(pages);
   
            }
        }

        .hr-golden {
            border-color: #d7b436;
            height: 1px;
            color: #d7b436;
            background-color: #d7b436;
        }

        .hr-black {
            margin-top: 0rem;
            margin-bottom: 0rem;
            border-color: black;
            height: 1px;
            color: black;
            background-color: black;
        }

        .left-side span {
            font-size: 14px;
        }

        .right-side span {
            margin-left: 20px;
            font-size: 14px;
        }

        .tdl {
            text-align: left;
            font-size: 14px;
        }

        .tdr {
            text-align: right;
            font-size: 14px;
        }

        .golden {
            background-color: #d7b436;
        }

        .golden2 {
            background-color: #E5DDC0;
        }

        .golden-color {
            color: #d7b436;
        }

        .footer {
            width: 100%;
            height: 100px;
            display: flex;
            flex-flow: row;
            justify-content: space-between;
            font-family: Arial Narrow, sans-serif;
        }

        .price {
            display: flex;
            max-width: 50%;
            min-width: 50%;
            height: 5vh;
            font-size: 50px;
            justify-content: space-between;
        }


        p {
            font-size: 10px;
        }

        .priceTag {
            font-size: 15px;
        }

        
    </style>
</head>

<body>
    <div id="report" class="container">
        <div class="row">
            <div class="col-sm-auto col-md-12 text-right">
                <img src="../Loggo.png" height="auto" width="200px" style="margin-bottom: 30px;" />
            </div>
        </div>
        <hr class="hr-golden" />
        <div class="row">
            <div class="col-md-6 text-left left-side">
                <span>{{leftSideText1}}</span> <br />
                <span>{{leftSideText2}}</span> <br />
                <span>{{leftSideText3}}</span> <br />
                <span>{{leftSideText4}}</span>
            </div>
            <div class="col-md-1"></div>
            <div class="col-md-5 text-right right-side">
                <table style="width: 70%; margin-left: 30%;">
                    <tr>
                        <td class="tdl" style="font-size: 16px;">BESTELL-NR.</td>
                        <td class="tdr" style="font-size: 16px;">{{rightSideText1}}</td>
                    </tr>
                    <tr>
                        <td class="tdl">DATUM</td>
                        <td class="tdr">{{rightSideText2}}</td>
                    </tr>
                    <tr>
                        <td class="tdl">KUNDEN-NR.</td>
                        <td class="tdr">{{rightSideText3}}</td>
                    </tr>
                </table>
            </div>
        </div>
        <div class="row" style="margin-top: 50px;">
            <div class="col-sm-auto col-md-12 text-center">
                <table class="table  table-sm" style="width: 100%; border: none;">
                    <thead style="max-height: 14px;">
                        <th class="text-left">PRODUKT</th>
                        <th class="text-center">EINHEIT</th>
                        <th class="text-center">ANZAHL</th>
                        <th class="text-right">PREIS</th>
                        <th class="text-right">GESAMT</th>
                    </thead>
                    <tbody>

                        {{#each data.lists}}

                        {{#each this.orderers}}

                        {{#each this.products}}

                        <tr style="max-height: 14px">

                            <td class="text-left" style="font-size: 14px;">{{this.product.parent.name}}</td>
                            <td class="text-center" style="font-size: 14px;">{{this.product.weight}} kg</td>
                            <td class="text-center" style="font-size: 14px;">{{this.amount}}</td>
                            <td class="text-right">
                                <div
                                    style="display: flex; padding-left: 35%; justify-content: space-between; align-items: center;">
                                    <p class="priceTag" style="margin: 0">
                                        €
                                    </p>
                                    <p class="priceTag" style="margin: 0">
                                        {{this.product.price}}
                                    </p>
                                </div>
                            </td>
                            <td class="text-right">
                                <div
                                    style="display: flex; padding-left: 35%; justify-content: space-between; align-items: center;">
                                    <p class="priceTag" style="margin: 0">
                                        €
                                    </p>
                                    <p class="priceTag" style="margin: 0">
                                        {{this.total}}
                                    </p>
                                </div>
                            </td>
                        </tr>
                        {{/each}}
                        {{#if_eq this.name 'initiator'}}

                        {{else}}


                        <tr class="golden" style="max-height: 14px; font-size: 14px; align-items: center;">
                            <td colspan="3" class="text-left" style="padding-bottom: 8px;">{{this.name}}</td>
                            <td class="text-right" style="font-size: 14px;">SUMME</td>
                            <td class="text-right" style=" border-top: black 1px solid;">
                                <div
                                    style="display: flex; padding-left: 35%; justify-content: space-between; align-items: center;">
                                    <p class="priceTag" style="margin: 0">€ </p>
                                    <p class="priceTag" style="margin: 0">
                                        {{this.total}}
                                    </p>
                                </div>
                            </td>
                        </tr>
                        {{/if_eq}}
                        {{/each}}
                        <tr class="golden2" style="border-bottom: black 2px solid; padding-bottom: 20px;">
                            <td colspan="3" class="text-left" style="padding-bottom: 8px;">{{this.name}}</td>
                            <td class="text-right">SUMME</td>
                            <td class="text-right">
                                <div
                                    style="display: flex; padding-left: 35%; justify-content: space-between; align-items: center;">
                                    <p class="priceTag" style="margin: 0">
                                        €
                                    </p>
                                    <p class="priceTag" style="margin: 0">
                                        {{this.total}}
                                    </p>
                                </div>
                            </td>
                        </tr>
                        {{/each}}


                    </tbody>
                </table>
            </div>
        </div>

        <div class="row" style="margin-top: 20px;">
            <div class="col-md-6 text-left">
                <span>GESAMTSUMME INKL. STEUER</span>
            </div>
            <div class="col-md-6 text-right">
                <span>€ {{sumField5}}</span>
            </div>
        </div>
        <hr class="hr-black" />
        <div class="row">
            <div class="col-md-6 text-left">
                <span>GESAMTMENGE</span>
            </div>
            <div class="col-md-6 text-right">
                <span>{{sumField3}} KG</span>
            </div>
        </div>
        <hr class="hr-golden" />

        <div class="footer">
            <div class="footer-box-1">
                <p>
                    {{footerHeader}}
                    <br>
                    <span class="golden-color">
                        {{footerSection11}}
                    </span>
                    <br>
                    {{footerSection12}}
                </p>
            </div>
            <div class="footer-box-2">
                <p>
                    <br>
                    {{footerSection21}}<br>
                    {{footerSection22}}
                </p>
            </div>
            <div class="footer-box-3">
                <p>
                    <br>
                    {{footerSection31}}<br>
                    {{footerSection32}}
                </p>
            </div>
            <div class="footer-box-4">
                <p>
                    <br>
                    {{footerSection41}}<br>
                    {{footerSection42}}
                </p>
            </div>
            <div class="footer-box-5">
                <p>
                    <span class="golden-color">
                        <br>
                        {{footerSection51}}
                    </span>
                    <br>

                    {{footerSection52}}
                </p>
            </div>
        </div>
    </div>
</body>

</html>

然后我在我的 React-App 中创建了一个函数。单击下载按钮时调用此函数。 这个函数看起来像这样:

const ExportOrder = async e => {


    await $.get('../../templates/report.html', async html => {
        const template = await Handlebars.compile(html);
        const htmlString = await template({
            logoUrl: `https://media.macphun.com/img/uploads/customer/how-to/579/15531840725c93b5489d84e9.43781620.jpg`,
            leftSideText1: `${order.customer.firstName} ${order.customer.lastName}`,
            leftSideText2: `${order.shipping.address1}`,
            leftSideText3: `${order.shipping.postcode} ${order.shipping.city}`,
            leftSideText4: `${order.shipping.country}`,
            rightSideText1: `${order.orderNumber}`,
            rightSideText2: `${order.date ? dateFormat(order.date, 'yyyy-mm-dd') : dateFormat(Date(), 'yyyy-mm-dd')}`,
            rightSideText3: `${order.customer.customerId}`,
            data: data2, //get from DB or JSON or wherever you want
            footerHeader: `asdfsadf`,
            footerSection11: `Gasdfasdf`,
            footerSection12: `Basdf`,
            footerSection21: `Wasdf`,
            footerSection22: `IBAN: asdfasdf`,
            footerSection31: `T +asdfasdf`,
            footerSection32: `BIC: Sasdfasdf`,
            footerSection41: `INFOsdfasdf`,
            footerSection42: `asdfasdf`,
            footerSection51: `tesdf`,
            footerSection52: `Text`,
            sumField3: amountSum,
            sumField5: data2.total.toLocaleString('DE-DE', { maximumFractionDigits: 2, minimumFractionDigits: 2 })
        });

        const opt = {
            margin: 0.5,
            filename: `bestellung.pdf`,
            image: { type: 'jpeg', quality: 0.98 },
            html2canvas: { scale: 2 },
            jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait' },
            dpi: 300,
            letterRendering: true,
            useCORS: true,
            pagebreak: { mode: ['avoid-all', 'css', 'legacy'] }
        };

        html2pdf().set(opt).from(htmlString)
    }, 'text');

}

函数中的第一行是使用您首先创建的 jQuery 获取 HTML。然后它会用数据填充 HTML,你会得到一个 htmlstring。 html2pdf 在最后一行使用给定的选项生成 pdf。

【讨论】:

  • 您是从服务器获取 html 页面还是驻留在客户端上?
  • 它将在客户端生成。 HTML也在客户端
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-20
  • 2019-12-06
相关资源
最近更新 更多