[js]表格转换为树形结构

/**
 * 表格工具
 * getCellPosition-获取单元格位置
 * getParentCells-获取单元格的所有上级单元格
 * getTree-返回表格的树形结构数据
 * 例子:
 * $('talbe#tab1').table2tree({});
 * var tree = $('talbe#tab1').table2tree('getTree');
 */
(function($){
	function init(target){
		var cells = [];
		var jq = $(target);
		var options = jq.table2tree('options');
		var trselector = 'tr';
		if(options.container){
			trselector = options.container + ' ' + trselector;
		}
		jq.find(trselector).each(function(row, domtr){
			var tr = $(this);
			cells[row] = [];
			var totalCol = 0;
			var mRowCells = [];
			if(row > 0){
				mRowCells = getMultiRowCells();
			}
			tr.find('th,td').each(initCell);
			
			function initCell(col, domtd){
				var colspan = domtd.colSpan;
				var rowspan = domtd.rowSpan;
				var colb = totalCol;
				if(row > 0){
					colb = getCellPosLeft();
				}	
				var cole = colb + colspan;
				var rowb = row;
				var rowe = row + rowspan;		
						
				cells[row][col] = {text:domtd.innerText, colspan:colspan, rowspan:rowspan, pos:{left:colb, right:cole, top:row, bottom:rowe}};
				totalCol = cole;			
				
				function getCellPosLeft(){
					var posLeft = 0;
					if(col > 0){
						posLeft = cells[row][col-1].pos.right;
					}
					for(var i = 0; i < mRowCells.length; i++){
						//若该位置被占用,继续叠加
						if(posLeft >= mRowCells[i].left && posLeft < mRowCells[i].right){
							posLeft += mRowCells[i].colspan;
						}
					}
					return posLeft;
				}			
			}
			
			function getMultiRowCells(){
				var mrcells = [];
				for(var i = 0; i < row; i++){
					for(var j = 0; j < cells[i].length; j++){
						var cell = cells[i][j];
						if(cell.rowspan > (row - i)){
							mrcells.push({colspan:cell.colspan,left:cell.pos.left,right:cell.pos.right});
						}
					}
				}
				return mrcells;
			}
		});
		if(cells.length){
			jq.data('table2tree', $.extend(jq.data('table2tree'),{cells:cells,tree:buildTree(jq,cells)}));
		}
	}
	
	function buildTree(jq, cells){
		var options = jq.data('table2tree').options;
		var tree = {id:options.rootId, text:options.rootName, childNodes:[]};
		for(var i = 0; i < cells[0].length; i++){
			var node = {text:cells[0][i].text};
			var childNodes = getChildren(0, i, cells);
			if(childNodes && childNodes.length){
				node.childNodes = childNodes;
			}
			tree.childNodes.push(node);
		}
		return tree;
	}
	
	function getChildren(row, col, cells){
		var thiscell = cells[row][col];
		if(thiscell.pos.bottom == cells.length){
			return;
		}
		var thispos = cells[row][col].pos;
		var childrow = row + thiscell.rowspan;
		var nodes = [];
		for(var j = 0; j < cells[childrow].length; j++){
			var pos = cells[childrow][j].pos;
			if(thispos.left <= pos.left && thispos.right >= pos.right){
				var node = {text:cells[childrow][j].text};
				var childNodes = getChildren(childrow,j,cells);
				if(childNodes && childNodes.length){
					node.childNodes = childNodes;
				}
				nodes.push(node);
			}
		}
		return nodes;
	}
	
	//获取单元格位置
	function getCellPosition(td){
		if(!td){
			return;
		}
		var nodeName = td.nodeName;
		if(nodeName != 'TD' && nodeName != 'TH'){
			return;
		}
		var jqtd = $(td);
		var colIndex = jqtd.prevAll().length;
		var rowIndex = $('table tr').has(jqtd).prevAll().length;
		return {row:rowIndex, col:colIndex};
	}
	
	//获取所有上级单元格的位置
	function getParentCells(td, cells){
		var cellpos = getCellPosition(td);
		if(!cellpos || cellpos.row == 0){
			return;
		}
		var thispos = cells[cellpos.row][cellpos.col].pos;
		var upcells = [];
		for(var i = cellpos.row - 1; i >= 0; i--){
			for(var j = 0; j < cells[i].length; j++){
				var pos = cells[i][j].pos;
				if(thispos.left >= pos.left && thispos.right <= pos.right){
					upcells.push({row:i, col:j});
					thispos = pos;
					break;
				}
			}
		}
		return upcells;
	}
	
	function isLoaded(jq){
		var state = jq.data('table2tree');
		if(state && state.cells){
			return true;
		}else{
			return false;
		}
	}
	
	$.fn.table2tree = function(options, param){
		if (typeof options == 'string'){
			var method = $.fn.table2tree.methods[options];
			if (method){
				return method(this, param);
			}
		}
		
		options = options || {};
		return this.each(function(){
			var state = $.data(this, 'table2tree');
			if (state){
				$.extend(state.options, options);
			} else {
				$.data(this, 'table2tree', {
					options: $.extend({}, $.fn.table2tree.defaults, options)
				});
			}
			init(this);
		});
	};
	
	$.fn.table2tree.methods = {
		options: function(jq){
			return $.data(jq[0], 'table2tree').options;
		},getCellPosition: function(jq, target){
			return getCellPosition(target);
		},getParentCells: function(jq, target){
			if(isLoaded(jq)){
				return getParentCells(target, jq.data('table2tree').cells);
			}
		},getTree: function(jq){
			if(isLoaded(jq)){
				return jq.data('table2tree').tree;
			}
		},isLoaded: isLoaded
	};
	
	$.fn.table2tree.defaults = {
		author:'attwice@qq.com',
		date:'2013/12/11',	
		container: '',		//顶层容器:'',thead,tbody
		rootId:'-1',		//树根节点ID
		rootName:'树形表格'	//树更节点名称
	};
})(jQuery);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 很好的问题!可以使用递归函数来实现将菜单表转换树形结构。以下是一个示例代码: ``` function buildTree(menuList, parentId) { var tree = []; for (var i = 0; i < menuList.length; i++) { if (menuList[i].parent_id === parentId) { var node = { menu_id: menuList[i].menu_id, children: buildTree(menuList, menuList[i].menu_id) }; tree.push(node); } } return tree; } // 在后端拦截器中调用 buildTree 函数,将菜单表转换树形结构 var menuList = // 从数据库中获取菜单表数据 var tree = buildTree(menuList, null); // 将树形结构传递到前端,使用前端框架(如 Vue.js)渲染为树形表格 ``` 这个函数会遍历整个菜单表,将每个节点的子节点作为其 children 属性,最终返回一个树形结构。这个函数使用了递归,因此不需要使用 for 循环。 ### 回答2: 通过SQL查询获取到的菜单表数据中,每条菜单数据都包含了菜单ID和父级菜单ID(menu_id和parent_id),现在要求使用后端拦截器将这些数据转换树形结构,然后传递到前端,最终展示成树形表格,但不允许使用for-in循环。 实现这个需求的一种方式是使用递归函数。递归函数是一种在函数体内调用自身的函数。 首先,定义一个递归函数,传入两个参数:菜单列表和父级菜单ID。函数的目标是找出指定父级菜单ID下的所有子菜单,并将其放入一个新的列表中。 在函数内部,遍历菜单列表。对于每个菜单,判断其父级菜单ID是否等于传入的父级菜单ID。如果相等,说明该菜单是指定父级菜单ID下的子菜单。 在找到子菜单之后,将该子菜单的ID作为新的父级菜单ID,再次调用递归函数,将新的父级菜单ID和菜单列表作为参数传入。递归调用将会继续寻找新父级菜单ID下的子菜单。 递归调用结束的条件是:当前父级菜单ID下没有子菜单,即找不到任何子菜单。 最后,将递归函数返回的子菜单列表作为菜单树结构返回给前端,然后前端根据这个菜单树结构展示成树形表格。 使用递归函数实现树形结构转换,可以避免使用for-in循环。 ### 回答3: 要把菜单表的数据通过后端拦截器变成树形结构,然后传到前端以树形表格展示,并且不使用for循环来实现。 首先,我们可以通过SQL查询语句将菜单表的所有数据按照菜单ID升序进行排序,以保证父级菜单在子级菜单之前被查询出来。 接着,我们可以使用一个map数据结构,将菜单ID作为key,对应的菜单信息作为value,来实现一个菜单的索引。 在查询结果中,我们遍历每一条记录,将其作为子菜单加入其对应的父菜单中。这里不需要使用for循环,可以通过递归的方式来构建菜单树。 具体做法是,对于每一个查询到的菜单,我们首先查找它的父菜单。如果父菜单存在于map中,则将其添加到父菜单的子菜单列表中;否则,将其添加为一个独立的根菜单。 重复上述步骤,直到遍历完所有的菜单记录,我们就得到了一个完整的菜单树。 最后,通过前端将菜单树以树形表格的形式展示出来。 这样,我们就成功地将菜单表的数据转换树形结构,并且通过后端拦截器传到前端实现了树形表格展示,同时不使用for循环。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值