bootstrap-table-print.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. /**
  2. * @update zhixin wen <wenzhixin2010@gmail.com>
  3. */
  4. const Utils = $.fn.bootstrapTable.utils
  5. function printPageBuilderDefault (table, styles) {
  6. return `
  7. <html>
  8. <head>
  9. ${styles}
  10. <style type="text/css" media="print">
  11. @page {
  12. size: auto;
  13. margin: 25px 0 25px 0;
  14. }
  15. </style>
  16. <style type="text/css" media="all">
  17. table {
  18. border-collapse: collapse;
  19. font-size: 12px;
  20. }
  21. table, th, td {
  22. border: 1px solid grey;
  23. }
  24. th, td {
  25. text-align: center;
  26. vertical-align: middle;
  27. }
  28. p {
  29. font-weight: bold;
  30. margin-left:20px;
  31. }
  32. table {
  33. width: 94%;
  34. margin-left: 3%;
  35. margin-right: 3%;
  36. }
  37. div.bs-table-print {
  38. text-align: center;
  39. }
  40. </style>
  41. </head>
  42. <title>Print Table</title>
  43. <body>
  44. <p>Printed on: ${new Date} </p>
  45. <div class="bs-table-print">${table}</div>
  46. </body>
  47. </html>
  48. `
  49. }
  50. Object.assign($.fn.bootstrapTable.locales, {
  51. formatPrint () {
  52. return 'Print'
  53. }
  54. })
  55. Object.assign($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)
  56. Object.assign($.fn.bootstrapTable.defaults, {
  57. showPrint: false,
  58. printAsFilteredAndSortedOnUI: true,
  59. printSortColumn: undefined,
  60. printSortOrder: 'asc',
  61. printStyles: [],
  62. printPageBuilder (table, styles) {
  63. return printPageBuilderDefault(table, styles)
  64. }
  65. })
  66. Object.assign($.fn.bootstrapTable.columnDefaults, {
  67. printFilter: undefined,
  68. printIgnore: false,
  69. printFormatter: undefined
  70. })
  71. Object.assign($.fn.bootstrapTable.defaults.icons, {
  72. print: {
  73. bootstrap3: 'glyphicon-print icon-share',
  74. bootstrap5: 'bi-printer',
  75. 'bootstrap-table': 'icon-printer'
  76. }[$.fn.bootstrapTable.theme] || 'fa-print'
  77. })
  78. $.BootstrapTable = class extends $.BootstrapTable {
  79. init (...args) {
  80. super.init(...args)
  81. if (!this.options.showPrint) {
  82. return
  83. }
  84. this.mergedCells = []
  85. }
  86. initToolbar (...args) {
  87. this.showToolbar = this.showToolbar || this.options.showPrint
  88. if (this.options.showPrint) {
  89. this.buttons = Object.assign(this.buttons, {
  90. print: {
  91. text: this.options.formatPrint(),
  92. icon: this.options.icons.print,
  93. event: () => {
  94. this.doPrint(this.options.printAsFilteredAndSortedOnUI ? this.getData() : this.options.data.slice(0))
  95. },
  96. attributes: {
  97. 'aria-label': this.options.formatPrint(),
  98. title: this.options.formatPrint()
  99. }
  100. }
  101. })
  102. }
  103. super.initToolbar(...args)
  104. }
  105. mergeCells (options) {
  106. super.mergeCells(options)
  107. if (!this.options.showPrint) {
  108. return
  109. }
  110. let col = this.getVisibleFields().indexOf(options.field)
  111. if (Utils.hasDetailViewIcon(this.options)) {
  112. col += 1
  113. }
  114. this.mergedCells.push({
  115. row: options.index,
  116. col,
  117. rowspan: options.rowspan || 1,
  118. colspan: options.colspan || 1
  119. })
  120. }
  121. doPrint (data) {
  122. const canPrint = column => {
  123. return !column.printIgnore && column.visible
  124. }
  125. const formatValue = (row, i, column) => {
  126. const value_ = Utils.getItemField(row, column.field, this.options.escape, column.escape)
  127. const value = Utils.calculateObjectValue(column,
  128. column.printFormatter || column.formatter,
  129. [value_, row, i], value_)
  130. return typeof value === 'undefined' || value === null ?
  131. this.options.undefinedText : $('<div>').html(value).html()
  132. }
  133. const buildTable = (data, columnsArray) => {
  134. const dir = this.$el.attr('dir') || 'ltr'
  135. const html = [`<table dir="${dir}"><thead>`]
  136. for (const columns of columnsArray) {
  137. html.push('<tr>')
  138. for (let h = 0; h < columns.length; h++) {
  139. if (canPrint(columns[h])) {
  140. html.push(
  141. `<th
  142. ${Utils.sprintf(' rowspan="%s"', columns[h].rowspan)}
  143. ${Utils.sprintf(' colspan="%s"', columns[h].colspan)}
  144. >${columns[h].title}</th>`)
  145. }
  146. }
  147. html.push('</tr>')
  148. }
  149. html.push('</thead><tbody>')
  150. const notRender = []
  151. if (this.mergedCells) {
  152. for (let mc = 0; mc < this.mergedCells.length; mc++) {
  153. const currentMergedCell = this.mergedCells[mc]
  154. for (let rs = 0; rs < currentMergedCell.rowspan; rs++) {
  155. const row = currentMergedCell.row + rs
  156. for (let cs = 0; cs < currentMergedCell.colspan; cs++) {
  157. const col = currentMergedCell.col + cs
  158. notRender.push(`${row},${col}`)
  159. }
  160. }
  161. }
  162. }
  163. for (let i = 0; i < data.length; i++) {
  164. html.push('<tr>')
  165. const columns = columnsArray.flat(1)
  166. columns.sort((c1, c2) => {
  167. return c1.colspanIndex - c2.colspanIndex
  168. })
  169. for (let j = 0; j < columns.length; j++) {
  170. if (columns[j].colspanGroup > 0) continue
  171. let rowspan = 0
  172. let colspan = 0
  173. if (this.mergedCells) {
  174. for (let mc = 0; mc < this.mergedCells.length; mc++) {
  175. const currentMergedCell = this.mergedCells[mc]
  176. if (currentMergedCell.col === j && currentMergedCell.row === i) {
  177. rowspan = currentMergedCell.rowspan
  178. colspan = currentMergedCell.colspan
  179. }
  180. }
  181. }
  182. if (
  183. canPrint(columns[j]) &&
  184. (
  185. !notRender.includes(`${i},${j}`) ||
  186. rowspan > 0 && colspan > 0
  187. )
  188. ) {
  189. if (rowspan > 0 && colspan > 0) {
  190. html.push(`<td ${Utils.sprintf(' rowspan="%s"', rowspan)} ${Utils.sprintf(' colspan="%s"', colspan)}>`, formatValue(data[i], i, columns[j]), '</td>')
  191. } else {
  192. html.push('<td>', formatValue(data[i], i, columns[j]), '</td>')
  193. }
  194. }
  195. }
  196. html.push('</tr>')
  197. }
  198. html.push('</tbody>')
  199. if (this.options.showFooter) {
  200. html.push('<footer><tr>')
  201. for (const columns of columnsArray) {
  202. for (let h = 0; h < columns.length; h++) {
  203. if (canPrint(columns)) {
  204. const footerData = Utils.trToData(columns, this.$el.find('>tfoot>tr'))
  205. const footerValue = Utils.calculateObjectValue(columns[h], columns[h].footerFormatter, [data], footerData[0] && footerData[0][columns[h].field] || '')
  206. html.push(`<th>${footerValue}</th>`)
  207. }
  208. }
  209. }
  210. html.push('</tr></footer>')
  211. }
  212. html.push('</table>')
  213. return html.join('')
  214. }
  215. const sortRows = (data, colName, sortOrder) => {
  216. if (!colName) {
  217. return data
  218. }
  219. let reverse = sortOrder !== 'asc'
  220. reverse = -(+reverse || -1)
  221. return data.sort((a, b) => reverse * a[colName].localeCompare(b[colName]))
  222. }
  223. const filterRow = (row, filters) => {
  224. for (let index = 0; index < filters.length; ++index) {
  225. if (row[filters[index].colName] !== filters[index].value) {
  226. return false
  227. }
  228. }
  229. return true
  230. }
  231. const filterRows = (data, filters) => data.filter(row => filterRow(row, filters))
  232. const getColumnFilters = columns => !columns || !columns[0] ? [] : columns[0].filter(col => col.printFilter).map(col => ({
  233. colName: col.field,
  234. value: col.printFilter
  235. }))
  236. data = filterRows(data, getColumnFilters(this.options.columns))
  237. data = sortRows(data, this.options.printSortColumn, this.options.printSortOrder)
  238. const table = buildTable(data, this.options.columns)
  239. const newWin = window.open('')
  240. const printStyles = typeof this.options.printStyles === 'string' ?
  241. this.options.printStyles.replace(/\[|\]| /g, '').toLowerCase().split(',') :
  242. this.options.printStyles
  243. const styles = printStyles.map(it =>
  244. `<link rel="stylesheet" href="${it}" />`).join('')
  245. const calculatedPrintPage = Utils.calculateObjectValue(this, this.options.printPageBuilder,
  246. [table, styles], printPageBuilderDefault(table, styles))
  247. const startPrint = () => {
  248. newWin.focus()
  249. newWin.print()
  250. newWin.close()
  251. }
  252. newWin.document.write(calculatedPrintPage)
  253. newWin.document.close()
  254. if (printStyles.length) {
  255. const links = document.getElementsByTagName('link')
  256. const lastLink = links[links.length - 1]
  257. lastLink.onload = startPrint
  258. } else {
  259. startPrint()
  260. }
  261. }
  262. }