使用简单的模板创建强大的文档: 使用TPL文件将HTML转换为PDF

968bc117f6ae5d66d3272aaecce8dc37.jpeg

本文探讨了如何在ABP框架中使用TPL文件将HTML模板转换为PDF文档.了解这种强大的方法如何简化文档创建,使开发人员能够高效地生成高质量的 PDF。和我一起演练利用 TPL 文件增强应用程序文档生成功能的步骤和最佳实践。

场景

我们的系统中有广告。当我们的广告获得管理员的批准后,我们将发送已批准的广告报告并发送邮件以获取信息。

注意: 我将使用 Puppeteer Sharp 库生成 pdf。

广告批准报告 TPL 文件部分

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ L "AdvertApprovedReport" }}</title>
</head>
<body>
    <div class="container">
        <div class="report-header">
            <div class="report-to">
                <strong>{{ L "ReportTo" }}</strong><br>
                {{ model.warehouse_name }}<br>
                {{ model.warehouse_postal_code }}, {{ model.warehouse_place }}<br>
                {{ model.warehouse_country }}
            </div>
            <div class="report-info">
                <div><strong>{{ L "AdvertCode" }}:</strong> {{ model.advert_code }}</div>
                <div><strong>{{ L "ApproveDate" }}:</strong> {{ model.approve_date }}</div>
            </div>
        </div>

        <h4>{{ L "AdvertItems" }}</h4>
        <table>
            <thead>
                <tr>
                    <th>{{ L "ProductName" }}</th>
                    <th>{{ L "UnitPrice" }}</th>
                    <th>{{ L "Quantity" }}</th>
                    <th>{{ L "TotalCost" }}</th>
                </tr>
            </thead>
            <tbody>
                {{ for item in model.advert_items }}
                <tr>
                    <td>{{ item.product_name }}</td>
                    <td> &euro;{{ item.unit_price }}</td>
                    <td>{{ item.quantity }}</td>
                    <td> &euro;{{ item.total_cost }}</td>
                </tr>
                {{ end }}
            </tbody>
        </table>

        <h4>{{ L "ApprovedAdvertItems" }}</h4>
        <table>
            <thead>
                <tr>
                    <th>{{ L "ProductName" }}</th>
                    <th>{{ L "UnitPrice" }}</th>
                    <th>{{ L "Quantity" }}</th>
                    <th>{{ L "TotalCost" }}</th>
                </tr>
            </thead>
            <tbody>
                {{ for item in model.approved_advert_items }}
                <tr>
                    <td>{{ item.product_name }}</td>
                    <td> &euro;{{ item.unit_price }}</td>
                    <td>{{ item.quantity }}</td>
                    <td> &euro;{{ item.total_cost }}</td>
                </tr>
                {{ end }}
            </tbody>
        </table>

        <div class="total">{{ L "Subtotal" }}: &euro;{{ model.subtotal }}</div>
        <div class="total">(-) {{ L "FreightPrice" }}:  &euro;{{ model.freight_price }}</div>
        <div class="total">(-) {{ L "ServiceFee" }}:  &euro;{{ model.service_fee }}</div>
        <div class="total"></div>
        <div class="expected-total">{{ L "ExpectedTotal" }}: &euro;{{ model.expected_total }}</div>
    </div>
</body>
</html>

我们需要在派生自 TemplateDefinitionProvider 的类中声明这一点。

public class AdvertEmailTemplateDefinitionProvider : TemplateDefinitionProvider  
{  
    public override void Define(ITemplateDefinitionContext context)  
    {  
  
        context.Add(  
            new TemplateDefinition(  
                AdvertEmailTemplates.AdvertApprovedReportBody,  
                displayName: LocalizableString.Create\<KhanPalettenResource>(  
                    $"TextTemplate:{AdvertEmailTemplates.AdvertApprovedReport}"  
                ),  
                layout: StandardEmailTemplates.Layout,  
                localizationResource: typeof(KhanPalettenResource)  
            ).WithVirtualFilePath("/Adverts/Templates/AdvertApprovedReport.tpl", true)  
        );  
    }  
}  
  
public class AdvertEmailTemplates  
{  
    public const string AdvertApprovedReport = "AdvertApprovedReport";  
}

电子邮件运营部分

public async Task SendAdvertReportAsync(
     Advert advert,
     List<string> customerMails,
     List<string> adminMails
 )
 {
     var attachmentModel = new
     {
         warehouse_name = advert?.Warehouse?.Name ?? string.Empty,
         warehouse_postal_code = advert?.Warehouse?.PostalCode?.Code ?? string.Empty,
         warehouse_place = advert?.Warehouse?.Place?.Name ?? string.Empty,
         warehouse_country = advert?.Warehouse?.Country?.Name ?? string.Empty,
         advert_code = advert?.TransactionNumber ?? string.Empty,
         approve_date = DateTime.Now.ToString("dd.MM.yyyy HH:mm"),
         advert_items = advert?.Items?.Select(x => new
         {
             product_name = x?.Product?.Name ?? string.Empty,
             quantity = x?.Quantity ?? 0,
             total_cost = x?.TotalCost ?? 0m,
             unit_price = x?.UnitPrice ?? 0m,
         }) ?? Enumerable.Empty<object>(),
         approved_advert_items = advert?.ApprovedItems?.Select(x => new
         {
             product_name = x?.Product?.Name ?? string.Empty,
             quantity = x?.Quantity ?? 0,
             total_cost = x?.TotalCost ?? 0m,
             unit_price = x?.UnitPrice ?? 0m,
         }) ?? Enumerable.Empty<object>(),
         subtotal = advert?.ApprovedItems?.Sum(x => x.TotalCost) ?? 0m,
         freight_price = advert?.TransportationPrice?.Price ?? 0m,
         service_fee = advert?.ApprovedItems?.Sum(x =>
             x.Quantity * x.ServiceFees.Sum(fee => fee.ServicePrice.Price)
         ) ?? 0m,
         expected_total = (advert?.ApprovedItems?.Sum(x => x.TotalCost) ?? 0m)
             - (advert?.TransportationPrice?.Price ?? 0m)
             - (
                 advert?.ApprovedItems?.Sum(x =>
                     x.Quantity * x.ServiceFees.Sum(fee => fee.ServicePrice.Price)
                 ) ?? 0m
             )
     };

     var emailAttachmentContent = await _templateRenderer.RenderAsync(
         AdvertEmailTemplates.AdvertApprovedReport,
         attachmentModel
     );
     byte[] pdfBytes = await _pdfService.ConvertHtmlToPdf(emailAttachmentContent);

     var mailMessage = new MailMessage
     {
         Subject = _stringLocalizer["AdvertApprovedReport"],
         IsBodyHtml = true,
         Body = emailBodyContent,
     };

     foreach (var customer in customerMails)
     {
         mailMessage.To.Add(customer);
     }

    // from our code the scenario was like this:)
     foreach (var admin in adminMails)
     {
         mailMessage.Bcc.Add(admin);
     }

     mailMessage.Attachments.Add(
         new Attachment(new MemoryStream(pdfBytes), "AdvertApprovedReport.pdf", "application/pdf")
     );

     await _emailSender.SendAsync(mailMessage);
 }

工作部分

public class SendAdvertApprovedInfoLinkJob
    : AsyncBackgroundJob<SendAdvertApprovedInfoLinkArgs>,
        ITransientDependency
{
    private readonly IAdvertRepository _advertRepository;
    private readonly IAdvertEmailer _advertEmailer;

    public SendAdvertApprovedInfoLinkJob(
        IAdvertRepository advertRepository,
        IAdvertEmailer advertEmailer
    )
    {
        _advertRepository = advertRepository;
        _advertEmailer = advertEmailer;
    }

    public override async Task ExecuteAsync(SendAdvertApprovedInfoLinkArgs args)
    {
        var advert = await _advertRepository.GetAsync(args.AdvertId);

        var ownerEmail = advert
            .Company.Members.Where(x => x.IsOwner)
            .Select(x => x.User.Email)
            .FirstOrDefault();
        Check.NotNullOrEmpty(ownerEmail, nameof(ownerEmail));

        await _advertEmailer.SendAdvertApprovedInfoLinkAsync(ownerEmail, args.TransactionNumber);
    }
}

Pdf转换部分

public class PdfService : IPdfService
  {
      public async Task<byte[]> ConvertHtmlToPdf(string htmlContent)
      {
          await new BrowserFetcher().DownloadAsync();

          using (
              var browser = await Puppeteer.LaunchAsync(
                  new LaunchOptions { Headless = true, Args = ["--no-sandbox"] }
              )
          )
          using (var page = await browser.NewPageAsync())
          {
              await page.SetContentAsync(htmlContent);
              var pdfBytes = await page.PdfDataAsync();
              return pdfBytes;
          }
      }
    }

让我们调用我们的方法

[HttpPut("/api/app/advert-approval/{id}/{approvedItemId}")]
 public async Task<AdvertDto> UpdateAsync(
     Guid id,
     long approvedItemId,
     UpdateAdvertItemDto input
 )
 {
     var advert = await _advertRepository.GetAsync(id);
     var item = advert.GetApprovedItem(approvedItemId);

     item.SetQuantity(input.Quantity);

     var advertManager = GetAdvertManager(advert.AppModule);
     await advertManager.CalculateTotalCost(advert);
     await _advertRepository.UpdateAsync(advert);

     await _backgroundJobManager.EnqueueAsync(new SendAdvertReportArgs(advert.Id));

     return ObjectMapper.MapToDto(advert);
 }

在这里,当我们更新广告状态时,我们称之为电子邮件发送部分。

pdf文件截图:

6e7030ddd61f10cdffda3eca5f37e2b2.jpeg

邮件屏幕截图:

0c7c124cbbc8766245f7657924403511.png

这就是我要分享的关于这个话题的全部内容。我希望这篇文章对每个人都有信息和富有成效。

如果你喜欢我的文章,请给我一个赞!谢谢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值