zoukankan      html  css  js  c++  java
  • 纯前端处理excel数据

    纯前端处理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活动议程可能是这样

    也可能是这样的表格

    所以不能固化到将数据仅仅整理成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 || {})
    
    
    

    处理出来的数据如图,

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

    兼容低版本浏览器可引入以下文件

  • 相关阅读:
    SQLite Java Wrapper/JDBC Driver(收集)
    JAVA 并行运行(收集)
    log4net使用方法(转载)
    WMI服务故障,VBS脚本无法运行错误
    ArcEngine中UID使用资料收集
    使用 ArcGIS Engine Runtime 制作安装包(转载)
    Eclipse安装WindowBuilder Pro(转载)
    C#操作SQL Server数据库
    自动化测试 (三) Web自动化测试原理
    HTTP协议 (六) 状态码详解
  • 原文地址:https://www.cnblogs.com/marvinemao/p/6111860.html
Copyright © 2011-2022 走看看