import { mkdir, writeFile } from 'node:fs/promises' import path from 'node:path' import { AlignmentType, BorderStyle, Document, Footer, HeadingLevel, Packer, Paragraph, Table, TableCell, TableRow, TextRun, WidthType, } from 'docx' const outputDir = path.resolve(process.cwd(), 'public/assets/templates') function createInfoTable() { const rows = [ ['合同名称', '{contractName}'], ['合同编码', '{contractCode}'], ['合同类型', '{contractType}'], ['合同金额', '人民币 {contractAmountDisplay}'], ['有效期', '{effectivePeriod}'], ['付款方式', '{paymentMethod}'], ['合同相对方', '{counterpartySummary}'], ['附件情况', '{attachmentSummary}'], ] return new Table({ width: { size: 100, type: WidthType.PERCENTAGE, }, rows: rows.map( ([label, value]) => new TableRow({ children: [ new TableCell({ width: { size: 22, type: WidthType.PERCENTAGE, }, children: [ new Paragraph({ children: [ new TextRun({ text: label, bold: true, }), ], }), ], }), new TableCell({ width: { size: 78, type: WidthType.PERCENTAGE, }, children: [ new Paragraph({ text: value, }), ], }), ], }), ), borders: { top: { style: BorderStyle.SINGLE, size: 1, color: 'D9DDE7' }, bottom: { style: BorderStyle.SINGLE, size: 1, color: 'D9DDE7' }, left: { style: BorderStyle.SINGLE, size: 1, color: 'D9DDE7' }, right: { style: BorderStyle.SINGLE, size: 1, color: 'D9DDE7' }, insideHorizontal: { style: BorderStyle.SINGLE, size: 1, color: 'E5E7EB' }, insideVertical: { style: BorderStyle.SINGLE, size: 1, color: 'E5E7EB' }, }, }) } function sectionTitle(text) { return new Paragraph({ text, heading: HeadingLevel.HEADING_2, spacing: { before: 280, after: 160, }, }) } function clause(text) { return new Paragraph({ text, spacing: { after: 160, line: 360, }, }) } async function writeTemplate(filename, config) { const doc = new Document({ sections: [ { properties: {}, footers: { default: new Footer({ children: [ new Paragraph({ alignment: AlignmentType.CENTER, children: [ new TextRun({ text: `${config.title} - 模板演示文档`, color: '6B7280', size: 18, }), ], }), ], }), }, children: [ new Paragraph({ text: config.title, heading: HeadingLevel.TITLE, alignment: AlignmentType.CENTER, spacing: { after: 240, }, }), new Paragraph({ alignment: AlignmentType.CENTER, children: [ new TextRun({ text: '用于演示合同发起模块模板填充与导出', italics: true, color: '64748B', }), ], spacing: { after: 280, }, }), sectionTitle('一、合同基础信息'), createInfoTable(), sectionTitle('二、合同正文示例'), ...config.clauses.map(clause), sectionTitle('三、补充说明'), clause('合同标的说明:{contractSubject}'), clause('业务备注:{businessRemark}'), clause('模板来源:{templateLabel}'), new Paragraph({ spacing: { before: 360, after: 180, }, children: [ new TextRun({ text: '甲方(业务方):__________________', }), ], }), new Paragraph({ spacing: { after: 180, }, children: [ new TextRun({ text: '乙方(相对方):__________________', }), ], }), new Paragraph({ children: [ new TextRun({ text: '签署日期:{signDate}', }), ], }), ], }, ], }) const buffer = await Packer.toBuffer(doc) await writeFile(path.join(outputDir, filename), buffer) } await mkdir(outputDir, { recursive: true }) await writeTemplate('purchase-contract-template.docx', { title: '采购合同模板', clauses: [ '甲乙双方经友好协商,就 {contractName} 项下采购合作事宜达成一致,并共同遵守本合同。', '第一条 合作内容:乙方根据甲方需求提供 {contractSubject},具体交付内容以双方确认的技术资料和补充协议为准。', '第二条 合同金额:本合同总金额为人民币 {contractAmountDisplay},付款方式采用 {paymentMethod}。', '第三条 合同期限:本合同自 {effectiveStart} 起生效,至 {effectiveEnd} 止。', ], }) await writeTemplate('sales-contract-template.docx', { title: '销售合同模板', clauses: [ '为明确双方权利义务,甲乙双方就 {contractName} 相关销售事项签订本合同。', '第一条 销售范围:甲方向乙方提供 {contractSubject},合同编码为 {contractCode}。', '第二条 金额与结算:本合同金额合计人民币 {contractAmountDisplay},采用 {paymentMethod} 完成结算。', '第三条 生效与终止:本合同有效期为 {effectivePeriod},双方应按约履约。', ], }) console.log(`Generated templates in ${outputDir}`)