纯前端导出Excel、PDF,单元格合并,设置宽高,字体大小,颜色等

1. 导出excel

1.1 Vue2中使用

基础使用

  1. 首先安装js-xlsx库,使用这个库可以完成一些基本的excel导出,如需导出带样式的excel,则需要使用另外一个库,后面会讲到。
  2. 执行如下命令
js 复制代码
npm install --save xlsx
  1. 在untils文件夹下,新建ecxel.js
js 复制代码
const XLSX = require("xlsx");  //使用import有的属性无法找到
export function exportExcel(filename, data) {
    let exc = XLSX.utils.book_new(); // 初始化一个excel文件
    let exc_data = XLSX.utils.aoa_to_sheet(data);   // 传入数据 , 到excel
    // 将文档插入文件并定义名称
    XLSX.utils.book_append_sheet(exc, exc_data, filename);
    // 执行下载
    XLSX.writeFile(exc, filename + '.xlsx');
}
  1. 使用如下
  2. 表格数据结构
  • 数据结构为一个二维数组,从上到下就是表格每一行的内容,数据层可以用循环把数据给push进去
  • excel_data数据可以是前端用已有的数据处理得到,如果前端数据处理太麻烦(下面会讲一些处理方法),直接扔给后端。

1.2 Vue3 中使用

1.在utils文件夹下新建ecxel.js文件

js 复制代码
import * as XLSX from 'xlsx' 
export function exportExcel(filename,data) {
    let exc = XLSX.utils.book_new();
    let exc_data = XLSX.utils.aoa_to_sheet(data); 
    XLSX.utils.book_append_sheet(exc, exc_data, filename);
    XLSX.writeFile(exc, filename + 'xlsx');
}
  1. 使用
js 复制代码
<template>
   <button @click="download">下载表格</button>
</template>
<script  setup>
import { exportExcel } from "./excelConfig"
const exc_data = [['第一列', '第二列' ,'第三列'],
		['aa', 'bb' ,'cc'],
		['dd', 'ee' ,'ff']];
function download() {  exportExcel('vue3导出的表格',this.exc_data)  }
</script>

1.3 基础excel表格的导出

在until文件目录下新建excel.js,封装导出函数,更加灵活多变,不必自己去处理数组里面的每一项数据。

js 复制代码
let XLSX = require('xlsx');

/**
 * 导出Excel的处理函数
 * @param {Array}   headers    需要导出的列 	示例:[{key: 'date', title: '日期'}, {key: 'name', title: '名称'}]
 * @param {Array}   data       导出的数据 		示例:[{date: '2023-05-31', name: 'megen.huang'}, {date: 'name', name: '姓名'}]
 * @param {String} fileName    文件的名称		示例:'导出结果.xlsx'
 * */
function ExportExcelTwo (headers, data, fileName = '导出结果.xlsx') {
	// 获取标题属性名称
	const excel_headers_key = headers.map(rowItem => rowItem.key)

	// 获取标题名称
	const excel_headers_title = headers.map(rowItem => rowItem.title)
	console.log(excel_headers_title);
	// 数据转换
	var excel_data = data.map(rowItem => {
		var arr = [];
		excel_headers_key.forEach(colItem => {
			arr.push(rowItem[colItem])
		})
		return (arr);
	})

	// 将标题添加到第一行
	excel_data.unshift(excel_headers_title)

	const worksheet = XLSX.utils.aoa_to_sheet(excel_data)
	const workbook = XLSX.utils.book_new()

	// 将数据添加到工作薄
	XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1')
	// 导出 Excel
	XLSX.writeFile(workbook, fileName)
}

export default ExportExcelTwo
举个例子:
  1. 现在需要导出下面这table表的excel表格

2. 这个表格的数据格式如图所示:

3. 对数据进行分析,根据excel导出数据要为一个数组,数组里面的每一项都为数组的格式。现在后端的数据,显然不符合要求,所以需要前端对数据进行处理。

4. 导出结果

1.4 导出带样式的表格,如单元格合并,设置宽度,字体大小等

前提是安装xlsx,已经可以导出普通的excel

js 复制代码
npm install --save xlsx file-saver

然后需要安装

js 复制代码
npm install xlsx-js-style
举个例子:合并单元格

先看导出结果:

js 复制代码
import XLSX from "xlsx-js-style";
js 复制代码
 exportExcel () {
      // 需要导出的数据
      let excelData = [
        ['幼儿园课表', null, null, null], // 标题
        ['序号', '课程名称', '教师名称', '上课地点'], // 表头
        ['1', '体育', '大头老师', '操场']
      ]

      let aoa = excelData;
      var sheet = XLSX.utils.json_to_sheet(aoa);

      // 设置标题行单元格合并
      // s即start, e即end, r即row, c即column

      let mergerarr = [
        // 第一行0的3列进行合并
        { s: { r: 0, c: 0 }, e: { r: 0, c: 3 } },
      ];

      sheet['!merges'] = mergerarr;
      const filename = `交接班审阅日志.xlsx`
      // Excel第一个sheet的名称
      const ws_name = 'Sheet1'
      const wb = XLSX.utils.book_new()
      const ws = XLSX.utils.json_to_sheet(aoa, {
        skipHeader: true,
      })
      // 设置宽度
      ws["!cols"] = [
        { wpx: 100, },
        { wpx: 120, },
        { wpx: 120, },
        { wpx: 100, },
      ];
      ws['!merges'] = sheet['!merges']
      XLSX.utils.book_append_sheet(wb, ws, ws_name) // 将数据添加到工作薄
      XLSX.writeFile(wb, filename) // 导出Excel
    },

如果需要导出下面动态数据的表格,那么可以自己设置一个标记,去记录每一行的需要设置的单元格样式。

下面代码思路可以供参考:

PS:数据叫后端处理,尽量不在前端处理一堆数据,不处理,直接叼他

js 复制代码
 handleExcel () {
      // 时间
      let list = [];//处理数据存放的数组
      let mergerarr = [
        { s: { r: 0, c: 0 }, e: { r: 0, c: 9 } },
        { s: { r: 1, c: 0 }, e: { r: 1, c: 2 } },
        { s: { r: 1, c: 3 }, e: { r: 1, c: 6 } },
        { s: { r: 1, c: 7 }, e: { r: 1, c: 9 } },
        { s: { r: 2, c: 0 }, e: { r: 2, c: 9 } },
      ];
      let tagindex = 2;
      let arr = [];
      arr.push(`交班日期:${this.viewData.WorkGiveDateTimeString}`, null, null);
      arr.push(`时间段:${this.viewData.WorkGiveTimePar.split('-')[0]}到次日${this.viewData.WorkGiveTimePar.split('-')[1]}`, null, null, null);
      arr.push(`交班类型:${this.viewData.WorkTypeTag === 1 ? '白班' : '夜班'}`, null, null);
      list.push(arr);
      let arr2 = [];
      arr2.push('一、接班情况');
      for (let i = 1; i < 10; i++) {
        arr2.push(null);
      }
      list.push(arr2);
      // 接班情况
      // 第四五行
      this.contentData.forEach((el) => {
        tagindex++;
        mergerarr.push({ s: { r: tagindex, c: 0 }, e: { r: tagindex, c: 9 } });
        let tagarr = [];
        tagarr.push(el);
        for (let i = 1; i < 10; i++) {
          tagarr.push(null)
        }
        list.push(tagarr);
      });
      // 本班工作情况
      let arr5 = [];
      arr5.push('二、本班工作情况');
      for (let i = 1; i < 10; i++) {
        arr5.push(null)
      };
      // 第六行
      tagindex++;
      mergerarr.push(
        { s: { r: tagindex, c: 0 }, e: { r: tagindex, c: 9 } }
      )
      list.push(arr5);
      const headers = [
        { key: 'indexTag', title: '序号' },
        { key: 'OperationTickSerial', title: '流水号' },
        { key: 'OTStartDateTimeStr', title: '开始时间' },
        { key: 'aresName', title: '装置/区域' },
        { key: 'EquipTagNumber', title: '位号' },
        { key: 'WorkContent', title: '作业内容' },
        { key: 'MalfTypeName', title: '故障类型' },
        { key: 'WorkEndFeedback', title: '处理结果/反馈' },
        { key: 'WorkHandlePerson', title: '处理人' },
        { key: 'IsOperationTickText', title: '是否完成' },
      ]
      // 获取标题属性名称
      const excel_headers_key = headers.map(rowItem => rowItem.key)

      // 获取标题名称
      const excel_headers_title = headers.map(rowItem => rowItem.title)
      // 数据转换
      let data = this.viewData.OperationTickets;
      var excel_data = data.map(rowItem => {
        var arr = [];
        excel_headers_key.forEach(colItem => {
          arr.push(rowItem[colItem])
        })
        return (arr);
      })
      // 将标题添加到第一行
      excel_data.unshift(excel_headers_title)

      // 第七八行
      excel_data.forEach((el, index) => {
        tagindex++;
        list.push(el);
      })
      // 本班工作情况
      let arr6 = [];
      arr6.push('二、交班情况');
      for (let i = 1; i < 10; i++) {
        arr6.push(null)
      };
      tagindex++;
      mergerarr.push({ s: { r: tagindex, c: 0 }, e: { r: tagindex, c: 9 } });
      list.push(arr6);
      // 交班情况
      this.contentData2.forEach((el, index) => {
        tagindex++;
        // 第九,十行
        mergerarr.push({ s: { r: tagindex, c: 0 }, e: { r: tagindex, c: 9 } });

        let tagarr = [];
        tagarr.push(el);
        for (let i = 1; i < 10; i++) {
          tagarr.push(null)
        };
        list.push(tagarr)
      })
      let arr10 = [];
      arr10.push(`交班人员:${this.viewData.WorkGivePerson}`, null, null,);
      arr10.push(`接班人员:${this.viewData.WorkCatchPerson}`, null, null, null);
      arr10.push(`审阅人:${this.viewData.ApprovalPerson === null ? '' : this.viewData.ApprovalPerson}`, null, null);
      list.push(arr10);
      tagindex++;
      // 第十一行
      mergerarr.push(
        { s: { r: tagindex, c: 0 }, e: { r: tagindex, c: 2 } },
        { s: { r: tagindex, c: 3 }, e: { r: tagindex, c: 6 } },
        { s: { r: tagindex, c: 7 }, e: { r: tagindex, c: 9 } },
      );


      let classesName = `${this.viewData.ClassesName}交接班审阅日志`;
      let arr11 = [];
      arr11.push(classesName);
      for (let i = 1; i < 10; i++) {
        arr11.push(null)
      };
      list.unshift(arr11);

      this.listexcel = list;
      this.mergerarr = mergerarr;
    },
    exportTable () {
    //数据处理函数
      this.handleExcel()
      //单元格处理
      let aoa = this.listexcel;
      var sheet = XLSX.utils.json_to_sheet(aoa);

      // 设置标题行单元格合并
      // s即start, e即end, r即row, c即column
      // 合并从--0行0列开始,到0行3列

      sheet['!merges'] = this.mergerarr;
      const filename = `${this.viewData.ClassesName} ${this.viewData.WorkGiveDateTimeString}交接班审阅日志.xlsx`
      // Excel第一个sheet的名称
      const ws_name = 'Sheet1'
      const wb = XLSX.utils.book_new()
      const ws = XLSX.utils.json_to_sheet(aoa, {
        skipHeader: true,
      })
      // 设置宽度
      ws["!cols"] = [
        { wpx: 100, },
        { wpx: 120, },
        { wpx: 120, },
        { wpx: 100, },
        { wpx: 100, },
        { wpx: 100, },
        { wpx: 100, },
        { wpx: 100, },
        { wpx: 100, },
        { wpx: 100, },
      ];
      ws['!merges'] = sheet['!merges']
      XLSX.utils.book_append_sheet(wb, ws, ws_name) // 将数据添加到工作薄
      XLSX.writeFile(wb, filename) // 导出Excel

    },

2 .excel 导出综合案例(如果其他的看不明白,直接看这个)

最终结果: 如果把这个弄懂,相信可以解决绝大部分的需求!!!

js 复制代码
<template>
  <div class="home">
    <el-button type="primary" @click="export">excel导出</el-button>
  </div>
</template>
<script>
import XLSX from "xlsx-js-style";
export default {
  name: 'HomeView',
  components: {
  },
  data () {
    return {
    }
  },
  methods: {
    export () {
      let excelData = [
        ['幼儿园课表', null, null, null], // 标题
        ['序号', '课程名称', '教师名称', '上课地点'], // 表头
        ['1', '体育', '大头老师', '操场'],
        ['2', '体育', '大头老师', '操场'],
        ['3', '化学', '李老师', '操场'],
        ['4', '化学', '李老师', '操场'],
      ];
      /*
      电子表格软件通常需要至少一个工作表,并强制执行 用户界面中的要求。例如,如果删除了最后一个工作表 在程序中,Apple Numbers 将自动创建一个新的空白表。
    SheetJS 写入函数强制执行此要求。 它们在尝试导出空工作簿时会引发错误。
      */
      const wb = XLSX.utils.book_new();//创建新工作簿
      const ws = XLSX.utils.aoa_to_sheet(excelData);//aoa_to_sheet将 JS 数据数组的数组转换为工作表。

      let mergerarr = [
        // 第一行的 0到3列进行合并
        { s: { r: 0, c: 0 }, e: { r: 0, c: 3 } },
        // 将第单列的第五行第六行进行合并
        { s: { r: 4, c: 2 }, e: { r: 5, c: 2 } },

      ];
      // 设置合并的单元格
      ws['!merges'] = mergerarr;
      // 设置宽度
      // !cols 设置列宽
      // cols 为一个对象数组,依次表示每一列的宽度。
      ws["!cols"] = [
        { wpx: 200, },
        { wpx: 120, },
        { wpx: 220, },
        { wpx: 200, },
        { wpx: 200, },
      ];
      // !rows 设置行高
      // rows 为一个对象数组,依次表示每一行的高度
      const rows = [
        { hpx: 20 },
        { hpx: 16 },
        { hpx: 18 },
        { hpx: 28 },
        { hpx: 20 },
      ]
      ws['!rows'] = rows; // 添加到sheet中
      let borderAll = { //单元格外侧框线
        top: {
          style: 'thin',//BORDER_STYLE 是用来设置边框样式的一个字符串,
        },
        bottom: {
          style: 'thin'
        },
        left: {
          style: 'thin'
        },
        right: {
          style: 'thin'
        }
      };
      // 设置每一个单元格
      for (let key in ws) {
        if (ws[key] instanceof Object) {
          if (key === 'B2') {//给B2这个单元格单独设置样式和背景颜色
            ws[key].s = {
              border: borderAll,
              alignment: {
                horizontal: 'center', //水平居中对齐
                vertical: 'center',//垂直居中
                wrapText: 1,//自动换行
              },
              font: {
                sz: 12,//单元格中字体的样式与颜色设置
                color: {
                  rgb: 'FF0187FA'
                }
              },
              bold: false,//是否加粗
              numFmt: 0,
              // fill 颜色填充属性
              fill: {
                fgColor: { rgb: '87CEEB' },
              },
            }
          } else {
            ws[key].s = {
              border: borderAll,
              alignment: {
                horizontal: 'center', //水平居中对齐
                vertical: 'center',//垂直居中
                wrapText: 1,//自动换行
              },
              font: {
                sz: 12,//单元格中字体的样式与颜色设置
                color: {
                  rgb: 'FF0187FA'
                }
              },
              bold: false,//是否加粗
              numFmt: 0,
              // fill 颜色填充属性
              fill: {
                fgColor: { rgb: 'FFFFFF' },
              },
            }
          }
        }
      };
      XLSX.utils.book_append_sheet(wb, ws, "readme demo");//将上面创建好的ws页添加到wb工作簿中表格内名字为readmedemo
      XLSX.writeFile(wb, "xlsx-js-style-demo.xlsx");////调用导出api
    },
  },
  created () { }
}
</script>
<style lang="less" scoped>
</style>

3. 导出PDF

导出pdf简单多了~

1.安装两个库

js 复制代码
npm install html2canvas
js 复制代码
npm install  jspdf
  1. utils文件夹下新建html2pdf.js文件
js 复制代码
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf'

export const htmlToPDF = async (htmlId, title = "报表", bgColor = "#fff") => {
    let pdfDom = document.getElementById(htmlId)
    pdfDom.style.padding = '0 10px !important'
    const A4Width = 595.28;
    const A4Height = 841.89;
    let canvas = await html2canvas(pdfDom, {
        scale: 2,
        useCORS: true,
        backgroundColor: bgColor,
    });
    let pageHeight = (canvas.width / A4Width) * A4Height;
    let leftHeight = canvas.height;
    let position = 0;
    let imgWidth = A4Width;
    let imgHeight = (A4Width / canvas.width) * canvas.height;
    /*
     根据自身业务需求  是否在此处键入下方水印代码
    */
    //添加水印
    const ctx = canvas.getContext('2d');
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.rotate((20 * Math.PI) / 180);
    ctx.font = '20px Microsoft Yahei';
    ctx.fillStyle = 'rgba(184, 184, 184, 0.8)';
    for (let i = canvas.width * -1; i < canvas.width; i += 240) {
        for (let j = canvas.height * -1; j < canvas.height; j += 200) {
            // 填充文字,x 间距, y 间距
            ctx.fillText('水印名33', i, j);
        }
    }

    
    let pageData = canvas.toDataURL("image/jpeg", 1.0);
    let PDF = new jsPDF("p", 'pt', 'a4');
    if (leftHeight < pageHeight) {
        PDF.addImage(pageData, "JPEG", 0, 0, imgWidth, imgHeight);
    } else {
        while (leftHeight > 0) {
            PDF.addImage(pageData, "JPEG", 0, position, imgWidth, imgHeight);
            leftHeight -= pageHeight;
            position -= A4Height;
            if (leftHeight > 0) PDF.addPage();
        }
    }

    PDF.save(title + ".pdf");
}
  1. 使用

导出结果:

参考链接:

CSDN博客_js导出excel设置样式

Vue 前端导出Excel表格,多级表头合并

xlsx-style ./cptable' 报错解决办法 - 龙果果 - 博客园

ps: 如果你觉得 xlsx-style 的功能还不够全面,不能实现你需求,这里再推荐一个项目 ExcelJS,这个项目的功能更加全面,而且项目也还在维护,可以试试看能否满足需求。

相关推荐
别拿曾经看以后~1 小时前
【el-form】记一例好用的el-input输入框回车调接口和el-button按钮防重点击
javascript·vue.js·elementui
我要洋人死1 小时前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人1 小时前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人1 小时前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR1 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香1 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q2498596931 小时前
前端预览word、excel、ppt
前端·word·excel
小华同学ai1 小时前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_9151 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼3 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍