bootstrap-treetable.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. /**
  2. * bootstrapTreeTable
  3. *
  4. * @author swifly
  5. */
  6. (function($) {
  7. "use strict";
  8. $.fn.bootstrapTreeTable = function(options, param) {
  9. var allData = null;//用于存放格式化后的数据
  10. // 如果是调用方法
  11. if (typeof options == 'string') {
  12. return $.fn.bootstrapTreeTable.methods[options](this, param);
  13. }
  14. // 如果是初始化组件
  15. options = $.extend({}, $.fn.bootstrapTreeTable.defaults, options || {});
  16. // 是否有radio或checkbox
  17. var hasSelectItem = false;
  18. var target = $(this);
  19. // 在外层包装一下div,样式用的bootstrap-table的
  20. var _main_div = $("<div class='bootstrap-tree-table fixed-table-container'></div>");
  21. target.before(_main_div);
  22. _main_div.append(target);
  23. target.addClass("table table-hover treetable-table table-bordered");
  24. if (options.striped) {
  25. target.addClass('table-striped');
  26. }
  27. // 工具条在外层包装一下div,样式用的bootstrap-table的
  28. if(options.toolbar){
  29. var _tool_div = $("<div class='fixed-table-toolbar'></div>");
  30. var _tool_left_div = $("<div class='bs-bars pull-left'></div>");
  31. _tool_left_div.append($(options.toolbar));
  32. _tool_div.append(_tool_left_div);
  33. _main_div.before(_tool_div);
  34. }
  35. // 格式化数据,优化性能
  36. target.formatData=function(data){
  37. var _root = options.rootCodeValue?options.rootCodeValue:null
  38. $.each(data, function(index, item) {
  39. // 添加一个默认属性,用来判断当前节点有没有被显示
  40. item.isShow = false;
  41. // 这里兼容几种常见Root节点写法
  42. // 默认的几种判断
  43. var _defaultRootFlag = item[options.parentCode] == '0'
  44. || item[options.parentCode] == 0
  45. || item[options.parentCode] == null
  46. || item[options.parentCode] == '';
  47. if (!item[options.parentCode] || (_root?(item[options.parentCode] == options.rootCodeValue):_defaultRootFlag)){
  48. if(!allData["_root_"]){allData["_root_"]=[];}
  49. allData["_root_"].push(item);
  50. }else{
  51. if(!allData["_n_"+item[options.parentCode]]){allData["_n_"+item[options.parentCode]]=[];}
  52. allData["_n_"+item[options.parentCode]].push(item);
  53. }
  54. });
  55. data=null;//回收
  56. }
  57. // 得到根节点
  58. target.getRootNodes = function() {
  59. return allData["_root_"];
  60. };
  61. // 递归获取子节点并且设置子节点
  62. target.handleNode = function(parentNode, lv, row_id, p_id, tbody) {
  63. var _ls = allData["_n_"+parentNode[options.code]];
  64. var tr = target.renderRow(parentNode,_ls?true:false,lv,row_id,p_id);
  65. tbody.append(tr);
  66. if(_ls){
  67. $.each(_ls, function(i, item) {
  68. var _row_id = row_id+"_"+i
  69. target.handleNode(item, (lv + 1), _row_id,row_id, tbody)
  70. });
  71. }
  72. };
  73. // 绘制行
  74. target.renderRow = function(item,isP,lv,row_id,p_id){
  75. // 标记已显示
  76. item.isShow = true;
  77. var tr = $('<tr id="'+row_id+'" pid="'+p_id+'"></tr>');
  78. var _icon = options.expanderCollapsedClass;
  79. if(options.expandAll){
  80. tr.css("display","table");
  81. _icon = options.expanderExpandedClass;
  82. }else if(options.expandFirst&&lv<=2){
  83. tr.css("display","table");
  84. _icon=(lv==1)?options.expanderExpandedClass:options.expanderCollapsedClass;
  85. }else{
  86. tr.css("display","none");
  87. _icon = options.expanderCollapsedClass;
  88. }
  89. $.each(options.columns, function(index, column) {
  90. // 判断有没有选择列
  91. if(column.field=='selectItem'){
  92. hasSelectItem = true;
  93. var td = $('<td style="text-align:center;width:36px"></td>');
  94. if(column.radio){
  95. var _ipt = $('<input name="select_item" type="radio" value="'+item[options.id]+'"></input>');
  96. td.append(_ipt);
  97. }
  98. if(column.checkbox){
  99. var _ipt = $('<input name="select_item" type="checkbox" value="'+item[options.id]+'"></input>');
  100. td.append(_ipt);
  101. }
  102. tr.append(td);
  103. }else{
  104. var td = $('<td title="'+item[column.field]+'" name="'+column.field+'" style="'+((column.width)?('width:'+column.width):'')+'"></td>');
  105. // 增加formatter渲染
  106. if (column.formatter) {
  107. td.html(column.formatter.call(this, item[column.field], item, index));
  108. } else {
  109. td.text(item[column.field]);
  110. }
  111. if(options.expandColumn==index){
  112. if(!isP){
  113. td.prepend('<span class="treetable-expander"></span>')
  114. }else{
  115. td.prepend('<span class="treetable-expander '+_icon+'"></span>')
  116. }
  117. for (var int = 0; int < (lv-1); int++) {
  118. td.prepend('<span class="treetable-indent"></span>')
  119. }
  120. }
  121. tr.append(td);
  122. }
  123. });
  124. return tr;
  125. }
  126. // 加载数据
  127. target.load = function(parms){
  128. // 加载数据前先清空
  129. allData = {};
  130. // 加载数据前先清空
  131. target.html("");
  132. // 构造表头
  133. var thr = $('<tr></tr>');
  134. $.each(options.columns, function(i, item) {
  135. var th = null;
  136. // 判断有没有选择列
  137. if(i==0&&item.field=='selectItem'){
  138. hasSelectItem = true;
  139. th = $('<th style="width:36px"></th>');
  140. }else{
  141. th = $('<th style="'+((item.width)?('width:'+item.width):'')+'"></th>');
  142. }
  143. th.text(item.title);
  144. thr.append(th);
  145. });
  146. var thead = $('<thead class="treetable-thead"></thead>');
  147. thead.append(thr);
  148. target.append(thead);
  149. // 构造表体
  150. var tbody = $('<tbody class="treetable-tbody"></tbody>');
  151. target.append(tbody);
  152. // 添加加载loading
  153. var _loading = '<tr><td colspan="'+options.columns.length+'"><div style="display: block;text-align: center;">正在努力地加载数据中,请稍候……</div></td></tr>'
  154. tbody.html(_loading);
  155. // 默认高度
  156. if(options.height){
  157. tbody.css("height",options.height);
  158. }
  159. $.ajax({
  160. type : options.type,
  161. url : options.url,
  162. data : parms?parms:options.ajaxParams,
  163. dataType : "JSON",
  164. success : function(data, textStatus, jqXHR) {
  165. // 加载完数据先清空
  166. tbody.html("");
  167. if(!data||data.length<=0){
  168. var _empty = '<tr><td colspan="'+options.columns.length+'"><div style="display: block;text-align: center;">没有找到匹配的记录</div></td></tr>'
  169. tbody.html(_empty);
  170. return;
  171. }
  172. // 格式化数据
  173. target.formatData(data);
  174. // 开始绘制
  175. var rootNode = target.getRootNodes();
  176. if(rootNode){
  177. $.each(rootNode, function(i, item) {
  178. var _row_id = "row_id_"+i
  179. target.handleNode(item, 1, _row_id,"row_root", tbody);
  180. });
  181. }
  182. // 下边的操作主要是为了查询时让一些没有根节点的节点显示
  183. $.each(data, function(i, item) {
  184. if(!item.isShow){
  185. var tr = target.renderRow(item,false,1);
  186. tbody.append(tr);
  187. }
  188. });
  189. target.append(tbody);
  190. //动态设置表头宽度
  191. thead.css("width", tbody.children(":first").css("width"));
  192. // 行点击选中事件
  193. target.find("tbody").find("tr").click(function(){
  194. if(hasSelectItem){
  195. var _ipt = $(this).find("input[name='select_item']");
  196. if(_ipt.attr("type")=="radio"){
  197. _ipt.prop('checked',true);
  198. target.find("tbody").find("tr").removeClass("treetable-selected");
  199. $(this).addClass("treetable-selected");
  200. }else{
  201. if(_ipt.prop('checked')){
  202. _ipt.prop('checked',false);
  203. $(this).removeClass("treetable-selected");
  204. }else{
  205. _ipt.prop('checked',true);
  206. $(this).addClass("treetable-selected");
  207. }
  208. }
  209. }
  210. });
  211. // 小图标点击事件--展开缩起
  212. target.find("tbody").find("tr").find(".treetable-expander").click(function(){
  213. var _flag = $(this).hasClass(options.expanderExpandedClass);
  214. var tr = $(this).parent().parent();
  215. var row_id = tr.attr("id");
  216. if(_flag){
  217. var _ls = target.find("tbody").find("tr[id^='"+row_id+"_']");//下所有
  218. if(_ls&&_ls.length>0){
  219. $.each(_ls, function(index, item) {
  220. $(item).css("display","none");
  221. var _icon = $(item).children().eq(options.expandColumn).find(".treetable-expander");
  222. if(_icon.hasClass(options.expanderExpandedClass)){
  223. _icon.removeClass(options.expanderExpandedClass)
  224. _icon.addClass(options.expanderCollapsedClass)
  225. }
  226. });
  227. }
  228. $(this).removeClass(options.expanderExpandedClass)
  229. $(this).addClass(options.expanderCollapsedClass)
  230. }else{
  231. var _ls = target.find("tbody").find("tr[pid='"+row_id+"']");//下一级
  232. if(_ls&&_ls.length>0){
  233. $.each(_ls, function(index, item) {
  234. $(item).css("display","table");
  235. });
  236. }
  237. $(this).removeClass(options.expanderCollapsedClass)
  238. $(this).addClass(options.expanderExpandedClass)
  239. }
  240. });
  241. },
  242. error:function(xhr,textStatus){
  243. var _errorMsg = '<tr><td colspan="'+options.columns.length+'"><div style="display: block;text-align: center;">'+xhr.responseText+'</div></td></tr>'
  244. tbody.html(_errorMsg);
  245. debugger;
  246. },
  247. });
  248. }
  249. if (options.url) {
  250. target.load();
  251. } else {
  252. // 也可以通过defaults里面的data属性通过传递一个数据集合进来对组件进行初始化....有兴趣可以自己实现,思路和上述类似
  253. }
  254. return target;
  255. };
  256. // 组件方法封装........
  257. $.fn.bootstrapTreeTable.methods = {
  258. // 返回选中记录的id(返回的id由配置中的id属性指定)
  259. // 为了兼容bootstrap-table的写法,统一返回数组,这里只返回了指定的id
  260. getSelections : function(target, data) {
  261. // 所有被选中的记录input
  262. var _ipt = target.find("tbody").find("tr").find("input[name='select_item']:checked");
  263. var chk_value =[];
  264. // 如果是radio
  265. if(_ipt.attr("type")=="radio"){
  266. var _data = {id:_ipt.val()};
  267. var _tds = _ipt.parent().parent().find("td");
  268. _tds.each(function(_i,_item){
  269. if(_i!=0){
  270. _data[$(_item).attr("name")]=$(_item).attr("title");
  271. }
  272. });
  273. chk_value.push(_data);
  274. }else{
  275. _ipt.each(function(_i,_item){
  276. var _data = {id:$(_item).val()};
  277. var _tds = $(_item).parent().parent().find("td");
  278. _tds.each(function(_ii,_iitem){
  279. if(_ii!=0){
  280. _data[$(_iitem).attr("name")]=$(_iitem).attr("title");
  281. }
  282. });
  283. chk_value.push(_data);
  284. });
  285. }
  286. return chk_value;
  287. },
  288. // 刷新记录
  289. refresh : function(target, parms) {
  290. if(parms){
  291. target.load(parms);
  292. }else{
  293. target.load();
  294. }
  295. },
  296. // 组件的其他方法也可以进行类似封装........
  297. };
  298. $.fn.bootstrapTreeTable.defaults = {
  299. id : 'id',// 选取记录返回的值
  300. code : 'id',// 用于设置父子关系
  301. parentCode : 'parentId',// 用于设置父子关系
  302. rootCodeValue: null,//设置根节点code值----可指定根节点,默认为null,"",0,"0"
  303. data : [], // 构造table的数据集合
  304. type : "GET", // 请求数据的ajax类型
  305. url : null, // 请求数据的ajax的url
  306. ajaxParams : {}, // 请求数据的ajax的data属性
  307. expandColumn : 1,// 在哪一列上面显示展开按钮
  308. expandAll : false, // 是否全部展开
  309. expandFirst : true, // 是否默认第一级展开--expandAll为false时生效
  310. striped : false, // 是否各行渐变色
  311. columns : [],
  312. toolbar: null,//顶部工具条
  313. height: 0,
  314. expanderExpandedClass : 'glyphicon glyphicon-chevron-down',// 展开的按钮的图标
  315. expanderCollapsedClass : 'glyphicon glyphicon-chevron-right'// 缩起的按钮的图标
  316. };
  317. })(jQuery);