bootstrap-table-group-by.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /**
  2. * @author: Yura Knoxville
  3. * @version: v1.1.0
  4. */
  5. let initBodyCaller
  6. let tableGroups
  7. // it only does '%s', and return '' when arguments are undefined
  8. const sprintf = function (str) {
  9. const args = arguments
  10. let flag = true
  11. let i = 1
  12. str = str.replace(/%s/g, () => {
  13. const arg = args[i++]
  14. if (typeof arg === 'undefined') {
  15. flag = false
  16. return ''
  17. }
  18. return arg
  19. })
  20. return flag ? str : ''
  21. }
  22. const groupBy = (array, f) => {
  23. const groups = {}
  24. array.forEach(o => {
  25. const group = f(o)
  26. groups[group] = groups[group] || []
  27. groups[group].push(o)
  28. })
  29. return groups
  30. }
  31. $.extend($.fn.bootstrapTable.defaults, {
  32. groupBy: false,
  33. groupByField: '',
  34. groupByFormatter: undefined
  35. })
  36. const Utils = $.fn.bootstrapTable.utils
  37. const BootstrapTable = $.fn.bootstrapTable.Constructor
  38. const _initSort = BootstrapTable.prototype.initSort
  39. const _initBody = BootstrapTable.prototype.initBody
  40. const _updateSelected = BootstrapTable.prototype.updateSelected
  41. BootstrapTable.prototype.initSort = function (...args) {
  42. _initSort.apply(this, Array.prototype.slice.apply(args))
  43. const that = this
  44. tableGroups = []
  45. if ((this.options.groupBy) && (this.options.groupByField !== '')) {
  46. if ((this.options.sortName !== this.options.groupByField)) {
  47. if (this.options.customSort) {
  48. Utils.calculateObjectValue(this.options, this.options.customSort, [
  49. this.options.sortName,
  50. this.options.sortOrder,
  51. this.data
  52. ])
  53. } else {
  54. this.data.sort((a, b) => {
  55. a = a[that.options.groupByField].toString()
  56. b = b[that.options.groupByField].toString()
  57. return a.localeCompare(b, undefined, { numeric: true })
  58. })
  59. }
  60. }
  61. const groups = groupBy(that.data, item => [item[that.options.groupByField]])
  62. let index = 0
  63. $.each(groups, (key, value) => {
  64. tableGroups.push({
  65. id: index,
  66. name: key,
  67. data: value
  68. })
  69. value.forEach(item => {
  70. if (!item._data) {
  71. item._data = {}
  72. }
  73. item._data['parent-index'] = index
  74. })
  75. index++
  76. })
  77. }
  78. }
  79. BootstrapTable.prototype.initBody = function (...args) {
  80. initBodyCaller = true
  81. _initBody.apply(this, Array.prototype.slice.apply(args))
  82. if ((this.options.groupBy) && (this.options.groupByField !== '')) {
  83. const that = this
  84. let checkBox = false
  85. let visibleColumns = 0
  86. this.columns.forEach(column => {
  87. if (column.checkbox) {
  88. checkBox = true
  89. } else {
  90. if (column.visible) {
  91. visibleColumns += 1
  92. }
  93. }
  94. })
  95. if (this.options.detailView && !this.options.cardView) {
  96. visibleColumns += 1
  97. }
  98. tableGroups.forEach(item => {
  99. const html = []
  100. html.push(sprintf('<tr class="info groupBy expanded" data-group-index="%s">', item.id))
  101. if (that.options.detailView && !that.options.cardView) {
  102. html.push('<td class="detail"></td>')
  103. }
  104. if (checkBox) {
  105. html.push('<td class="bs-checkbox">',
  106. '<input name="btSelectGroup" type="checkbox" />',
  107. '</td>'
  108. )
  109. }
  110. let formattedValue = item.name
  111. if (typeof(that.options.groupByFormatter) === 'function') {
  112. formattedValue = that.options.groupByFormatter(item.name, item.id, item.data)
  113. }
  114. html.push('<td',
  115. sprintf(' colspan="%s"', visibleColumns),
  116. '>', formattedValue, '</td>'
  117. )
  118. html.push('</tr>')
  119. that.$body.find(`tr[data-parent-index=${item.id}]:first`).before($(html.join('')))
  120. })
  121. this.$selectGroup = []
  122. this.$body.find('[name="btSelectGroup"]').each(function () {
  123. const self = $(this)
  124. that.$selectGroup.push({
  125. group: self,
  126. item: that.$selectItem.filter(function () {
  127. return ($(this).closest('tr').data('parent-index') ===
  128. self.closest('tr').data('group-index'))
  129. })
  130. })
  131. })
  132. this.$container.off('click', '.groupBy')
  133. .on('click', '.groupBy', function () {
  134. $(this).toggleClass('expanded')
  135. that.$body.find(`tr[data-parent-index=${$(this).closest('tr').data('group-index')}]`).toggleClass('hidden')
  136. })
  137. this.$container.off('click', '[name="btSelectGroup"]')
  138. .on('click', '[name="btSelectGroup"]', function (event) {
  139. event.stopImmediatePropagation()
  140. const self = $(this)
  141. const checked = self.prop('checked')
  142. that[checked ? 'checkGroup' : 'uncheckGroup']($(this).closest('tr').data('group-index'))
  143. })
  144. }
  145. initBodyCaller = false
  146. this.updateSelected()
  147. }
  148. BootstrapTable.prototype.updateSelected = function (...args) {
  149. if (!initBodyCaller) {
  150. _updateSelected.apply(this, Array.prototype.slice.apply(args))
  151. if ((this.options.groupBy) && (this.options.groupByField !== '')) {
  152. this.$selectGroup.forEach(item => {
  153. const checkGroup = item.item.filter(':enabled').length ===
  154. item.item.filter(':enabled').filter(':checked').length
  155. item.group.prop('checked', checkGroup)
  156. })
  157. }
  158. }
  159. }
  160. BootstrapTable.prototype.getGroupSelections = function (index) {
  161. const that = this
  162. return this.data.filter(row => row[that.header.stateField] && (row._data['parent-index'] === index))
  163. }
  164. BootstrapTable.prototype.checkGroup = function (index) {
  165. this.checkGroup_(index, true)
  166. }
  167. BootstrapTable.prototype.uncheckGroup = function (index) {
  168. this.checkGroup_(index, false)
  169. }
  170. BootstrapTable.prototype.checkGroup_ = function (index, checked) {
  171. let rows
  172. const filter = function () {
  173. return ($(this).closest('tr').data('parent-index') === index)
  174. }
  175. if (!checked) {
  176. rows = this.getGroupSelections(index)
  177. }
  178. this.$selectItem.filter(filter).prop('checked', checked)
  179. this.updateRows()
  180. this.updateSelected()
  181. if (checked) {
  182. rows = this.getGroupSelections(index)
  183. }
  184. this.trigger(checked ? 'check-all' : 'uncheck-all', rows)
  185. }