js export xlsx with style

基于react,xlsx, xlsx-style实现export xlsx文件,可以设置单元格合并,border,背景色等。
https://github.com/protobi/js-xlsx
https://github.com/SheetJS/js-xlsx/

  1. 在webpack.js 中设置: 解决fs编译问题
node: {
    fs: 'empty',
  },
  1. xlsx-style中dist/cpexcel.js中的部分代码注释掉,具体代码如下
  // if(typeof cptable === "undefined") {
  //   if(typeof require !== "undefined"){
  //     var cpt = require('./cpt' + 'able');
  //     if (typeof module !== 'undefined' && module.exports) module.exports = factory(cpt);
  //     else root.cptable = factory(cpt);
  //   } else throw new Error("cptable not found");
  // } else cptable = factory(cptable);
  1. main.js
import React, { Component } from 'react';
import PropTypes from 'prop-types'
import XLSX from 'xlsx-style'

const headerConfig = [
[{
  name: '1111',
  colspan: 3,
  C: 0
},{
  name: '2222',
  rowspan: 2,
  C: 3
},
...],[{
  name: '3333',
  colspan: 3,
  C: 0
},{
  name: '4444',
  C: 4
},
...],
...
];
let dataConfig = ['element1','element1',...];

// cell style config

let borderStyle = {

  top: {style: "thin", color: "000000"},

  bottom: {style: "thin", color: "000000"},

  left: {style: "thin", color: "000000"},

  right: {style: "thin", color: "000000"}

};

// workbook

let ws = {

  '!merges': []

};               

let range = {s: {c:10000000, r:10000000}, e: {c:0, r:0 }};

function sheet_from_array_of_arrays(headerConfig, data) {

  let cellWidth = [{wch:10},{wch:10},{wch:10},{wch:15},{wch:15},{wch:25},{wch:50},{wch:20},{wch:60},{wch:10},

                  {wch:40},{wch:10},{wch:15},{wch:15},{wch:10},{wch:20},{wch:30},{wch:20},{wch:30},{wch:10},{wch:40},{wch:20},{wch:10},{wch:10},{wch:70}];

  let titleStyle = {

    fill:{

      fgColor: { rgb: "B22222" }

    },

    alignment: {

      vertical: "center"

    },

    font: {

      bold: true,

      name: "Arial",

      sz: "12",

      color: { rgb: "FFFFFF" }

    }

  };

  ws['!merges'].push({

    s: { c: 0, r: 0 },

    e: { c: 22, r: 4 }

  });

  setTitle('  ERISA Client Validation Form',titleStyle,5,0,2,23);  // set title in workbook

  setHeader(headerConfig,7);

  addData(data,9,0,false);  // add data in workbook

  addData(Q6Options,0,23,true);  //add Q6_options in workbook

  ws['!cols'] = cellWidth; // set cell width

  if(range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);

  return ws;

}

function Workbook() {

  if(!(this instanceof Workbook)) return new Workbook();

  this.SheetNames = [];

  this.Sheets = {};

}

function datenum(v, date1904) {

  if(date1904) v+=1462;

  let epoch = Date.parse(v);

  return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);

}

function 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;

}

function setTitle(title,style,R,C,rowspan,colspan){

  if(range.s.r > R) range.s.r = R;

  if(range.s.c > C) range.s.c = C;

  if(range.e.r < R) range.e.r = R;

  if(range.e.c < C) range.e.c = C;

  var cell = {v: title };

  var cell_ref = XLSX.utils.encode_cell({c:C,r:R});

  cell.t = 's';

  cell.s = style;

  ws[cell_ref] = cell;

  ws['!merges'].push({

    s: { c: C, r: R },

    e: { c: C+colspan-1, r: R+rowspan-1 }

  });

}

function setHeader(headerConfig,R){

  headerConfig.map(item => {

    let C = 0;

    item.map(itm => {

      if(range.s.r > R) range.s.r = R;

      if(range.s.c > C) range.s.c = C;

      if(range.e.r < R) range.e.r = R;

      if(range.e.c < C) range.e.c = C;

      let cell = {v: itm.name };

      let cell_ref = XLSX.utils.encode_cell({c:itm.C,r:R});

      cell.t = 's';

      cell.s = {

        alignment: {

          wrapText: true,

          vertical: "center",

          horizontal: "center"

        },

        border: borderStyle,

        font:{

          bold:true,

          name: "Arial",

          sz: "10"

        }

      };

      ws[cell_ref] = cell;

      let currentC = C;

      if(itm.colspan){

        C += itm.colspan;

        setMergeCellBorder("C",R,currentC,itm.colspan);

        ws['!merges'].push({

          s: { c: currentC, r: R },

          e: { c: C - 1, r: R }

        });

      }else{

        C++;

      }

      if(itm.rowspan){

        setMergeCellBorder("R",R,currentC,itm.rowspan);

        ws['!merges'].push({

          s: { c: currentC, r: R },

          e: { c: currentC, r: R + itm.rowspan - 1 }

        });

      }

    });

      R++;

  });

}

function addData(data, startR, startC,wrapText){

  let R = startR;

  data.map(item => {

    let C = startC;

    item.map(itm => {

      if(range.s.r > R) range.s.r = R;

      if(range.s.c > C) range.s.c = C;

      if(range.e.r < R) range.e.r = R;

      if(range.e.c < C) range.e.c = C;

      var cell = {v: itm };

      var cell_ref = XLSX.utils.encode_cell({c:C,r:R});

      cell.t = 's';

      let borderStyle = {

        bottom: {style: "thin", color: "000000"}

      };

      borderStyle.top = R === startR ? {style: "medium", color: "000000"} : {style: "thin", color: "000000"};

      if([0,3,4,5,6,8,10,16,20,21].indexOf(C) >=0){

        borderStyle.left = {style: "medium", color: "000000"};

        borderStyle.right = {style: "thin", color: "000000"};

      }else if(C === 22){

        borderStyle.left = {style: "medium", color: "000000"};

        borderStyle.right = {style: "medium", color: "000000"};

      }else{

        borderStyle.left = {style: "thin", color: "000000"};

        borderStyle.right = {style: "thin", color: "000000"};

      }

      cell.s={

        alignment: {

          vertical: "center",

          horizontal: "left",

          wrapText: wrapText

        },

        border: borderStyle,

        font: {

          name: "Arial",

          sz: "10"

        }

      };

      ws[cell_ref] = cell;

      C++;

    });

    R++;

  });

}

function setMergeCellBorder(type,R,C,n){

  let s = type === "C" ? C : R;

  for(let i = s+1; i < s+n; i++){

    let cell_ref_merge = type === "C" ? XLSX.utils.encode_cell({c:i,r:R}) : XLSX.utils.encode_cell({c:C,r:i});

    let cell_merge = {v: ""};

    cell_merge.t = 's';

    cell_merge.s={

      border: borderStyle

    };

    ws[cell_ref_merge] = cell_merge;

  }

}

export default function save(data,filename){

  let rows = preprocessData(data,dataConfig);

  var ws_name = "ERISA Client Validation";

  var wb = new Workbook(), ws = sheet_from_array_of_arrays(headerConfig,rows);

  /* add worksheet to workbook */

  wb.SheetNames.push(ws_name);

  wb.Sheets[ws_name] = ws;

  var wbout = XLSX.write(wb, {bookType:'xlsx', bookSST:true, type: 'binary'});

  var excelBlob = new Blob([s2ab(wbout)],{type:"application/octet-stream"});

  var Sys = _browerVersion();

  if(Sys.isIE){

    navigator.msSaveBlob(excelBlob,filename + '.xlsx');

  }else{

  var oA = document.createElement('a');

  oA.href = URL.createObjectURL(excelBlob);

  oA.download = filename + '.xlsx';

  oA.click();

  }

}

function preprocessData(data,dataConfig){

  let rows = [];

  data.map(item => {

    let row = [];

    dataConfig.map(itm => {

      let value = item[itm] || "";

      row.push(value);

    });

    rows.push(row);

  });

  return rows;

}

function _browerVersion(){

  var Sys = {};

  //judge the brower version

  var userAgent = navigator.userAgent;

  console.log(userAgent);

  var isOpera = userAgent.indexOf("Opera") > -1; //whether Opera

  var isIE=window.ActiveXObject || "ActiveXObject" in window

  // var isEdge = userAgent.indexOf("Windows NT 6.1; Trident/7.0;") > -1 && !isIE; //whether IE的Edge浏览器

  var isEdge = userAgent.indexOf("Edge") > -1; //whether IE Edge

  var isFF = userAgent.indexOf("Firefox") > -1; //whether Firefox

  var isSafari = userAgent.indexOf("Safari") > -1 && userAgent.indexOf("Chrome") == -1; //whether Safari

  var isChrome = userAgent.indexOf("Chrome") > -1 && userAgent.indexOf("Safari") > -1&&!isEdge; //whether Chrome

  if(isChrome) Sys.isChrome = true;

  if(isIE) Sys.isIE = true;

  return Sys;

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容