pdfMake,xlsx-js-style,elementTable表格导出大量数据的pdf和xslx表格

news2024/11/16 13:54:51

 使用渲染dom传递给xlsx或将dom转canvas在传给jspdf数据量大都会造成页面负载过大

所以导pdf和xlsx都使用数据传递给pdfMake,xlsx-js-style,pdf涉及分页与合并单元格

 一.pdf

npm并引入pdfMake和其字体包(记录时使用版本0.2.10

import pdfMake from "pdfmake/build/pdfmake";
import pdfFonts from "pdfmake/build/vfs_fonts";

 官网地址pdfmake

pdfMake支持表格和列表等传递数据类型导出pdf,支持水印与合并行列单元格,支持字体颜色和简单样式设置

1.引入中文字体包

pdfMake自带的roboto字体不支持中文,所以需要自己下载一个支持中文的ttf文件,这里使用

SourceHanSansCN-Normal.ttf,网上下载该文件资源,在node_modules的pdfMake包中新建examples

文件夹,在examples中新建一个fonts文件夹,将需要的字体包复制进去。

在pdfMake的目录下打开控制台或者cmd等命令行运行命令,pdfmake会构建字体的编码到pdfmake下的/build/vfs_fonts.js中,这个文件应该包含四种roboto字体

node build-vfs.js "./examples/fonts"

2.引入字体出现错误

pdfmake Font 'Roboto' in style 'bold' is not defined in the font section of或

File 'Roboto-Regular.ttf' not found in virtual file system,等问题

pdfmake Font ‘Roboto‘ in style ‘bold‘ is not defined in the font section of或File ‘Roboto-Regular.t-CSDN博客

3.使用pdfMake的数据格式

contect =	{
			style: 'tableExample',
			color: '#444',
			table: {
//每行宽度
				widths: [200, 'auto', 'auto'],
//表头的行数,设置为表头分页时会携带
				headerRows: 2,
				// keepWithHeaderRows: 1,
//table的数据,合并Colspan ,rowspan
				body: [
					[{text: 'Header with Colspan = 2', style: 'tableHeader', colSpan: 2, alignment: 'center'}, {}, {text: 'Header 3', style: 'tableHeader', alignment: 'center'}],
					[{text: 'Header 1', style: 'tableHeader', alignment: 'center'}, {text: 'Header 2', style: 'tableHeader', alignment: 'center'}, {text: 'Header 3', style: 'tableHeader', alignment: 'center'}],
					['Sample value 1', 'Sample value 2', 'Sample value 3'],
					[{rowSpan: 3, text: 'rowSpan set to 3\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor'}, 'Sample value 2', 'Sample value 3'],
					['', 'Sample value 2', 'Sample value 3'],
					['Sample value 1', 'Sample value 2', 'Sample value 3'],
					['Sample value 1', {colSpan: 2, rowSpan: 2, text: 'Both:\nrowSpan and colSpan\ncan be defined at the same time'}, ''],
					['Sample value 1', '', ''],
				]
			}
		},
 let pdfMakeData = {
    // watermark: { text: '乐恒', font: 'SourceHanSansCN' } || '',
    pageSize: pageSize,
    // pageOrientation: 'portrait',
    pageMargins: [
      cardLeft,
      cardTop,
      cardLeft,
      cardTop,
    ],
    content: content,
//简单的样式
    styles: {
      tableExample: {
        margin: [0, 5, 0, 15],
        font: 'SourceHanSansCN',
        color: 'black',
      },
    },
    defaultStyle: {
      defaultStyle: {
        font: 'SourceHanSansCN'
      },
    }
  }
//业务逻辑结束,根据需要构建pdfMake需要的content
//添加vfs字体
  pdfMake.vfs = pdfFonts;
//添加字体对象,Roboto是必须添加的否则报错,剩余添加需要的字体这里是SourceHanSansCN
  pdfMake.fonts = {
    Roboto: {
      normal: 'Roboto-Regular.ttf',
      bold: 'Roboto-Medium.ttf',
      italics: 'Roboto-Italic.ttf',
      bolditalics: 'Roboto-MediumItalic.ttf'
    },
    SourceHanSansCN: {
      normal: 'SourceHanSansCN-Normal.ttf',
      bold: 'SourceHanSansCN-Normal.ttf',
      italics: 'SourceHanSansCN-Normal.ttf',
      bolditalics: 'SourceHanSansCN-Normal.ttf'
    }
  };
//下载
  pdfMake.createPdf(pdfMakeData).download(getFileName());

 4.使用pdfMake下载

function downPDF({ dataList, labelList, spanObj }) {
//设置一些活参数有用的是oneRowNum每页需要多少列,oneCellWidth每个单元格宽度,纸张型号宽高
  let cardTop = 10, cardLeft = 7, textLeft = 9, textTop = 14, oneColumnNum = 35, oneRowNum = 7, oneCellWidth = 73.7, oneCellHeight = 8, oneStrLen = 5, pdfWidth = 203, pdfHeight = 290, strLen = 8, pageSize = 'A4'
//我的业务逻辑实现开始(分页,合并单元格
  let startIndex = 0, endIndex = oneRowNum
  let content = []
  let columnPages = Math.ceil(dataList.length / oneRowNum) + 1
  for (let i = 1; i < columnPages; i++) {
    let currentColumnData = dataList.slice((i - 1) * oneRowNum, i * oneRowNum)
    let currentLabelData = labelList.slice((i - 1) * oneRowNum, i * oneRowNum)
    let ownObj = {
      table: {
        headerRows:spanObj.rowIndex[1],
        widths: new Array(currentLabelData.length).fill(oneCellWidth),
        body: [
          // [{text: 'Header 1'}, {text: 'Header 2', style: 'tableHeader'}, {text: 'Header 3', style: 'tableHeader'}],
          // ['Sample value 1', 'Sample value 2', 'Sample value 3'],

        ],
      },
      style: 'tableExample'
    }
    let headerArr = []
    currentLabelData.forEach((item) => {
      headerArr.push({ text: item })
    })
    let columnArr = new Array()
    currentColumnData.forEach((item, index) => {

      item.forEach((cItem, cIndex) => {
        columnArr[cIndex] = columnArr[cIndex] || []
        columnArr[cIndex].push(cItem)
      })
    })


    if (i === 1) {
      spanObj.columnIndex
        .forEach((item, index) => {
          for (let cindex = 0; cindex < columnArr.length; cindex++) {
            const cItem = columnArr[cindex];

            let cChild = cItem[item]
            if (cChild) {
              let repeatIndex = columnArr.filter((fItem) => {
                return fItem[item] === cChild
              }).length
              cItem[item] = { text: cChild, rowSpan: repeatIndex }
              index = repeatIndex + index - 1
            }

          }
        })
    }
    columnArr.unshift(headerArr)
    if (spanObj.rowIndex[0].length !== 0) {
      let rowHead = []
      let endLen = i + 1 === columnPages ? (dataList.length - (i - 1) * oneRowNum) : oneRowNum
      for (let keyIndex = 0; keyIndex < endLen; keyIndex++) {
        let rowCurrent = (i - 1) * oneRowNum + keyIndex
        let rowEnd = 0

        for (const key in spanObj.rowIndex[0]) {
          let children = spanObj.rowIndex[0][key]
          rowEnd = rowEnd + children.length
          if (rowEnd > rowCurrent) {
            let obj = { text: key }
            rowHead.push(obj)

            break;
          }
        }
      }

      for (let index = 0; index < rowHead.length; index++) {
        const cItem = rowHead[index];
        let repeatIndex = rowHead.filter((fItem) => {
          return fItem.text === cItem.text
        }).length
        rowHead[index].colSpan = repeatIndex
        index = repeatIndex + index - 1

      }

      columnArr.unshift(rowHead)
    }

    ownObj.table.body = columnArr
    content.push(ownObj)
  }
  console.log('content', JSON.parse(JSON.stringify(content)))
//业务逻辑结束,根据需要构建pdfMake需要的content
//添加vfs字体
  pdfMake.vfs = pdfFonts;
//添加字体对象,Roboto是必须添加的否则报错,剩余添加需要的字体这里是SourceHanSansCN
  pdfMake.fonts = {
    Roboto: {
      normal: 'Roboto-Regular.ttf',
      bold: 'Roboto-Medium.ttf',
      italics: 'Roboto-Italic.ttf',
      bolditalics: 'Roboto-MediumItalic.ttf'
    },
    SourceHanSansCN: {
      normal: 'SourceHanSansCN-Normal.ttf',
      bold: 'SourceHanSansCN-Normal.ttf',
      italics: 'SourceHanSansCN-Normal.ttf',
      bolditalics: 'SourceHanSansCN-Normal.ttf'
    }
  };
//创建打印数据对象
  let pdfMakeData = {
    // watermark: { text: '乐恒', font: 'SourceHanSansCN' } || '',
    pageSize: pageSize,
    // pageOrientation: 'portrait',
    pageMargins: [
      cardLeft,
      cardTop,
      cardLeft,
      cardTop,
    ],
    content: content,
//简单的样式
    styles: {
      tableExample: {
        margin: [0, 5, 0, 15],
        font: 'SourceHanSansCN',
        color: 'black',
      },
    },
    defaultStyle: {
      defaultStyle: {
        font: 'SourceHanSansCN'
      },
    }
  }

//下载
  pdfMake.createPdf(pdfMakeData).download(getFileName());
}

GitHub - bpampuch/pdfmake: Client/server side PDF printing in pure JavaScript

二.xlsx

1.npm下载并引入xlsx-js-style,它比xlsx支持自定义表格style,即用数据和属性可以构建xlsx的下载不传dom只操作数据

import XLSX from "xlsx-js-style";

2.下载函数,整理获取到的table数据,根据需求填充,需求只涉及二级表头的合并行与前几列的合并,并非整个表格合并

//表格数据数据,表头数据数组,合并行列数组
function downXlsx({ dataList, labelList, spanObj }) {
console.log(dataList, labelList, spanObj)
//添加合并列的单元格
  let mergesArr = []
  spanObj.columnIndex
    .forEach((item, index) => {
      for (let cindex = 0; cindex < dataList.length; cindex++) {
        const cItem = dataList[cindex];
        let cChild = cItem[item]
        if (cChild) {
          let repeatIndex = dataList.filter((fItem) => {
            return fItem[item] === cChild
          }).length
          mergesArr.push({ s: { r: cindex + 1, c: item }, e: { r: cindex + repeatIndex, c: item } })
          cindex = repeatIndex + cindex - 1
        }
      }
    })
//添加表头为第一列
  dataList.unshift(labelList)
//添加合并列的单元格(二级表头
  if (spanObj.rowIndex[0].length !== 0) {
    let rowHead = []
    for (let keyIndex = 0; keyIndex < labelList.length; keyIndex++) {
      let rowEnd = 0
      for (const key in spanObj.rowIndex[0]) {
        let children = spanObj.rowIndex[0][key]
        rowEnd = rowEnd + children.length
        if (rowEnd > keyIndex) { 
          rowHead.push(key)
          break;
        }
      }
    }
    for (let index = 0; index < rowHead.length; index++) {
      const cItem = rowHead[index];
      let repeatIndex = rowHead.filter((fItem) => {
        return fItem === cItem
      }).length
      mergesArr.push({ s: { r: 0, c: index }, e: { r: 0, c: index + repeatIndex-1 } })
      index = repeatIndex + index - 1
    }
    dataList.unshift(rowHead)
  }
//创建xlsx,下载
  const wb = XLSX.utils.book_new();
  const ws = XLSX.utils.json_to_sheet(dataList, {
    skipHeader: true,
  });

  ws["!merges"] = mergesArr;
  XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
  XLSX.writeFile(wb, getFileName());
}

spanObj的数据格式

//datalist每个元素的数据是table的行数据
datalist:[[],[],[]]
labelList:["名称","事件","ey位m","温度℃""]
spanObj :{
//多级表头,一级表头属性下的二级表头内容
    "rowIndex": [
        {
            "基础信息": [
                {
                    "label": "名称",
                    "prop": "MingCheng",
                    "width": "150",
                    "click": "rezhan"
                },
                {
                    "label": "事件",
                    "prop": "事件",
                    "width": "150",
                    "click": "rezhan"
                },
            ],

            "xx数据": [
                {
                    "label": "ey位m",
                    "prop": "shuiXiangYeWei",
                    "width": "100",
                    "fixed": 1
                },
            
            ],
            "其他数据": [
                {
                    "label": "温度℃",
                    "prop": "shiWaiWenDu",
                    "width": "110",
                    "fixed": 1
                },
          
            ]
        },
        2
    ],
//第0列需要合并,合并相同内容
    "columnIndex": [0]
}

 html简单demo,fonts里有字体运行index即可

pdfxlsx: 不操作dom导出pdf xlsxxlsx-js-style pdfmake

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1678158.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

仅1年!荣登中科院1区经济类SSCI宝座!影响因子3连涨,创刊时间不长但口碑飙升!

【SciencePub学术】今天小编给大家带来了一本经济学领域的高分优刊解读&#xff0c;创刊时间不长&#xff0c;但影响因子3连涨现已高达8.5&#xff0c;JCR1区&#xff0c;中科院1区&#xff0c;领域相符的学者可考虑&#xff01; Oeconomia Copernicana 1 期刊概况 【期刊简介…

大龄程序员是否要入职嵌入式python岗位?

在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「Python的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; 是否要做嵌入式 Python 取决于…

vue(九) 生命周期 v3.0和v2.0对比,父子组件生命周期的执行顺序

文章目录 生命周期vue2.0生命周期1.图示2.生命周期解释说明3.代码示例 vue3.0生命周期1.图示2.生命周期解释说明3.代码示例 父子组件中生命周期执行顺序v.3和v2.0生命周期对比 生命周期 每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤&#xff0c;比如设置好数据侦听…

批量下载huggingface的仓库全部权重文件

下载huggingface的仓库全部权重文件 配置和下载git-lfs **ubuntu:**sudo apt-get install git-lfs 其他&#xff1a; 下载git-lfs Releases git-lfs/git-lfs (github.com) 配置&#xff1a; export PATH$PATH://home/software/lfs/git-lfs-3.5.1/ # 其中目录为你文件夹的目…

tomcat启动闪退解决办法

tomcat启动闪退解决办法 1. 端口号被占用2. 检查电脑环境配置2.1 如何配置电脑的环境变量&#xff1f; windows系统下&#xff0c;tomcat安装好以后&#xff0c;双击bin文件下startup.bat后&#xff0c;tomcat没能按照预期打开&#xff0c;反而闪退了。 导致这种情况发生&…

python高级爱心代码

python高级爱心代码实现&#xff1a; import turtle import random # 设置画布 screen turtle.Screen() screen.bgcolor("black") # 创建画笔 pen turtle.Turtle() pen.speed(0) pen.color("red") pen.penup() # 移动画笔到起始位置 pen.goto(0, -20…

刘邦痛恨的叛徒雍齿,为何后来还被封了侯?

雍齿&#xff0c;原是沛县的世族出身&#xff0c;家庭往上追溯几代&#xff0c;也曾经显赫过。 虽然比不上先祖世代为楚将的项梁、项羽&#xff0c;但雍齿这个没落的世族后代&#xff0c;身上多多少少也还讲究点贵族遗风。 战国时期&#xff0c;以秦国的军功爵制为代表&#…

YOLOV8从环境部署(GPU版本)

一、安装&#xff43;&#xff55;&#xff44;&#xff41;和&#xff43;&#xff55;&#xff44;&#xff4e;&#xff4e; 1、安装cuda之前先打开英伟达控制面板查看自己的显卡信息 2、“帮助”—>“系统信息”—>“组件”&#xff0c;然后看第三行的信息“Nvidia …

Franz Electron + React 源码启动运行填坑指南

环境要求 安装miniconda python 环境electron/rebuild用得着&#xff0c;miniconda 默认自带的 python 是 3.11 版本&#xff0c;比较新&#xff1b; 安装virsual studio 2019 要把C桌面相关的都安装了&#xff0c;大概需要20G&#xff0c;不要安装到 C 盘&#xff0c;都安装到…

什么是Serverless ?

目录&#xff1a; 1、服务器发展阶段 2、Serverless定义 3、Serverless理解

AI图像生成-基本步骤

模型板块 1、新建采样器&#xff1a;新建节点-》采样器-》K采样器 2、拖动模型节点后放开&#xff0c;选择checkpoint加载器&#xff08;简易&#xff09;&#xff0c;模型新建成功 提示词板块 1、拖动正面条件节点后放开&#xff0c;选择CLIP文本编码器&#xff0c;模型新建…

《Fundamentals of Power Electronics》——转换器的传递函数

转换器的工程设计过程主要由以下几个主要步骤组成&#xff1a; 1. 定义了规范和其他设计目标。 2. 提出了一种电路。这是一个创造性的过程&#xff0c;利用了工程师的物理洞察力和经验。 3. 对电路进行了建模。组件和系统的其他部分适当建模&#xff0c;通常使用供应商提供的…

校园科普气象站的工作原理

TH-XQ3校园科普气象站是学校为了进行气象科普教育而设立的一种特殊设施。它不仅是一个能够实时监测和记录各种气象参数的气象站&#xff0c;更是一个促进学生对气象科学兴趣和理解的重要平台。 校园科普气象站通常包括一系列的气象观测设备和相关的科普设施。这些设备包括但不限…

【Git教程】(十八)拆分大项目 — 概述及使用要求,执行过程及其实现,替代解决方案 ~

Git教程 拆分大项目 1️⃣ 概述2️⃣ 使用要求3️⃣ 执行过程及其实现3.1 拆分模块版本库3.2 将拆分出的模块作为外部版本库集成 4️⃣ 替代解决方案 通常软件项目都是由单体小型系统开始的&#xff0c;在开发过程中项目规模和团队人员不断扩大&#xff0c; 将项目模块化会显得…

【保姆级】生成式网络模型基础知识(图像合成/语音合成/GPT)

生成式模型基础知识 初步接触生成任务 生成任务&#xff0c;顾名思义就是要去生成一个东西&#xff0c;比如生成图片/音频/文字等等。 大家接触最多比如chatGPT、stable diffusion、还有一些语音合成相关的东西。 那么问题来了&#xff0c;具体生成步骤是什么样的&#xff…

【Kibana】快速上手Kibana平台(KQL)

文章目录 快速使用Kibana平台常用查询语句KQL基本查询覆合查询模糊查询 目前市面上大部分的公司的日志系统都是使用ELK系统&#xff0c;因此我们进行工作必须得掌握Kibana平台的基本使用&#xff0c;这里主要说明怎么“快速使用Kibana平台”以及记录一些常用的“KQL语言”。 快…

数字化应用标杆 | 又两家成套厂效率翻倍,利用率高达93%以上!

利驰 联能 & 利驰 俊郎 近日&#xff0c;利驰数字科技&#xff08;苏州&#xff09;有限公司&#xff08;简称利驰软件&#xff09;成功与俊郎电气有限公司&#xff08;简称俊郎电气&#xff09;、浙江联能电气有限公司&#xff08;简称联能电气&#xff09;成功确立了数字…

Elasticsearch_sql插件安装+使用

一、安装 前提是你先安装好了elasticseach&#xff0c;安装过程在我上一篇博客有说&#xff0c;可以看一下。 在elasticsearch容器启动的情况下&#xff0c;进入到elasticsearch容器&#xff0c;Elasticsearch_sql仓库&#xff0c;比如我的版本是8.11.2&#xff0c;那么我就选…

618值得入手的数码有哪些?数码好物清单推荐|款款实用闭眼冲

每年的618购物节都是消费者们翘首以盼的盛宴&#xff0c;这一天&#xff0c;各大品牌和电商平台都会推出极具吸引力的优惠活动&#xff0c;让消费者们能够以更优惠的价格购买到心仪的数码好物&#xff0c;为了帮助大家在这个购物狂欢节中挑选到真正实用、性价比高的数码产品&am…

javaSE:类和对象

面向对象 java是一种面向对象的编程语言&#xff0c;面向对象就是把能为我们所用的东西直接拿来使用&#xff0c;省去中间过程&#xff0c;比如洗衣服&#xff0c;要完成这一个动作&#xff0c;我们本来需要一个盆&#xff0c;放水&#xff0c;放衣服&#xff0c;换水&#xf…