利用js-xlsx前端導出Excel

因爲項目臨時加入導出Excel功能,後臺童靴沒有多餘時間來處理,想前端來處理導出。前端

查詢了前端導出方法,大部分是利用js-xlsx來作處理,按照js-xlsx來導出excel時是沒有樣式處理的。須要咱們來利用該庫的專業版本才能設置樣式。git

這裏就須要xlsx-style來處理,給咱們的excel文件來添加樣式。github

利用數據導出

拿到後端數據,先要對數據進行組裝才能導出咱們想要的文件格式。若是想文件有樣式只能利用xlsx-style;npm

npm install xlsx-style

拷貝下載下來的文件目錄dist裏的xlsx.core.min.js,引入文件後端

圖片描述

這裏借鑑這位博主主要代碼,整理了下,能夠實現表頭與導出文件名稱。dom

具體調用能夠查看DEMOthis

/**
 * @param {Array} header 表格頭部
 * @param {Array} body 表格數據
 * @param {String} title 表格導出名稱
 * @param {Boolean} hasTitle 是否須要表格標題
 */
function ExportsEXCL() {
  this.downLoad = ({
    header = [],
    body = [],
    title = 'excel',
    hasTitle = false,
  }) => {
    const styleCell = this.setBorderStyle();

    const _headers = header
      .map((v, i) => {
        let key = Object.keys(v);
        return Object.assign(
          {},
          {
            v: `${v[key[0]]}<key>${key[0]}`,
            position: String.fromCharCode(65 + i) + (hasTitle ? 1 : 0)
          }
        );
      })
      .reduce(
        (prev, next) =>
          Object.assign({}, prev, {
            [next.position]: { v: next.v, s: styleCell }
          }),
        {}
      );
    const _body = body
      .map((v, i) =>
        header.map((k, j) => {
          let key = Object.keys(k);
          return Object.assign(
            {},
            {
              v: v[key[0]],
              position: String.fromCharCode(65 + j) + (i + (hasTitle ? 2 : 1))
            }
          );
        })
      )
      .reduce((prev, next) => prev.concat(next))
      .reduce(
        (prev, next) =>
          Object.assign({}, prev, {
            [next.position]: { v: next.v, s: styleCell }
          }),
        {}
      );

    const mergeThead = this.setMergeThead(_headers, hasTitle, title);

    const _merges = this.setTableMerges(header, _headers, hasTitle);

    const _thead = this.setTableThead(mergeThead);

    const output = Object.assign({}, _thead, _body);

    const outputPos = Object.keys(output).sort();

    const ref = outputPos[0] + ':' + outputPos[outputPos.length - 1];

    const wb = {
      SheetNames: ['mySheet'],
      Sheets: {
        mySheet: Object.assign({}, output, { '!ref': ref, '!merges': _merges })
      }
    };

    this.save(wb, `${title}.xlsx`);
  };

  this.setTableThead = wb => {
    for (let key in wb) {
      let i = wb[key].v.indexOf('<key>');
      if (wb[key].v.includes('<key>')) {
        wb[key].v = wb[key].v.substr(0, i);
      }
    }
    return wb;
  };

  // 設置合併表頭
  this.setTableMerges = (header, wb, hasTitle) => {
    let _merges = [];
    let len = header.length - 1;
    if (hasTitle) {
      let o = {
        s: {
          c: 0,
          r: 0
        },
        e: {
          c: len,
          r: 0
        }
      };
      _merges.push(o);
    }
    return [..._merges];
  };

  // 設置表頭
  this.setMergeThead = (wb, merge, hasTitle, title) => {
    const borderAll = {
      top: {
        style: 'thin'
      },
      bottom: {
        style: 'thin'
      },
      left: {
        style: 'thin'
      },
      right: {
        style: 'thin'
      }
    };
    if (hasTitle) {
      wb['A1'] = {
        v: `${title}`,
        s: {
          border: borderAll,
          font: {
            sz: 18,
            bold: true
          },
          alignment: {
            horizontal: 'center'
          }
        }
      };
    }
    return wb;
  };

  this.setBorderStyle = () => {
    const borderAll = {
      top: {
        style: 'thin'
      },
      bottom: {
        style: 'thin'
      },
      left: {
        style: 'thin'
      },
      right: {
        style: 'thin'
      }
    };
    return { border: borderAll };
  };

  this.save = (wb, fileName) => {
    let wopts = {
      bookType: 'xlsx',
      bookSST: false,
      type: 'binary'
    };
    let xw = XLSX.write(wb, wopts);
    let obj = new Blob([this.s2ab(xw)], {
      type: ''
    });
    let elem = document.createElement('a');
    elem.download = fileName || '下載';
    elem.href = URL.createObjectURL(obj);
    elem.click();
    setTimeout(function() {
      URL.revokeObjectURL(obj);
    }, 100);
  };

  this.s2ab = s => {
    if (typeof ArrayBuffer !== 'undefined') {
      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;
    } else {
      var buf = new Array(s.length);
      for (var i = 0; i != s.length; ++i) buf[i] = s.charCodeAt(i) & 0xff;
      return buf;
    }
  };

  // 根據val查詢Object key
  this.findKey = (val, obj) => {
    return Object.keys(obj).find(v => obj[v] === val);
  };
}

利用DOM導出

js-xlsx提供了一個直接導出HTML上的DOM表格方法XLSX.utils.table_to_book,可是導出的excel文件時沒有任何樣式,利用xlsx-style中的xlsx.full.min.js來導出時,沒有上面這個方法,後面取了巧。spa

xlsx-style中的full文件導出來的XLSX改爲了STYLEXLSX3d

圖片描述

引用以下:excel

<script src="//unpkg.com/xlsx/dist/shim.min.js"></script>
<script src="//unpkg.com/blob.js@1.0.1/Blob.js"></script>
<script src="//unpkg.com/file-saver@1.3.3/FileSaver.js"></script>
<script src="./STYLEXLSX.full.min.js"></script>
<script src="./xlsx.full.min.js"></script>

<script src="./index.js"></script>

利用js-xlsx來導出表格數據,用STYLEXLSX.write來寫入數據。

STYLEXLSX.write(
  wb,
  {
    bookType: type == undefined ? 'xlsx' : type,
    bookSST: false,
    type: 'binary'
  }
)

具體主要代碼:

/**
 * 
 * @param {Object} dom table的dom元素
 * @param {Object} name 導出的表名
 * @param {Object} type 導出的類型
 * 
 */
function exportExl(dom, name = '導出數據', type) {
    var wb = XLSX.utils.table_to_book(dom, {sheet: "Sheet JS"});
    var wopts = {
    bookType: 'xlsx',
    bookSST: true,
    type: 'binary',
    cellStyles: true
    };

    setExlStyle(wb['Sheets']['Sheet JS']);
    
    let tmpDown = new Blob(
        [
            this.s2ab(
                STYLEXLSX.write(
                    wb,
                    {
                        bookType: type == undefined ? 'xlsx' : type,
                        bookSST: false,
                        type: 'binary'
                    } //這裏的數據是用來定義導出的格式類型
                )
            )
        ],
        {
            type: ''
        }
    );
    saveAs(
        tmpDown,
        `${name}` +
            '.' +
            (wopts.bookType == 'biff2' ? 'xls' : wopts.bookType)
    );    
}

function saveAs(obj, fileName) {
    let tmpa = document.createElement('a');
    tmpa.download = fileName || '下載';
    tmpa.href = URL.createObjectURL(obj);
    tmpa.click();
    setTimeout(function() {
        URL.revokeObjectURL(obj);
    }, 100);
}    


function s2ab (s){
    if (typeof ArrayBuffer !== 'undefined') {
        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;
    } else {
        var buf = new Array(s.length);
        for (var i = 0; i != s.length; ++i) buf[i] = s.charCodeAt(i) & 0xff;
        return buf;
    }
};

function setExlStyle (data) {  
    
  let borderAll = {  //單元格外側框線
    top: {
      style: 'thin'
    },
    bottom: {
      style: 'thin'
    },
    left: {
      style: 'thin'
    },
    right: {
      style: 'thin'
    }
  }; 
    data['!cols'] = [];
    for (let key in data) {
        if (data[key] instanceof Object) {
            data[key].s = {
                border: borderAll,
                alignment: {
                    horizontal: 'center'   //水平居中對其
                },
                numFmt: 0
            }
            data['!cols'].push({wpx: 170});
        }
    }
    return data;
}

缺陷

若是用數據來控制咱們的導出,發現配置多級表頭時會好複雜,因此沒去深究了,用DOM來導出,又發現若是要導出分頁表格時只能導出當前頁的數據。

並且如今全部的表格均可能不是規範的表格,每一個表格結構可能都不相同,只能單獨處理。

當前全部代碼查看

相關文章
相關標籤/搜索