You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用TypeScript基于WebService数据生成、打印并下载PDF销售合同

Hey there! Let me walk you through a practical, battle-tested approach to generate a sales contract PDF using TypeScript, pull data from a WebService, and add print/download functionality. I’ve built similar workflows for e-commerce platforms before, so here’s what works smoothly:

1. Tech Stack Selection

First, let’s pick tools that play well with TypeScript and simplify PDF generation:

  • pdfmake: A client-side library with excellent TypeScript support, perfect for building structured PDFs (like contracts with tables and styled text) without needing server-side processing.
  • axios: A promise-based HTTP client to fetch data from your WebService—clean, type-safe, and easy to integrate.
2. Step-by-Step Implementation

2.1 Set Up Dependencies

Start by installing the required packages and their TypeScript types:

npm install pdfmake axios @types/pdfmake @types/axios --save

2.2 Fetch Data from WebService

First, define a TypeScript interface to enforce the structure of your contract data—this keeps your code type-safe and catches mismatches early:

interface SalesContractData {
  contractNumber: string;
  customerName: string;
  customerAddress: string;
  items: Array<{
    productName: string;
    quantity: number;
    unitPrice: number;
    total: number;
  }>;
  totalAmount: number;
  signingDate: string;
}

async function fetchContractData(contractId: string): Promise<SalesContractData> {
  try {
    // Replace with your WebService endpoint
    const response = await axios.get(`/api/contracts/${contractId}`);
    return response.data;
  } catch (error) {
    console.error('Failed to fetch contract data:', error);
    throw new Error('Could not retrieve contract information. Please try again later.');
  }
}

2.3 Generate the PDF Contract

Use pdfmake to build a styled, data-driven PDF. We’ll define a document structure that maps directly to your contract data:

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

// Load default fonts (required for pdfmake to render text)
pdfMake.vfs = pdfFonts.pdfMake.vfs;

function generateContractPdf(data: SalesContractData) {
  const docDefinition = {
    content: [
      { text: 'OFFICIAL SALES CONTRACT', style: 'header' },
      { text: `Contract #: ${data.contractNumber}`, style: 'subheader' },
      { text: `Date: ${data.signingDate}`, style: 'subheader' },
      { text: '\n' },
      
      { text: 'Customer Details', style: 'sectionHeader' },
      { text: `Name: ${data.customerName}` },
      { text: `Address: ${data.customerAddress}` },
      { text: '\n' },
      
      { text: 'Items Purchased', style: 'sectionHeader' },
      {
        table: {
          headerRows: 1,
          widths: ['*', 'auto', 'auto', 'auto'], // Responsive column widths
          body: [
            ['Product', 'Quantity', 'Unit Price', 'Total'],
            ...data.items.map(item => [
              item.productName,
              item.quantity.toString(),
              `$${item.unitPrice.toFixed(2)}`,
              `$${item.total.toFixed(2)}`
            ]),
            [{ text: 'Grand Total', colSpan: 3, alignment: 'right' }, {}, {}, `$${data.totalAmount.toFixed(2)}`]
          ]
        }
      },
      { text: '\n' },
      
      { text: 'Terms & Conditions', style: 'sectionHeader' },
      { text: '1. Payment is due within 30 days of contract signing.' },
      { text: '2. All products are sold as-is, unless covered by a manufacturer warranty.' },
      { text: '3. The seller retains ownership until full payment is received.' },
      { text: '\n' },
      
      { text: 'Customer Signature:', style: 'signature' },
      { text: '_________________________', style: 'signatureLine' },
      { text: data.customerName, style: 'signatureName' }
    ],
    styles: {
      header: {
        fontSize: 22,
        bold: true,
        alignment: 'center',
        margin: [0, 0, 0, 15]
      },
      subheader: {
        fontSize: 14,
        bold: true,
        alignment: 'right'
      },
      sectionHeader: {
        fontSize: 16,
        bold: true,
        margin: [0, 10, 0, 5]
      },
      signature: {
        fontSize: 14,
        margin: [0, 25, 0, 5]
      },
      signatureLine: {
        margin: [0, 0, 0, 5]
      },
      signatureName: {
        fontSize: 12,
        italic: true
      }
    },
    pageSize: 'A4',
    pageMargins: [40, 60, 40, 60]
  };

  return pdfMake.createPdf(docDefinition);
}

2.4 Add Download & Print Functionality

Finally, tie everything together into a function that fetches data, generates the PDF, and lets users download or print it:

async function handleContractActions(contractId: string) {
  try {
    const contractData = await fetchContractData(contractId);
    const pdf = generateContractPdf(contractData);

    // Trigger download with a meaningful filename
    pdf.download(`SalesContract_${contractData.contractNumber}.pdf`);

    // Open browser's print dialog for the PDF
    pdf.print();
  } catch (error) {
    alert('Oops! Failed to generate your contract: ' + (error as Error).message);
  }
}

// Example: Attach to a button click
// document.getElementById('generateContractBtn')?.addEventListener('click', () => {
//   handleContractActions('CON-2024-001');
// });
3. Key Practical Tips
  • Type Safety: Always define interfaces for your WebService responses—this prevents unexpected data from breaking your PDF layout.
  • Font Customization: If you need branded fonts, you can embed them into pdfmake by following their font embedding guide (no external links needed, just base64 encode your font files and add them to the pdfMake.vfs object).
  • Error Handling: Add user-friendly alerts for failed data fetch or PDF generation—don’t leave users guessing what went wrong.
  • Testing: Test edge cases like long customer addresses, empty item lists, or large numbers to ensure the PDF layout doesn’t break.
4. Alternatives If You Need Different Features
  • jsPDF: Great if you need to draw custom shapes or have full control over pixel-perfect layouts, but requires more manual work than pdfmake.
  • html2pdf.js: Perfect if you already have an HTML template for your contract—just convert the DOM element directly to a PDF, which is handy for reusing existing UI components.

内容的提问来源于stack exchange,提问作者Lonna

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 08:13:36