纯前端处理excel数据

本文介绍了一种纯前端处理Excel数据的方法,通过JavaScript和开源库xlsx实现在浏览器环境中解析Excel文件,将数据转换为JSON格式,并根据设计稿动态生成前端页面。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

纯前端处理excel数据

问题所在

由于公司一直有关于活动、会议等专题前端页面的需求。并且之中会有会议议程,相关表格等。处理这类表格需求,之前的做法有两种:
1.直接使用设计师的设计切图
2.前端将会议议程写进页面中
其实两种方法都可以,但有个问题是这样的表格会进行频繁的更改。比如出席人员、会议项目等,随时有进行修改、删除的可能。这样每次修改都会牵涉相关的人力,每次的小修改会造成大量的人力浪费。
如果时第一种方式,前端开发人员会减少一定的工作量。但对于设计人员,据他们的发聩情况是说这样会非常麻烦。第二种方式前端修改也是相当繁琐。所以就想能不能想办法解决这个问题。

寻找解决方案

解决方案应该满足以下条件:

  • 表格/列表以代码方式实现,不再使用一张切图(这样也是前端偷懒的做法)
  • 设计只负责出设计效果图,前端根据设计搞实现效果
  • 将数据与UI主见相互分离,不会因为数据的更改而使UI不能使用

但是存在一个问题,后期数据的修改如何来处理。是需求修改后,产品再与开发人员沟通进行数据修改。这样其实就没任何的改进,怎样才能让产品可以轻松、方便的进行数据修改。这样就想到了一个方法——excel,产品对于excel的操作肯定是非常熟悉的了,也便于将这个数据修改的工作交到他们手中。

如何实现

首先,由于后端小伙伴工作的繁重。对于excel数据的处理没法让他们进行技术支持,所以只能由前端来处理了。
那我在浏览器的这个环境下如何来处理excel数据呢?javascript当然是可以处理excel数据文件的,但是在浏览器这样的环境下怎么来读取excel这种复杂的文件呢。所以只能去找找有没有这样的插件
在github上找到了一个开源库xlsx,可以通过npm方式来安装。

npm install xlsx --save

在自己的html文件里面添加对js文件的引用

<script src="./node_modules/xlsx/dist/jszip.js"></script>
<script src="./node_modules/xlsx/dist/xlsx.js"></script>

有一个问题,这种数据是希望将它持久化显示的。但又没有后台的支持,只能完全依靠表格文件。所以表格就是一个持久化的数据。
我的思路时用ajax异步去请求文件,相应地就可以读取表格文件数据。只要能拿到数据,进入到javascript环境就有接下来的故事。刚好开源库xlsx也支持这样的方式:

/* set up XMLHttpRequest */
var url = "test_files/formula_stress_test_ajax.xlsx";
var oReq = new XMLHttpRequest();
oReq.open("GET", url, true);
oReq.responseType = "arraybuffer";

oReq.onload = function(e) {
  var arraybuffer = oReq.response;

  /* convert data to binary string */
  var data = new Uint8Array(arraybuffer);
  var arr = new Array();
  for(var i = 0; i != data.length; ++i) arr[i] = String.fromCharCode(data[i]);
  var bstr = arr.join("");

  /* Call XLSX */
  var workbook = XLSX.read(bstr, {type:"binary"});

  /* DO SOMETHING WITH workbook HERE */
}

oReq.send();

这样我就可以将表格数据拿出来处理了。
最终的UI活动议程可能是这样
819452-20161128232822865-35946985.png
也可能是这样的表格
819452-20161128232835084-1519144624.png
所以不能固化到将数据仅仅整理成table表格数据,我们需要将数据处理成前端便于处理的json数据。这样就不怕设计的设计如何变化。

直接上代码

/**
 * [description]
 * excel配置会议议程
 * @author mao
 * @version 1
 * @date    2016-09-28
 */
var excel = (function(mod) {

    /**
     * [checkColumn description]
     * 检测是几列数据
     * @author mao
     * @version 1
     * @date    2016-10-12
     * @param   {object}   workbook excel数据
     * @return  {[object]}            结果数据
     */
    function checkColumn(workbook) {
        var result = workbook.Sheets[(workbook.SheetNames)[0]],
                column,
                data;

        //判断是列excel数据
        column = ((result['!ref']).split(':')[1]).charAt(0);

        //处理成hash结构
        data = processOrigin(result, column);

        return data;
    }

    /**
     * [processOrigin description]
     * @author mao
     * @version 1
     * @date    2016-10-12
     * @param   {object}   result 待处理excel数据
     * @param   {string}   column 有多少列数据
     * @return   {Object}    结果数据
     */
    function processOrigin(result, column) {
        var merges = result['!merges'] || [],  //合并表格的位置信息
                obj,response;

        //生成对应的几项hash数据
        switch(column) {
            case 'B' : 
                obj = {
                    _1:{}
                };
                break;
            case 'C' :
              obj = {
                    _1:{},
                    _2:{}
                };
              break;
            case 'D' :
              obj = {
                    _1:{},
                    _2:{},
                    _3:{}
                };
              break;
            case 'E' :
              obj = {
                    _1:{},
                    _2:{},
                    _3:{},
                    _4:{}
                };
              break;
            default: break;
        }

        //拿到excel初始的hash数据
        for(var i in result) {
            if(i.charAt(0) === '!' || i.charAt(0) === 'A') continue;
            switch(i.charAt(0)) {
                case 'B':{
                    var key = i.slice(1,i.length);
                    obj._1[key] = result[i].v;
                    break;
                }
                case 'C':{
                    var key = i.slice(1,i.length);
                    obj._2[key] = result[i].v;
                    break;
                }
                case 'D':{
                    var key = i.slice(1,i.length);
                    obj._3[key] = result[i].v;
                    break;
                }
                case 'E':{
                    var key = i.slice(1,i.length);
                    obj._4[key] = result[i].v;
                    break;
                }
                default:break;
            }
        }

        //合并项
        response = mergeColumn(obj, merges);

        return response;
    }

    /**
     * [mergeColumn]
     * description
     * @author mao
     * @version 1
     * @date    2016-10-12
     * @param   {obj}   obj    取出的excel数据
     * @param   {Object}   merges 合并单元格的起始坐标
     * @return  {Object}          补全后的单元格数据
     */
    function mergeColumn(obj, merges) {
        //判断是否只为一列
        var _keys = [];
        for(var i in obj) {
            _keys.push(i);
        }
        if(_keys.length === 1) {
            return obj;
        }

        //验证是否有合并
        if(merges.length === 0) {
            console.log('merges is empty');
            return obj;
        }

        //将数据处理成全项目的hash
        for(var i = 0; i < merges.length; i++) { //纵向合并
            if(merges[i].e.c == merges[i].s.c) { 
                var start = merges[i].s.r + 1,
                        end = merges[i].e.r + 1,
                        sub = merges[i].e.c, //起点x坐标
                        range = end - start,
                        origin = obj['_' + sub][start];
                
                //起始点数据
                obj['_' + sub][start] = {
                    _v: origin,
                    _w: 'row',
                    _s: true,
                    _c: (range + 1)
                }

                //补全被合并项
                for(var j = 1; j <= range; j++) {
                    start ++;
                    obj['_' + sub][start] = {
                        _v: origin,
                        _w: 'row',
                      _s: false,
                        _c: (range + 1)
                    }
                }

            } else { //横向的合并
                var start = merges[i].s.c,
                        end = merges[i].e.c,
                        sub = merges[i].e.r + 1, //起点y坐标
                        range = end - start,
                        origin = obj['_' + start][sub];

                //起始点数据
                obj['_' + start][sub] = {
                    _v: origin,
                    _w: 'col',
                    _s: true,
                    _c: (range + 1)
                }

                //补全被合并项
                for(var j = 1; j <= range; j++) {
                    start ++;
                    obj['_' + start][sub] = {
                        _v: origin,
                        _w: 'col',
                        _s: false,
                        _c: (range + 1)
                    }
                }
            }
        }

        return obj;
    }

    /**
     * [toArray description]
     * @author mao
     * @version 1
     * @date    2016-10-12
     * @param   {Object}   obj 待处理的hash
     * @return  {array}       处理成的数组
     */
    function toArray(obj) {
        var keys = [],
                data = obj._1,
                res = [];
        //获取key值
        for(var i in obj) {
            keys.push(i);
        }

        //处理成数组
        for(var i in data) {
            var current = {};
            for(var j = 0; j < keys.length; j++) {
                current[keys[j]] = obj[keys[j]][i];
            }
            res.push(current);
        }
        return res;
    }


    /**
     * [createXHR]
     * 创建一个xhr
     * @author mao
     * @version 1
     * @date    2016-09-26
     * @return  {object}   xhr
     */
    function createXHR() {
        if(window.XMLHttpRequest) {
        return new XMLHttpRequest();
        } else if(window.ActiveXObject) {  //ie6
            return new ActiveXObject('MSXML2.XMLHTTP.3.0');
        } else {
            throw 'XHR unavailable for your browser';
        }
    }


    /**
     * [transferData]
     * 请求excel文件请求
     * @author mao
     * @version 1
     * @date    2016-09-28
     * @param   {Function} option.dataRender 回调函数,处理结果数据
     * @param   {string}   option.url      xlsx文件请求地址
     */
    mod.transferData = function(option) {
        //新建xhr
        var oReq = createXHR(),
                resultData;
        //建立连接
        oReq.open("GET", option.url, true);

        //判断是否为低版本的ie,处理返回
        if(typeof Uint8Array !== 'undefined') {
            oReq.responseType = "arraybuffer";
            oReq.onload = function(e) {
                if(typeof console !== 'undefined') console.log("onload", new Date());
                var arraybuffer = oReq.response;
                var data = new Uint8Array(arraybuffer);
                var arr = new Array();
                for(var i = 0; i != data.length; ++i) {
                    arr[i] = String.fromCharCode(data[i]);
                }
                //处理数据
                var wb = XLSX.read(arr.join(""), {type:"binary"});

                //数据放入回调
                option.dataRender(toArray(checkColumn(wb)));
            };
        } else {
            oReq.setRequestHeader("Accept-Charset", "x-user-defined");  
            oReq.onreadystatechange = function() { 
                if(oReq.readyState == 4 && oReq.status == 200) {
                    var ff = convertResponseBodyToText(oReq.responseBody);
                    if(typeof console !== 'undefined') {
                        console.log("onload", new Date());
                    }

                    //处理数据
                    var wb = XLSX.read(ff, {type:"binary"});

                    //数据放入回调
                    option.dataRender(toArray(checkColumn(wb)));
                }
            };
        }

        //发送请求
        oReq.send();
    }

    /**
     * [check_undefind description]
     * @author mao
     * @version 1
     * @date    2016-10-13
     * @param   {string}   data 数据
     * @return  {string}        返回空或数据本身
     */
    function check_undefind(data) {
        if(!data) {
            return '';
        } else {
            if(typeof data != 'number') {
                //检测特殊字符
                if(data.indexOf('&#10;') != -1) {
                    return data.split('&#10;').join('<br/>');
                } else {
                    return data;
                }
            } else {
                return data;
            }
        }
    }

    /**
     * [renderHTML description]
     * @author mao
     * @version 1
     * @date    2016-10-13
     * @param   {object}   table 最终数据
     * @return  {string}         渲染的dom
     */
    mod.renderHTML = function(table) {
        var html = '';
        for(var i = 0; i <table.length; i++) {
            html += '<tr>';
            for(var j in table[i]) {
                var item = table[i][j];
                if(typeof item === 'object') {
                    switch(item._w) {
                        case 'col': {
                            if(item._s) {
                                html += '<td colspan="'+item._c+'">'+check_undefind(item._v)+'</td>';
                            }
                            break;
                        }
                        case 'row': {
                            if(item._s) {
                                html += '<td rowspan="'+item._c+'">'+check_undefind(item._v)+'</td>';
                            }
                            break;
                        }
                        default:break;
                    }
                } else {
                    html += '<td>'+check_undefind(item)+'</td>';
                }
            }
            html += '</tr>';
        }
        return html;
    }

    return mod;

})(excel || {})

处理出来的数据如图,
819452-20161128232713224-1355206658.png

这样的结果数据可以很友好地装载入UI结构当中,这样最后议程修改就只需要产品修改excel数据。很大程度上节省了流程以及人力成本。

兼容低版本浏览器可引入以下文件
819452-20161128233404412-96867468.png

转载于:https://www.cnblogs.com/marvinemao/p/6111860.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值