用node实现生成word的方法及代码是什么
Admin 2022-08-23 群英技术资讯 1313 次浏览
本篇内容介绍了“用node实现生成word的方法及代码是什么”的有关知识,在实际项目的操作过程或是学习过程中,不少人都会遇到这样的问题,接下来就让小编带大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
最近有项目需要用到生成word文档,平时经常用的都是通过模板生成,里面变量使用占位符替换,好处是快捷、方便、简单、不需要通过代码调word样式,确定是很多库不支持图片绘制(很多都是付费功能),找一圈,发现一个很有意思的库,正好也满足我们的需求,特此分享一下
// https://docx.js.org/#/ npm i docx // https://www.npmjs.com/package/download npm i download
说明,因为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)
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
最近小编在做一个项目,其中涉及到一个模块关于星座查询功能,即在文本框中输入一个生日值,点击按钮可以得到对应的星座,怎么实现这个需求呢?下面小编通过示例代码给大家介绍下,需要的朋友参考下吧
JavaScript中wrap的意思是“包裹”,wrap()方法用于指定的 HTML元素来包裹每个被选元素,语法为“元素对象.wrap(wrappingElement,function(index))”。
这篇文章主要介绍的是前端JavaScript中的反射和代理,本文主要围绕JavaScript中的内置Reflect、JavaScript中的内置Proxy、Proxy实现观察者模式三个话题展开文章内容,需要的朋友可以参一考下
本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于运算符的相关问题,运算符也被称为操作符,是用于实现赋值、比较和执行算术运算等功能的符号,下面一起来看一下,希望对大家有帮助。
这篇文章主要介绍JavaScript继承方式,在JavaScript学习中,继承是很重要的一个知识内容。下文会给大家介绍原型式继承、原型链式继承、借用构造函数、组合继承、寄生组合式继承这几种继承方式,感需求的朋友就继续往下看吧。
成为群英会员,开启智能安全云计算之旅
立即注册关注或联系群英网络
7x24小时售前:400-678-4567
7x24小时售后:0668-2555666
24小时QQ客服
群英微信公众号
CNNIC域名投诉举报处理平台
服务电话:010-58813000
服务邮箱:service@cnnic.cn
投诉与建议:0668-2555555
Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 ICP核准(ICP备案)粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008