使用VUE+XLSX实现纯前端的导出Excel


此方法仅在@vue/cli3的项目中使用,其他项目暂未测试~

1、安装需要的插件

npm i xlsx -S

npm i xlsx-style -S

npm i file-saver -S

安装完xlsx-style插件并引入后启动项目会报错
./cptable in ./node_modules/xlsx-style/dist.cpexcel.js
出现这个问题时,需要在vue.config.js中的configureWebpack配置项中添加如下配置即可:
externals:{
'./cptable':'var cptable'
}

2、引入并使用

点击查看代码
//引入相关插件
import XLSX from "xlsx";
import XLSXStyle from 'xlsx-style';
import XLSX_SAVE from 'file-saver';
	
//导出的源数据
let originData = [//每一个Object对应excel一行的内容
  {
    "表头1":'value1',
    "表头2":"value2",
    "表头3":"value3"
  },
  {
    "表头1":'value1',
    "表头2":"value2",
    "表头3":"value3"
  }
];

let wb = {
  SheetNames:['工作簿名称'],
  Sheets:{},
  Props:{}
};

let ws = XLSX.utils.json_to_sheet(originData);
	
//设置表格每列的宽度
ws['!cols'] = [200,200,200];
	
//设置单元格属性
Object.keys(ws).forEach(key => {
  if(key.indexOf('!') < 0) {//判断是否是每个单元格
    //获取单元格的序号
    let num = key.replace(/[^0-9]/ig,"");
    if(num.length === 1 && +num ===1){//判断是否为首行
      //设置首行样式
      ws[key].s = {
        alignment: {
          horizontal:'center',//水平居中
          vertical:'center',//垂直居中
          wrapText:true//是否自动换行
        },
        border: {//设置边框
          top:{style:'thin'},
          bottom:{style:'thin'},
          right:{style:'thin'}
        },
        fill:{
          fgColor:{rgb:ws[key].v.indexOf('必填') === -1 ? "E1F3D8" : "F56C6C"};//单元格背景色的填充
        }
      }
    } else {//非首行的单元格样式
      ws[key].s = {
        alignment: {
          horizontal:'center',
          vertical:'center',
          wrapText:true
        }
      }
    }
  }
})
						  
wb.Sheets[工作簿名称] = ws;
const wbout = XLSXStyle.write(wb,{
  type:"binary",
  bookType:"xlsx"
})
XLSX_SAVE.saveAs(new Blob([s2ab(wbout)],{type:'application/octet-stream'}),导出的excel文件名称);
						  
const s2ab = s => {
  let buf = new ArrayBuffer(s.length);
  let view = new Uint8Array(buf);
  for(let i = 0; i != s.length; i++) view[i] = s.charCodeAt(i) & 0xFF;
  return buf;
}

3、vue项目中的简单封装使用

单工作簿的导出,可直接复制调用

点击查看代码
//导出excel方法
import XLSX from "xlsx";
import XLSXStyle from 'xlsx-style';
import XLSX_SAVE from 'file-saver';

/*
 *
 * originData => 导出的表格数据,最后传入的格式为:  ****必传****
 *  [
 *    {
 *      '表头1':'xxx1',
 *      '表头2':'yyy1',
 *      '表头3':'zzz1',
 *    },
 *    {
 *      '表头1':'xxx2',
 *      '表头2':'yyy2',
 *      '表头3':'zzz2',
 *    },
 *  ]
 *
 * sheetName => excel表格里的sheet名称  ****必传****
 *
 * excelName => excel表格的名称  ****必传****
 *
 * colWidth => 表格列宽  可传可不传
 *    ==> 不传默认为200
 *    ==> 可传数字,代表所有列宽均为此宽度
 *    ==> 可传数组,[200,300,400,500,...]按数组值依次设置列宽
 *
 */


export const exportExcel = (originData,sheetName,excelName,colWidth) =>{
  let sheet  = sheetName ? sheetName : '导出的excel';
  let excel  = excelName ? excelName : '导出的excel';
  let columnWidth = [];
  if (colWidth){
    if (typeof colWidth === "number"){//只传了列宽的值,表明所有列宽均为此值
      if (originData.length) {
        Object.keys(originData[0]).forEach(item=>{
          columnWidth.push({wpx:colWidth})
        })
      }
    }else if (colWidth instanceof Array) {//传进来的是数组,按顺序依次赋值
      colWidth.forEach(item=>{
        columnWidth.push({wpx:item});
      })
    }
  }else {
    if (originData.length) {
      Object.keys(originData[0]).forEach(item=>{
        columnWidth.push({wpx:200})
      })
    }
  }
  var wb = {
    SheetNames: [sheet],
    Sheets: {},
    Props: {},
  }
  var ws = XLSX.utils.json_to_sheet(originData);
  ws['!cols'] = columnWidth;
  Object.keys(ws).forEach((key) => {//设置单元格属性
    if (key.indexOf('!') < 0) {
      //获取单元格的数字
      let num =  key.replace(/[^0-9]/ig,"");
      if (num.length === 1 && +num === 1){
        ws[key].s = {
          alignment: {
            horizontal: 'center',
            vertical: 'center',
            wrapText: true,
          },
          border: {  //单元格外侧框线
            top: {
              style: 'thin'
            },
            bottom: {
              style: 'thin'
            },
            right: {
              style: 'thin'
            }
          },
          fill:{
            fgColor: { rgb: ws[key].v.indexOf('必填') === -1 ? "E1F3D8" : 'F56C6C' },
          }
        }
      }else {
        ws[key].s = {
          alignment: {
            horizontal: 'center',
            vertical: 'center',
            wrapText: true,
          },
        }
      }
    }
  })

  wb.Sheets[sheet] = ws
  const wbout = XLSXStyle.write(wb, {
    type: 'binary',
    bookType: 'xlsx',
  })
  XLSX_SAVE.saveAs(new Blob([s2ab(wbout)], {type: 'application/octet-stream',}), `${excel}.xlsx`);
}

const s2ab = (s) =>{
  var buf = new ArrayBuffer(s.length);
  var view = new Uint8Array(buf);
  for (var i = 0; i != s.length; i++) view[i] = s.charCodeAt(i) & 0xFF;
  return buf;
}


多工作簿的导出,可直接复制调用

点击查看代码
//导出excel方法
import XLSX from "xlsx";
import XLSXStyle from 'xlsx-style';
import XLSX_SAVE from 'file-saver';

/*
 *
 * originArr => 导出的表格数据,最后传入的格式为:  ****必传****
 *  [
 *    {
 *       originData:[
 *        {
 *          '表头1':'xxx1',
 *          '表头2':'yyy1',
 *          '表头3':'zzz1',
 *        },
 *        {
 *          '表头1':'xxx2',
 *          '表头2':'yyy2',
 *          '表头3':'zzz2',
 *        },
 *      ],
 *      sheetName:"xxxx",
 *      colWidth:200
 *    }
 *  ]
 *
 *
 * excelName => excel表格的名称  ****必传****
 *
 *
 */


export const exportMoreExcel = (originArr,excelName) =>{
  let sheetName = [];
  let excel  = excelName ? excelName : '导出的excel';
  originArr.forEach(item=>{
    sheetName.push(item.sheetName);
  })
  var wb = {
    SheetNames: sheetName,
    Sheets: {},
    Props: {},
  }
  originArr.forEach(originItem=>{
    let {originData,sheetName,colWidth} = originItem;
    let columnWidth = [];
    if (colWidth){
      if (typeof colWidth === "number"){//只传了列宽的值,表明所有列宽均为此值
        if (originData.length) {
          Object.keys(originData[0]).forEach(item=>{
            columnWidth.push({wpx:colWidth})
          })
        }
      }else if (colWidth instanceof Array) {//传进来的是数组,按顺序依次赋值
        colWidth.forEach(item=>{
          columnWidth.push({wpx:item});
        })
      }
    }else {
      if (originData.length) {
        Object.keys(originData[0]).forEach(item=>{
          columnWidth.push({wpx:200})
        })
      }
    }

    var ws = XLSX.utils.json_to_sheet(originData);
    ws['!cols'] = columnWidth;
    Object.keys(ws).forEach((key) => {//设置单元格属性
      if (key.indexOf('!') < 0) {
        //获取单元格的数字
        let num =  key.replace(/[^0-9]/ig,"");
        if (num.length === 1 && +num === 1){
          ws[key].s = {
            alignment: {
              horizontal: 'center',
              vertical: 'center',
              wrapText: true,
            },
            border: {  //单元格外侧框线
              top: {
                style: 'thin'
              },
              bottom: {
                style: 'thin'
              },
              right: {
                style: 'thin'
              }
            },
            fill:{
              fgColor: { rgb: ws[key].v.indexOf('必填') === -1 ? "E1F3D8" : 'F56C6C' },
            }
          }
        }else {
          ws[key].s = {
            alignment: {
              horizontal: 'center',
              vertical: 'center',
              wrapText: true,
            },
          }
        }
      }
    })

    wb.Sheets[sheetName] = ws
  })

  const wbout = XLSXStyle.write(wb, {
    type: 'binary',
    bookType: 'xlsx',
  })
  XLSX_SAVE.saveAs(new Blob([s2ab(wbout)], {type: 'application/octet-stream',}), `${excel}.xlsx`);
}

const s2ab = (s) =>{
  var buf = new ArrayBuffer(s.length);
  var view = new Uint8Array(buf);
  for (var i = 0; i != s.length; i++) view[i] = s.charCodeAt(i) & 0xFF;
  return buf;
}