Heim > Web-Frontend > js-Tutorial > Hauptteil

Wie verwende ich Node zum Generieren von Word-Dokumenten? Teilen Sie mithilfe von Bibliotheken

青灯夜游
Freigeben: 2022-07-28 19:48:37
nach vorne
2633 Leute haben es durchsucht

怎么利用node生成word文档?下面本篇文章给大家介绍一下使用node生成word文档的方法,分享一个实用库,聊聊该库的使用方法,希望对大家有所帮助!

Wie verwende ich Node zum Generieren von Word-Dokumenten? Teilen Sie mithilfe von Bibliotheken

最近有项目需要用到生成word文档,平时经常用的都是通过模板生成,里面变量使用占位符替换,好处是快捷、方便、简单、不需要通过代码调word样式,确定是很多库不支持图片绘制(很多都是付费功能),找一圈,发现一个很有意思的库,正好也满足我们的需求,特此分享一下

依赖

// https://docx.js.org/#/
npm i docx 

// https://www.npmjs.com/package/download
npm i download
Nach dem Login kopieren

说明,因为docx绘图只支持文件流,所以要把网络文件下载到本地转成buffer

代码

话不多说,上代码

import * as fs from "fs"
import { Document, Packer, Paragraph, TextRun, ImageRun, HeadingLevel, AlignmentType, convertInchesToTwip, Table, TableRow, TableCell, WidthType, VerticalAlign, BorderStyle } from "docx"
const download = require('download')

// 性别
enum Gender {
  Male = 'male',
  Female = 'female'
}

// 选手
type PlayerSchema = {
  name: string
  gender: string
  idCard?: string
  birthday?: string
  weight?: string
  remark?: string
  avatar?: string
  localAvatar?: string
  level: string
}
type GroupSchema = {
  // gender: Gender
  institution: string
  leader: string
  phone: string
  coach: string
  doctor: string
  players: PlayerSchema[]
}

// 所有数据
interface DataSchema  {
  [key: string]: GroupSchema
}

// 表格无边框
const noBoder = {
  top: {
    style: BorderStyle.NIL,
    size: 0,
    color: 'FFFFFF'
  },
  bottom: {
    style: BorderStyle.NIL,
    size: 0,
    color: 'FFFFFF'
  },
  left: {
    style: BorderStyle.NIL,
    size: 0,
    color: 'FFFFFF'
  },
  right: {
    style: BorderStyle.NIL,
    size: 0,
    color: 'FFFFFF'
  }
}

// 删除下载的照片及文件夹
function delStaticFile(groupNames: string[]) {
  for (let groupName of groupNames) {
    if (fs.existsSync(groupName)) {
      const files = fs.readdirSync(groupName)
      files.map((file: string) => {
        let curPath = groupName + "/" + file
        // 删除选手招聘
        fs.unlinkSync(curPath)
      })
      fs.rmdirSync(groupName)
    }
  }
}

// 生成word
async function generate (data: DataSchema) {
  const groupNames = Object.keys(data)

  // 比较粗糙的控制单元格长度逻辑
  const longHeaders = ['身份证号', '备注']

  // 下载远程资源到本地
  for (let groupName of groupNames) {
    if (!fs.existsSync(groupName)) {
      fs.mkdirSync(groupName)
    }

    const players = data[groupName].players
    for (let player of players) {
      if (player.avatar) {
        const avatarArr = player.avatar.split('/')
        const fileName = `${groupName}/${avatarArr[avatarArr.length - 1]}`

        if (!fs.existsSync(fileName)) {
          await download(player.avatar, groupName)
        }
        // 下载后的本地的资源路径
        player.localAvatar = fileName
      }
    }
  }

  // 需要多个文件合一
  const sections = groupNames.map(groupName => {
    const info = data[groupName]
    const { institution, leader, phone, coach, doctor, players } = info
    // 标头内容
    // let headers = ['序号', '照片', '姓名', '性别', '出生年月', '体重', '级别', '备注']
    let headers = ['序号', '照片', '姓名', '性别', '身份证号', '级别', '备注']

    // 表格数据
    let tableData: any[][] = []
    tableData.push(headers)

    // 填充选手信息
    let index = 1
    for (let player of players) {
      tableData.push([
        index.toString(),
        player.localAvatar || '',
        player.name,
        player.gender === Gender.Male ? '男' : '女',
        player.idCard,
        // player.birthday,
        // player.weight,
        player.level,
        player.remark,
      ])
      index++
    }

    // 表格渲染
    const tableRows = tableData.map(colums => {
      return new TableRow({
        children: colums.map(cell => {
        return new TableCell({
          verticalAlign: VerticalAlign.CENTER,
          width: {
            // 设置宽度 dxa长度单位 https://stackoverflow.com/questions/14360183/default-wordml-unit-measurement-pixel-or-point-or-inches
            size: longHeaders.some(j => cell === j) ? 3000 : 800,
            type: WidthType.DXA,
          },
          children: cell && colums.findIndex(i => i === cell) === 1 && cell !== '照片' ?
              [new Paragraph({
                alignment: AlignmentType.CENTER,
                children: [
                  new ImageRun({
                    // 将图片转化为buffer
                    data: fs.readFileSync(cell),
                    transformation: {
                      width: 100,
                      height: 129,
                    },
                  })
                ]
              })]:
            [new Paragraph({
              alignment: AlignmentType.CENTER,
              children:[
                new TextRun(cell || '')
              ]
            })]
          })
        })
      })
    })

    // 渲染报名表格
    const table = new Table({
      alignment: AlignmentType.CENTER,
      rows: tableRows
    })

    return {
      properties: {},
      children: [
        // new Paragraph({
        //   style: "wellSpaced",
        //   children: [
        //     new TextRun({
        //       text: '附件 4',
        //       color: '999999',
        //     })
        //   ],
        // }),

        // 表头信息
        new Paragraph({
          spacing: {
            before: 400,
            after: 400
          },
          style: "Title",
          text: `自 由 搏 击 比 赛 报 名 表(${groupName === Gender.Male ? '男子' : '女子'})`,
          heading: HeadingLevel.TITLE,
          alignment: AlignmentType.CENTER
        }),

        // 队伍信息
        new Table({
          style: "wellSpaced",
          alignment: AlignmentType.CENTER,
          borders: noBoder,
          rows: [
            new TableRow({
            children: [
              new TableCell({
                width: {
                  size: 600,
                  type: WidthType.DXA,
                },
                borders: noBoder,
                children: [
                  new Paragraph(`单位: `),
                ],
              }),
              new TableCell({
                width: {
                  size: 1800,
                  type: WidthType.DXA,
                },
                borders: noBoder,
                children: [
                  new Paragraph(`${institution}`)
                ],
              }),
              new TableCell({
                width: {
                  size: 700,
                  type: WidthType.DXA,
                },
                borders: noBoder,
                children: [
                  new Paragraph(`  领队: `),
                ],
              }),
              new TableCell({
                width: {
                  size: 1200,
                  type: WidthType.DXA,
                },
                borders: noBoder,
                children: [
                  new Paragraph(`${leader}`)
                ],
              }),
              new TableCell({
                width: {
                  size: 1100,
                  type: WidthType.DXA,
                },
                borders: noBoder,
                children: [
                  new Paragraph(`  联系电话: `),
                ],
              }),
              new TableCell({
                width: {
                  size: 1400,
                  type: WidthType.DXA,
                },
                borders: noBoder,
                children: [
                  new Paragraph(`${phone}`)
                ],
              }),
              new TableCell({
                width: {
                  size: 700,
                  type: WidthType.DXA,
                },
                borders: noBoder,
                children: [
                  new Paragraph(`  教练: `),
                ],
              }),
              new TableCell({
                width: {
                  size: 1300,
                  type: WidthType.DXA,
                },
                borders: noBoder,
                children: [
                  new Paragraph(`${coach}`)
                ],
              }),
              new TableCell({
                width: {
                  size: 700,
                  type: WidthType.DXA,
                },
                borders: noBoder,
                children: [
                  new Paragraph(`  队医: `),
                ],
              }),
              new TableCell({
                width: {
                  size: 1300,
                  type: WidthType.DXA,
                },
                borders: noBoder,
                children: [
                  new Paragraph(`${doctor}`)
                ],
              }),
            ],
          }),
          ]
        }),

        // 用于段落距离(table无法设置spacing属性)
        new Paragraph({
          spacing: {
            // 通过调整before值来调整段落渐进
            before: 400,
          },
          text: ``,
        }),

        // 选手信息
        table,

        // 印章和时间
        new Paragraph({
          style: "wellSpaced",
          children: [
            new TextRun({
              text: '\t\t\t\t报名单位章:\t\t\t\t\t\t',
            }),
            new TextRun({
              text: '年\t\t'
            }),
            new TextRun({
              text: '月\t\t'
            }),
            new TextRun({
              text: '日'
            })
          ]
        })
      ]
    }
  })

  // 创建整个文档
  const doc = new Document({
    styles: {
      paragraphStyles: [
        {
          id: "Title",
          name: "title",
          basedOn: "Normal",
          next: "Normal",
          quickFormat: true,
          run: {
              size: 30,
              bold: true,
              color: "000000"
          }
        },
        {
          id: "wellSpaced",
          name: "Well Spaced",
          basedOn: "Normal",
          quickFormat: true,
          paragraph: {
            indent: {
              left: convertInchesToTwip(0.5),
            },
            spacing: {
              before: 400,
            },
          },
        },
      ],
    },
    sections
  })
  
  // 生成word文档
  Packer.toBuffer(doc).then((buffer) => {
    fs.writeFileSync("enrolls.docx", buffer)
  })

  // 删除下载的选手照片
  delStaticFile(groupNames)
}

const group: GroupSchema = {
  institution: '江苏省南京市舜禹集团总部',
  leader: '王猛(男)',
  phone: '18861856665',
  coach: '刘国梁(男)',
  doctor: '杨永信(女)',
  players: [
    {
      name: '莱昂纳多迪卡普里奥',
      gender: Gender.Male,
      idCard: '320888199001019878',
      birthday: '1999-01-02',
      weight: '60kg',
      avatar: 'https://multi-xm.oss-cn-hangzhou.aliyuncs.com/atms/13.png',
      remark: '',
      level: '60kg'
    },
    {
      name: '张三',
      gender: Gender.Male,
      idCard: '320888199001019878',
      birthday: '1999-01-02',
      weight: '60kg',
      avatar: 'https://multi-xm.oss-cn-hangzhou.aliyuncs.com/atms/7.png',
      remark: '',
      level: '60kg'
    },
    {
      name: '张三',
      gender: Gender.Male,
      idCard: '320888199001019878',
      birthday: '1999-01-02',
      weight: '60kg',
      avatar: 'https://multi-xm.oss-cn-hangzhou.aliyuncs.com/atms/14.png',
      remark: '',
      level: '60kg'
    },
    {
      name: '张三',
      gender: Gender.Male,
      idCard: '320888199001019878',
      birthday: '1999-01-02',
      weight: '60kg',
      avatar: 'https://multi-xm.oss-cn-hangzhou.aliyuncs.com/atms/14.png',
      remark: '',
      level: '60kg'
    },
    {
      name: '张三',
      gender: Gender.Male,
      idCard: '320888199001019878',
      birthday: '1999-01-02',
      weight: '60kg',
      avatar: 'https://multi-xm.oss-cn-hangzhou.aliyuncs.com/atms/14.png',
      remark: '',
      level: '60kg'
    },
    {
      name: '张三',
      gender: Gender.Male,
      idCard: '320888199001019878',
      birthday: '1999-01-02',
      weight: '60kg',
      avatar: 'https://multi-xm.oss-cn-hangzhou.aliyuncs.com/atms/14.png',
      remark: '',
      level: '60kg'
    },
    {
      name: '张三',
      gender: Gender.Male,
      idCard: '320888199001019878',
      birthday: '1999-01-02',
      weight: '60kg',
      avatar: 'https://multi-xm.oss-cn-hangzhou.aliyuncs.com/atms/14.png',
      remark: '',
      level: '60kg'
    },
    {
      name: '张三',
      gender: Gender.Male,
      idCard: '320888199001019878',
      birthday: '1999-01-02',
      weight: '60kg',
      avatar: 'https://multi-xm.oss-cn-hangzhou.aliyuncs.com/atms/14.png',
      remark: '',
      level: '60kg'
    },
    {
      name: '张三',
      gender: Gender.Male,
      birthday: '1999-01-02',
      weight: '60kg',
      avatar: 'https://multi-xm.oss-cn-hangzhou.aliyuncs.com/atms/14.png',
      idCard: '320888199001019878',
      remark: '',
      level: '60kg'
    },
    {
      name: '张三',
      gender: Gender.Male,
      idCard: '320888199001019878',
      birthday: '1999-01-02',
      weight: '60kg',
      avatar: 'https://multi-xm.oss-cn-hangzhou.aliyuncs.com/atms/14.png',
      remark: '',
      level: '60kg'
    },
    {
      name: '张三',
      gender: Gender.Male,
      idCard: '320888199001019878',
      birthday: '1999-01-02',
      weight: '60kg',
      avatar: 'https://multi-xm.oss-cn-hangzhou.aliyuncs.com/atms/14.png',
      remark: '',
      level: '60kg'
    },
    {
      name: '张三',
      gender: Gender.Male,
      idCard: '320888199001019878',
      birthday: '1999-01-02',
      weight: '60kg',
      avatar: 'https://multi-xm.oss-cn-hangzhou.aliyuncs.com/atms/14.png',
      remark: '',
      level: '60kg'
    }
  ]
}

const data: DataSchema = {
  [Gender.Male]: group,
  [Gender.Female]: group,
}

generate(data)
Nach dem Login kopieren

更多node相关知识,请访问:nodejs 教程

Das obige ist der detaillierte Inhalt vonWie verwende ich Node zum Generieren von Word-Dokumenten? Teilen Sie mithilfe von Bibliotheken. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:segmentfault.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage