怎么利用node生成word文档?使用库分享

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

怎么利用node生成word文档?使用库分享

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

依赖

// https://docx.js.org/#/npm i docx // https://www.npmjs.com/package/downloadnpm i download

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

AiTxt 文案助手 AiTxt 文案助手

AiTxt 利用 Ai 帮助你生成您想要的一切文案,提升你的工作效率。

AiTxt 文案助手 15 查看详情 AiTxt 文案助手

代码

话不多说,上代码

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)    }  }}// 生成wordasync 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: 'tttt报名单位章:tttttt',            }),            new TextRun({              text: '年tt'            }),            new TextRun({              text: '月tt'            }),            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)

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

以上就是怎么利用node生成word文档?使用库分享的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/548680.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
苹果手机可以边打电话边录音吗_苹果手机通话中录音可行性与方法
上一篇 2025年11月9日 19:03:06
中通快递官方物流查询平台 中通运单号查询入口
下一篇 2025年11月9日 19:03:12

相关推荐

  • 开源免费PHP工具 PHP开发效率提升利器

    推荐开源免费PHP开发工具以提升效率:VS Code、Sublime Text轻量高效,PhpStorm专业强大;调试用Xdebug、Kint、Ray;依赖管理选Composer;代码质量工具包括PHPStan、Psalm、PHP_CodeSniffer;数据库管理可用%ignore_a_1%MyA…

    2026年5月10日
    000
  • JavaScript 高效判断页面所有复选框状态的技巧与实践

    本文旨在提供一套高效且专业的javascript方法,用于判断网页中所有复选框的选中状态。我们将探讨如何利用`array.some()`快速确定是否有未选中的复选框(进而判断是否全部选中),以及如何使用`array.filter()`统计选中和未选中的复选框数量。通过优化dom元素选择和数组操作,提…

    2026年5月10日
    000
  • JavaScript Electron桌面应用

    答案:使用JavaScript开发%ignore_a_1%桌面应用需结合Web技术与Node.js,通过主进程管理窗口、渲染进程展示界面,并利用IPC通信,调用系统功能如文件对话框,最后用electron-builder打包发布,注意安全与进程职责分离。 用JavaScript开发Electron桌…

    2026年5月10日
    000
  • OSMnx中interpolate_points函数详解及街道细分与图构建实践

    本文详细介绍了osmnx库中`utils_geo.interpolate_points`函数的使用方法,特别是其返回的python生成器类型。我们将学习如何处理生成器输出,并提供一个完整的教程,演示如何利用此函数将现有街道几何体细分为更小的线段,进而构建一个精细化的网络图,以支持更细粒度的空间分析。…

    2026年5月10日
    000
  • 如何安全有效地从外部网页获取HTML元素数据并应用于自身页面

    本教程旨在解决如何在不同域名下,通过javascript获取并使用另一个网页的html元素数据。文章将深入探讨同源策略的限制,并提供两种主要解决方案:使用` 在现代Web开发中,有时我们需要从外部网站获取特定的HTML内容或属性值,并将其整合到我们自己的网页中。例如,从XYZ.COM/B.html页…

    2026年5月10日
    000
  • JS如何操作HTML元素_DOM编程核心方法【教程】

    必须掌握操作HTML元素的核心DOM方法:一、通过ID获取单个元素;二、通过类名获取元素集合;三、通过标签名获取元素集合;四、通过CSS选择器获取元素;五、为元素绑定事件监听器;六、创建并插入新元素;七、替换或删除现有元素。 如果您希望使用JavaScript动态修改网页内容、响应用户交互或构建交互…

    2026年5月10日
    000
  • Golang如何提升TCP长连接处理效率_Golang TCP长连接处理性能优化实践详解

    答案:通过非阻塞I/O、单Goroutine双工模型、sync.Pool对象复用、TCP_NODELAY优化及高效心跳管理,结合系统调优,可显著提升Golang百万级TCP长连接处理效率。 在高并发网络服务场景中,TCP长连接的处理效率直接影响系统的吞吐能力和资源消耗。Golang凭借其轻量级Gor…

    2026年5月10日
    000
  • 使用SMTP.js发送邮件:客户端集成、常见问题与最佳实践指南

    本文深入探讨了使用SMTP.js库在前端发送邮件时可能遇到的问题,特别是与Elastic Email集成时的挑战。我们将分析代码中常见的异步处理错误、条件函数定义陷阱,并提供修正后的代码示例和最佳实践。重点强调了正确处理Promise链、确保函数可访问性以及客户端邮件发送的安全考量,帮助开发者构建更…

    2026年5月10日
    000
  • 如何在不暴露密钥的情况下,在客户端创建 Stripe Payment Link

    本文介绍了在纯静态网站环境下,如何利用 Stripe Payment Link 实现商品售卖,并着重讨论了在不暴露 Stripe 密钥的前提下,客户端创建 Payment Link 的可行性。分析了直接在客户端使用密钥的风险,并提出了预先生成 Payment Link 或使用后端服务动态生成 Pay…

    2026年5月10日
    000
  • Windows用Prettier一键格式化乱码HTML代码

    首先确保HTML文件保存为UTF-8编码,使用文本编辑器另存为UTF-8格式;其次在命令行执行chcp 65001切换至UTF-8代码页后再运行Prettier;接着在VS Code中设置files.encoding为utf8并启用files.autoGuessEncoding;最后可通过Node.…

    2026年5月10日
    000
  • php怎么截取网页_php抓取网页内容的几种方法

    file_get_contents适用于静态页抓取,但受限于allow_url_fopen且无法执行JS;2. cURL支持自定义请求头、Cookie等,适合处理复杂HTTP请求;3. Guzzle作为现代PHP项目推荐方案,具备良好扩展性与异步支持;4. 动态渲染内容需借助Puppeteer或Se…

    2026年5月10日
    000
  • html函数如何实现动态内容显示 html函数在网页交互中的核心应用

    JavaScript函数通过操作DOM实现动态内容更新与交互,如显示时间、实时搜索、增删元素及加载数据,使网页具备动态功能。 HTML 本身没有“函数”的概念,它是一种标记语言,用于定义网页结构。真正实现动态内容显示和交互功能的是 JavaScript。通常所说的“HTML函数”其实是 JavaSc…

    2026年5月10日
    000
  • HTMLAMP怎么做_加速移动页面实现教程

    答案:HTML AMP通过规范标签、禁用自定义JS、引入AMP JS库和缓存技术提升移动页面加载速度,需遵循AMP HTML标准并验证有效性,有助于SEO但非万能,未来将更开放并与PWA等融合。 HTML AMP 旨在加速移动页面加载速度,提升用户体验。简单来说,它通过限制某些 HTML 功能,并采…

    2026年5月10日
    000
  • 理解PHP服务器端请求与浏览器开发者工具的限制

    当PHP脚本使用file_get_contents等函数发起服务器端请求时,这些请求直接在服务器上执行,而非通过浏览器。因此,浏览器开发者工具的网络活动面板无法捕获和显示这些内部的服务器间通信,因为它仅监控浏览器自身发出的网络请求,对服务器内部处理过程无感知。 客户端请求与服务器端请求的本质区别 在…

    2026年5月10日
    000
  • JavaScript DOM操作:点击关联元素获取目标文本内容的教程

    本教程详细介绍了如何通过JavaScript处理用户点击事件,并结合DOM的 closest() 和 querySelector() 方法,从复杂的HTML结构中准确获取目标元素的文本内容。文章强调了使用 addEventListener() 进行事件绑定、避免重复ID以及高效DOM遍历的最佳实践,…

    2026年5月10日
    000
  • 前端构建优化:利用常量折叠提升应用性能

    本文深入探讨了一种在构建阶段执行部分源代码以进行优化的技术——常量折叠(Constant Folding)。通过在编译时预计算表达式并替换为最终结果,该技术显著减少了运行时开销,提升了应用性能。文章将详细解释其工作原理、优势,并探讨其在现代前端构建工具中的应用与配置,旨在帮助开发者实现更高效的代码优…

    2026年5月10日
    000
  • JavaScript数据结构实现_javascript算法基础

    JavaScript中常用数据结构包括栈、链表和字典:1. 栈利用数组的push和pop实现LIFO,适用于括号匹配;2. 链表由节点组成,插入删除高效,适合频繁修改场景;3. 字典用对象实现键值对存储,常用于频率统计;4. 二分查找在有序数组中以O(log n)效率查找目标值,需数组已排序。掌握这…

    2026年5月10日
    000
  • JavaScript井字棋(Tic-Tac-Toe)核心交互逻辑实现教程

    本教程详细介绍了如何使用javascript实现井字棋(tic-tac-toe)游戏的核心交互逻辑。内容涵盖了如何遍历并为棋盘上的每个方格添加点击事件监听器,实现玩家x和o的交替落子,以及重置游戏状态的功能。通过提供的html、css和javascript代码示例,读者可以快速理解并构建一个基础的井…

    2026年5月10日
    000
  • 全栈JS代码怎么结构化_全栈JavaScript项目代码结构与规范指南

    采用分层+功能划分的目录结构,明确分离前后端代码;2. 遵循单一职责原则,路由、控制器、服务与模型各司其职;3. 统一命名规范并集成ESLint+Prettier保证代码风格一致;4. 使用环境变量管理配置,通过脚本实现自动化构建与并发启动服务。 全栈JavaScript项目涉及前端、后端、数据库交…

    2026年5月10日
    000
  • JavaScript模块加载机制_JavaScript代码组织规范

    现代前端推荐使用ES Modules,通过import和export实现静态依赖管理,配合合理目录结构与命名规范提升可维护性,注意浏览器与Node.js的运行差异。 JavaScript 的模块加载机制和代码组织规范是现代前端开发中的核心基础。随着项目规模扩大,良好的模块化设计能提升代码可维护性、复…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信