欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JS 组件系列之BootstrapTable的treegrid功能

程序员文章站 2022-04-10 10:14:02
上篇给大家介绍了js 组件系列之 bootstrap treegrid 组件封装过程,下面重点给大家介绍js 组件系列之bootstraptable的treegrid功能,...

上篇给大家介绍了js 组件系列之 bootstrap treegrid 组件封装过程,下面重点给大家介绍js 组件系列之bootstraptable的treegrid功能,需要的的朋友一起学习吧!

一、效果预览

全部折叠

JS 组件系列之BootstrapTable的treegrid功能

展开一级

JS 组件系列之BootstrapTable的treegrid功能

全部展开

JS 组件系列之BootstrapTable的treegrid功能

二、代码示例

怎么样?效果还行吧。给出js的源码供大家参考。

(function ($) {
 'use strict';
 var sprintf = function (str) {
 var args = arguments,
  flag = true,
  i = 1;
 str = str.replace(/%s/g, function () {
  var arg = args[i++];
  if (typeof arg === 'undefined') {
  flag = false;
  return '';
  }
  return arg;
 });
 return flag ? str : '';
 };
 var getfieldindex = function (columns, field) {
 var index = -1;
 $.each(columns, function (i, column) {
  if (column.field === field) {
  index = i;
  return false;
  }
  return true;
 });
 return index;
 };
 var calculateobjectvalue = function (self, name, args, defaultvalue) {
 var func = name;
 if (typeof name === 'string') {
  var names = name.split('.');
  if (names.length > 1) {
  func = window;
  $.each(names, function (i, f) {
   func = func[f];
  });
  } else {
  func = window[name];
  }
 }
 if (typeof func === 'object') {
  return func;
 }
 if (typeof func === 'function') {
  return func.apply(self, args);
 }
 if (!func && typeof name === 'string' && sprintf.apply(this, [name].concat(args))) {
  return sprintf.apply(this, [name].concat(args));
 }
 return defaultvalue;
 };
 var getitemfield = function (item, field) {
 var value = item;
 if (typeof field !== 'string' || item.hasownproperty(field)) {
  return item[field];
 }
 var props = field.split('.');
 for (var p in props) {
  value = value[props[p]];
 }
 return value;
 };
 var getparent = function (node, source, field) {
 var data = [];
 var items = $.grep(source, function (item, index) {
  return node.parentid == item[field];
 });
 $.each(items, function (index, item) {
  data.splice(0, 0, item);
  var child = getparent(item, source, field);
  $.each(child, function (i, n) {
  data.splice(0, 0, n);
  });
 });
 return data;
 };
 var getchild = function (node, source, field) {
 var data = [];
 var items = $.grep(source, function (item, index) {
  return item.parentid == node[field];
 });
 $.each(items, function (index, item) {
  data.push(item);
  var child = getchild(item, source, field);
  $.each(child, function (i, n) {
  data.push(n);
  });
 });
 return data;
 };
 //调用bootstraptable组件的构造器得到对象
 var bootstraptable = $.fn.bootstraptable.constructor,
 _initdata = bootstraptable.prototype.initdata,
 _initpagination = bootstraptable.prototype.initpagination,
 _initbody = bootstraptable.prototype.initbody;
 //重写bootstraptable的initdata方法
 bootstraptable.prototype.initdata = function () {
 _initdata.apply(this, array.prototype.slice.apply(arguments));
 var that = this;
 if (that.options.treeview && this.data.length > 0) {
  var rows = [];
  var roots = $.grep(this.data, function (row, index) {
  return row.level == that.options.treerootlevel;
  });
  $.each(roots, function (index, item) {
  rows.push(item);
  var child = getchild(item, that.data, that.options.treeid);
  $.each(child, function (i, n) {
   if (that.options.treecollapseall) {
   n.hidden = true;
   }
   rows.push(n);
  });
  });
  that.options.data = that.data = rows;
 }
 };
 //重写bootstraptable的initpagination方法
 bootstraptable.prototype.initpagination = function () {
 //理论情况下,treegrid是不支持分页的,所以默认分页参数为false
 this.options.pagination = false;
  //调用“父类”的“虚方法”
 _initpagination.apply(this, array.prototype.slice.apply(arguments));
 };
 //重写bootstraptable的initbody方法
 bootstraptable.prototype.initbody = function (fixedscroll) {
 var that = this,
  html = [],
  data = this.getdata();
 this.trigger('pre-body', data);
 this.$body = this.$el.find('tbody');
 if (!this.$body.length) {
  this.$body = $('<tbody></tbody>').appendto(this.$el);
 }
 if (!this.options.pagination || this.options.sidepagination === 'server') {
  this.pagefrom = 1;
  this.pageto = data.length;
 }
 for (var i = this.pagefrom - 1; i < this.pageto; i++) {
  var key,
  item = data[i],
  style = {},
  csses = [],
  data_ = '',
  attributes = {},
  htmlattributes = [];
  if (item.hidden) continue;
  style = calculateobjectvalue(this.options, this.options.rowstyle, [item, i], style);
  if (style && style.css) {
  for (key in style.css) {
   csses.push(key + ': ' + style.css[key]);
  }
  }
  attributes = calculateobjectvalue(this.options,
  this.options.rowattributes, [item, i], attributes);
  if (attributes) {
  for (key in attributes) {
   htmlattributes.push(sprintf('%s="%s"', key, escapehtml(attributes[key])));
  }
  }
  if (item._data && !$.isemptyobject(item._data)) {
  $.each(item._data, function (k, v) {
   if (k === 'index') {
   return;
   }
   data_ += sprintf(' data-%s="%s"', k, v);
  });
  }
  html.push('<tr',
  sprintf(' %s', htmlattributes.join(' ')),
  sprintf(' id="%s"', $.isarray(item) ? undefined : item._id),
  sprintf(' class="%s"', style.classes || ($.isarray(item) ? undefined : item._class)),
  sprintf(' data-index="%s"', i),
  sprintf(' data-uniqueid="%s"', item[this.options.uniqueid]),
  sprintf('%s', data_),
  '>'
  );
  if (this.options.cardview) {
  html.push(sprintf('<td colspan="%s">', this.header.fields.length));
  }
  if (!this.options.cardview && this.options.detailview) {
  html.push('<td>',
   '<a class="detail-icon" href="javascript:" rel="external nofollow" >',
   sprintf('<i class="%s %s"></i>', this.options.iconsprefix, this.options.icons.detailopen),
   '</a>',
   '</td>');
  }
  $.each(this.header.fields, function (j, field) {
  var text = '',
   value = getitemfield(item, field),
   type = '',
   cellstyle = {},
   id_ = '',
   class_ = that.header.classes[j],
   data_ = '',
   rowspan_ = '',
   title_ = '',
   column = that.columns[getfieldindex(that.columns, field)];
  if (!column.visible) {
   return;
  }
  style = sprintf('style="%s"', csses.concat(that.header.styles[j]).join('; '));
  value = calculateobjectvalue(column,
   that.header.formatters[j], [value, item, i], value);
  if (item['_' + field + '_id']) {
   id_ = sprintf(' id="%s"', item['_' + field + '_id']);
  }
  if (item['_' + field + '_class']) {
   class_ = sprintf(' class="%s"', item['_' + field + '_class']);
  }
  if (item['_' + field + '_rowspan']) {
   rowspan_ = sprintf(' rowspan="%s"', item['_' + field + '_rowspan']);
  }
  if (item['_' + field + '_title']) {
   title_ = sprintf(' title="%s"', item['_' + field + '_title']);
  }
  cellstyle = calculateobjectvalue(that.header,
   that.header.cellstyles[j], [value, item, i], cellstyle);
  if (cellstyle.classes) {
   class_ = sprintf(' class="%s"', cellstyle.classes);
  }
  if (cellstyle.css) {
   var csses_ = [];
   for (var key in cellstyle.css) {
   csses_.push(key + ': ' + cellstyle.css[key]);
   }
   style = sprintf('style="%s"', csses_.concat(that.header.styles[j]).join('; '));
  }
  if (item['_' + field + '_data'] && !$.isemptyobject(item['_' + field + '_data'])) {
   $.each(item['_' + field + '_data'], function (k, v) {
   if (k === 'index') {
    return;
   }
   data_ += sprintf(' data-%s="%s"', k, v);
   });
  }
  if (column.checkbox || column.radio) {
   type = column.checkbox ? 'checkbox' : type;
   type = column.radio ? 'radio' : type;
   text = [that.options.cardview ?
   '<div class="card-view">' : '<td class="bs-checkbox">',
   '<input' +
   sprintf(' data-index="%s"', i) +
   sprintf(' name="%s"', that.options.selectitemname) +
   sprintf(' type="%s"', type) +
   sprintf(' value="%s"', item[that.options.idfield]) +
   sprintf(' checked="%s"', value === true ||
   (value && value.checked) ? 'checked' : undefined) +
   sprintf(' disabled="%s"', !column.checkboxenabled ||
   (value && value.disabled) ? 'disabled' : undefined) +
   ' />',
   that.header.formatters[j] && typeof value === 'string' ? value : '',
   that.options.cardview ? '</div>' : '</td>'
   ].join('');
   item[that.header.statefield] = value === true || (value && value.checked);
  } else {
   value = typeof value === 'undefined' || value === null ?
   that.options.undefinedtext : value;
   var indent, icon;
   if (that.options.treeview && column.field == that.options.treefield) {
   var indent = item.level == that.options.level ? '' : sprintf('<span style="margin-left: %spx;"></span>', (item.level - that.options.treerootlevel) * 15);
   var child = $.grep(data, function (d, i) {
    return d.parentid == item[that.options.treeid] && !d.hidden;
   });
   icon = sprintf('<span class="tree-icon %s" style="cursor: pointer; margin: 0px 5px;"></span>', child.length > 0 ? that.options.expandicon : that.options.collapseicon);
   //icon = sprintf('<span class="tree-icon %s" style="cursor: pointer; margin: 0px 5px;"></span>', child.length > 0 ? that.options.expandicon : "");
   }
   text = that.options.cardview ? ['<div class="card-view">',
   that.options.showheader ? sprintf('<span class="title" %s>%s</span>', style,
    getpropertyfromother(that.columns, 'field', 'title', field)) : '',
   sprintf('<span class="value">%s</span>', value),
   '</div>'
   ].join('') : [sprintf('<td%s %s %s %s %s %s>', id_, class_, style, data_, rowspan_, title_),
   indent,
   icon,
   value,
   '</td>'
   ].join('');
   if (that.options.cardview && that.options.smartdisplay && value === '') {
   text = '';
   }
  }
  html.push(text);
  });
  if (this.options.cardview) {
  html.push('</td>');
  }
  html.push('</tr>');
 }
 if (!html.length) {
  html.push('<tr class="no-records-found">',
  sprintf('<td colspan="%s">%s</td>',
   this.$header.find('th').length, this.options.formatnomatches()),
  '</tr>');
 }
 this.$body.html(html.join(''));
 if (!fixedscroll) {
  this.scrollto(0);
 }
 this.$body.find('> tr[data-index] > td').off('click dblclick').on('click dblclick', function (e) {
  var $td = $(this),
  $tr = $td.parent(),
  item = that.data[$tr.data('index')],
  index = $td[0].cellindex,
  field = that.header.fields[that.options.detailview && !that.options.cardview ? index - 1 : index],
  column = that.columns[getfieldindex(that.columns, field)],
  value = getitemfield(item, field);
  if ($td.find('.detail-icon').length) {
  return;
  }
  that.trigger(e.type === 'click' ? 'click-cell' : 'dbl-click-cell', field, value, item, $td);
  that.trigger(e.type === 'click' ? 'click-row' : 'dbl-click-row', item, $tr);
  if (e.type === 'click' && that.options.clicktoselect && column.clicktoselect) {
  var $selectitem = $tr.find(sprintf('[name="%s"]', that.options.selectitemname));
  if ($selectitem.length) {
   $selectitem[0].click();
  }
  }
 });
 this.$body.find('> tr[data-index] > td > .detail-icon').off('click').on('click', function () {
  debugger;
  var $this = $(this),
  $tr = $this.parent().parent(),
  index = $tr.data('index'),
  row = data[index]; 
  if ($tr.next().is('tr.detail-view')) {
  $this.find('i').attr('class', sprintf('%s %s', that.options.iconsprefix, that.options.icons.detailopen));
  $tr.next().remove();
  that.trigger('collapse-row', index, row);
  } else {
  $this.find('i').attr('class', sprintf('%s %s', that.options.iconsprefix, that.options.icons.detailclose));
  $tr.after(sprintf('<tr class="detail-view"><td colspan="%s">%s</td></tr>',
   $tr.find('td').length, calculateobjectvalue(that.options,
   that.options.detailformatter, [index, row], '')));
  that.trigger('expand-row', index, row, $tr.next().find('td'));
  }
  that.resetview();
 });
 this.$body.find('> tr[data-index] > td > .tree-icon').off('click').on('click', function (e) {
  debugger;
  e.stoppropagation();
  var $this = $(this),
  $tr = $this.parent().parent(),
  index = $tr.data('index'),
  row = data[index];
  var icon = $(this);
  var child = getchild(data[index], data, that.options.treeid);
  $.each(child, function (i, c) {
  $.each(that.data, function (index, item) {
   if (item[that.options.treeid] == c[that.options.treeid]) {
   item.hidden = icon.hasclass(that.options.expandicon);
   that.uncheck(index);
   return;
   }
  });
  });
  if (icon.hasclass(that.options.expandicon)) {
  icon.removeclass(that.options.expandicon).addclass(that.options.collapseicon);
  } else {
  icon.removeclass(that.options.collapseicon).addclass(that.options.expandicon);
  }
  that.options.data = that.data;
  that.initbody(true);
 });
 this.$selectitem = this.$body.find(sprintf('[name="%s"]', this.options.selectitemname));
 this.$selectitem.off('click').on('click', function (event) {
  event.stopimmediatepropagation();
  var $this = $(this),
  checked = $this.prop('checked'),
  row = that.data[$this.data('index')];
  if (that.options.maintainselected && $(this).is(':radio')) {
  $.each(that.options.data, function (i, row) {
   row[that.header.statefield] = false;
  });
  }
  row[that.header.statefield] = checked;
  if (that.options.singleselect) {
  that.$selectitem.not(this).each(function () {
   that.data[$(this).data('index')][that.header.statefield] = false;
  });
  that.$selectitem.filter(':checked').not(this).prop('checked', false);
  }
  that.updateselected();
  that.trigger(checked ? 'check' : 'uncheck', row, $this);
 });
 $.each(this.header.events, function (i, events) {
  if (!events) {
  return;
  }
  if (typeof events === 'string') {
  events = calculateobjectvalue(null, events);
  }
  var field = that.header.fields[i],
  fieldindex = $.inarray(field, that.getvisiblefields());
  if (that.options.detailview && !that.options.cardview) {
  fieldindex += 1;
  }
  for (var key in events) {
  that.$body.find('tr').each(function () {
   var $tr = $(this),
   $td = $tr.find(that.options.cardview ? '.card-view' : 'td').eq(fieldindex),
   index = key.indexof(' '),
   name = key.substring(0, index),
   el = key.substring(index + 1),
   func = events[key];
   $td.find(el).off(name).on(name, function (e) {
   var index = $tr.data('index'),
    row = that.data[index],
    value = row[field];
   func.apply(this, [e, value, row, index]);
   });
  });
  }
 });
 this.updateselected();
 this.resetview();
 this.trigger('post-body');
 };
 //给组件增加默认参数列表
 $.extend($.fn.bootstraptable.defaults, {
 treeview: false,//treeview视图
 treefield: "id",//treeview视图字段
 treeid: "id",
 treerootlevel: 0,//根节点序号
 treecollapseall: false,//是否全部展开
 collapseicon: "glyphicon glyphicon-chevron-right",//折叠样式
 expandicon: "glyphicon glyphicon-chevron-down"//展开样式
 });
})(jquery);

组件的使用如下:

1、首先引用这个js文件。

2、然后初始化组件

$('#tb').bootstraptable({
   url: actionurl + 'getmenulist',
   toolbar: '#toolbar',
   sidepagination: 'client',
   pagination: false,
   treeview: true,
   treeid: "id",
   treefield: "name",
   treerootlevel: 1,
   clicktoselect: true,//collapseicon: "glyphicon glyphicon-triangle-right",//折叠样式
   //expandicon: "glyphicon glyphicon-triangle-bottom"//展开样式
  });

treeview:true表示启用树表格模式;

treeid:'id'表示每一行tree的id;

treefield:'name'表示要对那一列进行展开;

treerootlevel:1表示树根的级别。

还有一个地方需要注意,要建立记录之间的父子级关系,必然后有一个parentid的概念,所以在从后端返回的结果集里面,每条记录势必有一个parentid的属性,如果是根节点,parentid为null。比如我们后台得到的结果集的json格式如下:

[{id: 1, name: "系统设置", url: null, parentid: null, level: 1, createtime: null, status: 1, sortorder: 1,…},
{id: 2, name: "菜单管理", url: "/systems/menu/index", parentid: 1, level: 2, createtime: null, status: 1,…},
{id: 3, name: "订单管理", url: null, parentid: null, level: 1, createtime: "2017-05-31 17:05:27",…},
{id: 4, name: "基础数据", url: null, parentid: null, level: 1, createtime: "2017-05-31 17:05:55",…},
{id: 5, name: "新增订单", url: "/order/add", parentid: 3, level: 2, createtime: "2017-05-31 17:07:03",…}]

三、组件需要完善的地方

上述封装给大家提供一个扩展bootstraptable组件treeview功能,还有很多地方需要完善,比如:

1、我们的叶子节点前面的图标可以去掉;

2、增加展开所有、折叠所有的功能;

3、level字段可以去掉,通过parentid为null来确定根节点。

有兴趣的小伙伴可以自己试试。

四、总结

至此本文就结束了,这篇针对上篇做了一个补充,使得我们可以根据项目的需求自己选择用哪种方式,如果我们项目使用的是bootstraptable作为数据展示的组件,可以考虑上述扩展;如果没有使用bootstraptable,可以试试上篇的组件。