前端JS导出excel表格

4,043 阅读3分钟

前端导出excel

之前写过一个js导出excel的代码,一直用着好好的,最近突然报错了,研究了半天发现在表格列数超过26列的时候就会出现这个问题,也是就excel的列是A-Z,然后跟着是AA,AB,AC...这种,在超出26列时前端导出就会报错了,所以又加了下特殊处理,在超过26列时生成sheet中修改了列头,给大家分享一下

this.handleExport = function () {
    // 此处_this.onShowItemList为要导出的数据列表
    var downloadItemList = angular.copy(_this.onShowItemList);
    downloadItemList.forEach(function (item) {
        Object.keys(item).forEach(function (key) {
            var val = 0;
            // 这里对英文逗号做了下处理,转换成了中文逗号,否则后续使用时会有问题
            if (!isNaN(Number(item[key])) || item[key].indexOf(',') > -1) {
                val = item[key].replace(/,/g, '');
                item[key] = val;
            }
        })
    })
    generateExcel(downloadItemList, _this.menuName + '.xlsx');
}

/**
 * @listItems 要导出的数据
 * @excelName 导出excel的名称
 */
function generateExcel(listItems, excelName) {
    _this.exportAllData = [];
    _this.exportAllData.push(['序号']);
    // 这里是有多列表头的情况,正常直接使用else中的部分就可以了
    if (_this.titleList3 && _this.titleList3.length > 0 && _this.titleList2 && _this.titleList2.length > 0) {
        _this.exportAllData.push([1]);
        _this.exportAllData.push([2]);
        _this.titleList3.forEach(function (item) {
            _this.exportAllData[0].push(item);
        })
        _this.titleList2.forEach(function (item) {
            _this.exportAllData[1].push(item);
        })
        angular.forEach(_this.titleList, function (a, b, c) {
            _this.exportAllData[2].push(a);
        })
    } else if (_this.titleList2 && _this.titleList2.length > 0) {
        _this.exportAllData.push([1]);
        _this.titleList2.forEach(function (item) {
            _this.exportAllData[0].push(item);
        })
        angular.forEach(_this.titleList, function (a, b, c) {
            _this.exportAllData[1].push(a);
        })
    } else {
        angular.forEach(_this.titleList, function (a, b, c) {
            _this.exportAllData[0].push(a);
        })
    }
    angular.forEach(listItems, function (value, key) {
        var num = Object.keys(_this.itemList[_this.itemList.length - 1]).length;
        _this.exportAllData[key + num] = [key + num];
        angular.forEach(_this.valueList, function (a, b, c) {
            _this.exportAllData[key + num].push(value[a]);
        })
    })
    var str = '';
    for (var i = 0; i < _this.exportAllData.length; i++) {
        for (var item in _this.exportAllData[i]) {
            var newStr = '';
            if (_this.exportAllData[i][item] && _this.exportAllData[i][item].length > 0) {
                newStr = isNaN(_this.exportAllData[i][item]) ? (_this.exportAllData[i][item] || '') : _this.exportAllData[i][item].replace(',', ',')
            } else {
                newStr = _this.exportAllData[i][item] || '';
            }
            str += newStr + '\t,';
        }
        str += '\n';
    }
    exportExcel(str, excelName);
}

function csv2sheet(csv) {
    var sheet = {}; // 将要生成的sheet
    csv = csv.split('\n');
    csv.forEach(function (row, i) {
        row = row.split(',');
        var tmpArr = [];
        row.forEach(function (item) {
            tmpArr.push(item.trim());
        });
        row = tmpArr;
        // 这里就是文章开头提到的当列数超过26列时的处理
        var charCode = row.length > 26 ? String.fromCharCode(65 + row.length/26 - 1) + String.fromCharCode(65 + row.length%26 - 1) : String.fromCharCode(65 + row.length - 1);
        if (i == 0) sheet['!ref'] = 'A1:' + charCode + (csv.length - 1);
        row.forEach(function (col, j) {
            var code = j > 26 ? String.fromCharCode(65 + row.length/26 - 1) + String.fromCharCode(65 + j%26) : String.fromCharCode(65 + j);
            // 这一部分是判断是否为百分比类型的数据,如果是的话当前单元格按照nnumber类型处理,
            // 这样做的好处是方便导出的文件直接进行排序
            if (col.indexOf('%') > -1 && !isNaN(col.trim().replace('%', ''))) {
                var percentValue = col.trim();
                sheet[code + (i + 1)] = {
                    v: Number(percentValue.substring(0, percentValue.length - 1)) / 100,
                    t: 'n',
                    z: '0.00%'
                };
            } else {
                sheet[code + (i + 1)] = {v: Number((col).trim()) || col.trim(), t: 'n'};
            }
        });
    });
    return sheet;
}

// 将一个sheet转成最终的excel文件的blob对象,然后利用URL.createObjectURL下载
function sheet2blob(sheet, sheetName) {
    sheetName = sheetName || 'sheet1';
    var workbook = {
        SheetNames: [sheetName],
        Sheets: {}
    };
    workbook.Sheets[sheetName] = sheet;
    // 生成excel的配置项
    var wopts = {
        bookType: 'xlsx', // 要生成的文件类型
        bookSST: false, // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性
        type: 'binary'
    };
    var wbout = XLSX.write(workbook, wopts);
    var blob = new Blob([s2ab(wbout)], {type: "application/octet-stream"});

    // 字符串转ArrayBuffer
    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;
    }

    return blob;
}

function openDownloadDialog(url, saveName) {
    if (typeof url == 'object' && url instanceof Blob) {
        url = URL.createObjectURL(url); // 创建blob地址
    }
    var aLink = document.createElement('a');
    aLink.href = url;
    aLink.download = saveName || ''; // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效
    var event;
    if (window.MouseEvent) event = new MouseEvent('click');
    else {
        event = document.createEvent('MouseEvents');
        event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
    }
    aLink.dispatchEvent(event);
}

function exportExcel(csv, excelName) {
    var sheet = csv2sheet(csv);
    var blob = sheet2blob(sheet);
    openDownloadDialog(blob, excelName);
}

当时数据导出的数据默认为字符串类型,结果导出excel后无法按照数字进行排序就改了一下,现在又遇到修改的需求,所以记录一下,欢迎大家在评论区留言讨论。❀❀❀

新写了一个java导出excel,支持导出复杂表头的文章,欢迎大家查阅juejin.cn/post/700771…