怎么利用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

相关推荐

  • VSCode的自动保存与文件监听功能如何结合以避免不必要的构建触发?

    通过配置VSCode自动保存延迟和构建工具防抖,减少频繁触发构建。设置”files.autoSave”: “afterDelay”与”files.autoSaveDelay”: 3000,结合Vite或Webpack的watch…

    2026年9月21日
    000
  • Linux怎么监控特定进程的运行状态

    Linux怎么监控特定进程的运行状态Linux怎么监控特定进程的运行状态Linux怎么监控特定进程的运行状态Linux怎么监控特定进程的运行状态

    监控Linux进程需综合使用ps、top、htop、pgrep和systemctl等工具,结合资源占用、进程状态、日志输出和进程数量判断是否异常,并通过systemd的Restart机制或看门狗脚本实现自动重启,同时利用journalctl、sar、atop及Prometheus+Grafana等方…

    2026年9月21日 用户投稿
    100
  • Linux如何创建符号链接和硬链接

    Linux如何创建符号链接和硬链接Linux如何创建符号链接和硬链接Linux如何创建符号链接和硬链接Linux如何创建符号链接和硬链接

    符号链接是快捷方式,指向文件或目录路径,原文件删除后链接失效;2. 硬链接共享同一inode,不能跨文件系统或链接目录;3. 使用ln -s创建符号链接,ln创建硬链接;4. 符号链接可跨分区,硬链接删除原文件后仍可访问数据。 在Linux中,创建符号链接(软链接)和硬链接是管理文件和目录的常用操作…

    2026年9月21日 用户投稿
    000
  • 从 API 响应中提取元素并在 Java 中使用

    本文介绍了如何在 Java 中解析 API 响应,并从中提取特定元素的值。以 JSON 格式的响应为例,演示了如何使用 Jackson 库将 JSON 字符串转换为 Java 对象,并提取所需的数据,例如账户 ID,以便在后续操作中使用。 在 Java 开发中,经常需要与 API 进行交互,并从 A…

    2026年9月21日
    100
  • 如何调整VSCode的设置以获得最佳性能?

    合理配置VSCode可显著提升性能。1. 禁用不必要扩展,减少后台资源占用;2. 在settings.json中设置files.watcherExclude和search.exclude以降低CPU负载;3. 启用editor.renderLineHighlight和largeFileOptimiz…

    2026年9月20日
    500
  • VSCode的扩展推荐是怎么工作的?

    VSCode的扩展推荐基于用户行为和项目环境智能生成,当你打开.py文件时会推荐Python相关工具,打开.ts、.vue等文件则触发对应语言插件;系统通过分析package.json、requirements.txt等依赖文件识别技术栈,推荐Docker、ESLint等匹配扩展;同时记录常用操作如…

    2026年9月20日
    000
  • Linux怎么查看进程使用的端口号

    答案是使用netstat、ss或lsof命令可查看Linux进程占用的端口。首先推荐ss命令,如ss -tulnp | grep 8080,能快速显示监听端口及对应进程;其次netstat -tulnp | grep 8080用法类似,但速度较慢;lsof -i :8080可精确查看指定端口的进程信…

    2026年9月20日
    000
  • 当VSCode启动或运行变慢时,有哪些系统性的排查和优化步骤?

    答案:VSCode变慢主要由扩展、文件监控和设置引起。先以安全模式启动排查扩展影响,使用内置性能工具分析启动耗时,优化工作区的文件监听与搜索范围,调整渲染设置并清理缓存,可显著提升运行效率。 VSCode 启动或运行变慢通常涉及扩展、设置、系统资源或文件索引等问题。以下是系统性的排查与优化步骤,帮助…

    2026年9月20日
    000
  • Linux如何解决rpm依赖关系错误

    Linux如何解决rpm依赖关系错误Linux如何解决rpm依赖关系错误Linux如何解决rpm依赖关系错误Linux如何解决rpm依赖关系错误

    使用YUM可自动解决RPM依赖,通过yum localinstall安装本地包或yum install自动处理依赖;2. 较新系统推荐使用DNF,命令为dnf install 包名.rpm,依赖解析更高效;3. 无法使用YUM/DNF时可手动处理,通过rpm -ivh查看缺失依赖,再下载并按序安装;…

    2026年9月20日 用户投稿
    000
  • VSCode的侧边栏图标代表什么?

    资源管理器(文件夹图标)用于管理项目文件结构,支持新建、重命名、删除和拖拽操作;2. 搜索(放大镜图标)实现全局文本查找与替换,支持正则表达式及范围筛选;3. 源代码管理(分支图标)集成Git功能,可查看变更、提交代码并同步远程仓库;4. 运行和调试(虫子图标)支持断点调试、变量监控及多语言启动配置…

    2026年9月20日
    000
  • 如何为VSCode添加自定义主题?

    可通过安装扩展、手动添加JSON文件或使用Yeoman生成器创建主题。首先安装现成主题扩展并从颜色主题列表中选择应用;其次将自定义主题JSON文件放入用户themes目录后在命令面板中启用;最后可用yo code生成项目开发发布主题,需编辑配色文件并预览效果。 为 VSCode 添加自定义主题有几种…

    2026年9月20日
    000
  • edge浏览器无法打开本地HTML文件或显示空白怎么办_Edge浏览器打开本地HTML文件失败解决方法

    1、检查文件路径并选择Edge打开,确保路径为纯英文;2、在edge://flags中启用“Allow file access from files”;3、使用开发者工具排查资源加载错误;4、通过命令行启动Edge绕过安全限制;5、推荐使用npx live-server搭建本地服务器运行HTML文件…

    2026年9月20日
    000
  • mysql如何配置临时文件权限

    通过设置tmpdir指定专用目录并配置系统权限为700,结合文件系统安全挂载选项与MySQL用户权限控制,可有效保障MySQL临时文件安全性。 MySQL 本身不直接提供配置“临时文件权限”的参数,但可以通过操作系统层面和 MySQL 相关配置共同控制临时文件的创建位置与访问权限,确保安全性。关键在…

    2026年9月20日
    000
  • Linux如何给用户分配磁盘配额

    答案:Linux通过quota工具为用户设置磁盘空间和文件数量限制。首先安装quota工具,编辑/etc/fstab添加usrquota和grpquota选项,重新挂载文件系统;运行quotacheck创建配额数据库,使用quotaon启用配额;通过edquota -u设置用户配额,包括块和inod…

    2026年9月20日
    000
  • VSCode的扩展推荐是基于什么?

    答案是VSCode根据文件类型、项目配置、用户行为和社区流行度智能推荐扩展。打开.py文件时提示Python扩展,存在package.json则推荐JS工具,频繁格式化代码会推荐Prettier,安装ESLint后可能推荐Prettier,高评分和官方扩展优先展示。 VSCode的扩展推荐主要基于你…

    2026年9月20日
    000
  • mysql如何实现用户注册功能

    答案:通过MySQL创建用户表并结合后端逻辑实现注册功能。首先在MySQL中创建包含用户名、密码、邮箱等字段的users表,确保唯一性约束;后端接收%ignore_a_1%提交的注册数据,对密码加密(如SHA256或bcrypt),使用参数化查询将数据安全存入数据库;前端通过表单或AJAX发送注册请…

    2026年9月13日
    200
  • 如何利用VSCode的配置文件(Launch.json)环境变量来管理不同环境的配置?

    通过配置launch.json中的多个调试环境并结合.env文件,可高效管理开发、测试、生产等不同环境的运行参数。 在使用 VSCode 进行开发时,launch.json 文件是调试配置的核心。通过合理利用环境变量和配置文件分离,可以轻松管理不同环境(如开发、测试、生产)的运行参数。 理解 lau…

    2026年9月13日
    000
  • 如何用ChatGPT来重构优化一段旧代码_提升代码质量与性能的步骤

    明确目标并提供上下文,分步引导ChatGPT优化代码结构、算法和语言特性,最后验证建议并本地测试,确保重构有效且安全。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ 用ChatGPT来重构和优化旧代码是一种高效提升代码质量与性能的方式。关键…

    2026年9月12日
    100
  • 如何让VSCode自动识别项目类型?

    VSCode通过项目中的文件和配置自动识别类型,需提供线索如package.json、requirements.txt等标志性文件,结合安装对应语言扩展、配置.vscode/settings.json及使用.code-workspace文件,即可让编辑器正确识别并支持Node.js、Python、R…

    2026年9月12日
    300
  • 当IntelliSense反应迟钝时,如何调整相关设置或重建工作区索引?

    重建索引并优化设置可解决IntelliSense反应迟钝:启用自动完成、参数提示和语法错误高亮;2. 通过重置IntelliSense数据库、删除.ipch文件夹重建索引;3. 调整C_Cpp.intelliSenseEngine为Default或Tag Parser,设置缓存大小及排除监控目录以提…

    2026年9月12日
    200

发表回复

登录后才能评论
关注微信