bootstrap-table-group-by.js 7.0 KB

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