datatable_export_action.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. function colIdxToLetter(n) {
  2. let s = "";
  3. n = n + 1;
  4. while (n > 0) {
  5. const m = (n - 1) % 26;
  6. s = String.fromCharCode(65 + m) + s;
  7. n = Math.floor((n - m) / 26);
  8. }
  9. return s;
  10. }
  11. function escapeXmlText(txt) {
  12. return String(txt ?? "")
  13. .replace(/&/g, "&")
  14. .replace(/</g, "&lt;")
  15. .replace(/>/g, "&gt;");
  16. }
  17. function readCellText($cell, sheet, xlsx) {
  18. const t = $cell.attr("t");
  19. if (t === "inlineStr") {
  20. const tNode = $cell.find("is t");
  21. return tNode.length ? tNode.text() : "";
  22. }
  23. if (t === "s") {
  24. const v = $cell.find("v").text();
  25. const idx = parseInt(v, 10);
  26. const sst = xlsx.xl["sharedStrings.xml"];
  27. if (!sst) return "";
  28. const si = $("sst si", sst).eq(idx);
  29. if (!si.length) return "";
  30. let out = "";
  31. si.find("t").each(function () {
  32. out += $(this).text();
  33. });
  34. return out;
  35. }
  36. const vNode = $cell.find("v");
  37. return vNode.length ? vNode.text() : "";
  38. }
  39. function newexportaction(e, dt, button, config, cb) {
  40. const self = this;
  41. const settings = dt.settings()[0];
  42. const isServerSide = settings.oFeatures && settings.oFeatures.bServerSide;
  43. const hasAjax = !!settings.ajax;
  44. const customizeHeaderExcel = function (xlsx) {
  45. const sheet = xlsx.xl.worksheets["sheet1.xml"];
  46. const styles = xlsx.xl["styles.xml"];
  47. // --- crea background azzurro ---
  48. const fills = $("fills", styles);
  49. fills.append(
  50. '<fill><patternFill patternType="solid">' +
  51. '<fgColor rgb="FFDDEBF7"/><bgColor indexed="64"/>' +
  52. "</patternFill></fill>"
  53. );
  54. const fillId = $("fill", styles).length - 1;
  55. fills.attr("count", $("fill", styles).length); // aggiorna count
  56. // --- crea FONT bold nero ---
  57. const fonts = $("fonts", styles);
  58. fonts.append(
  59. '<font><b/><sz val="11"/><color rgb="FF000000"/><name val="Calibri"/></font>'
  60. );
  61. const fontId = $("font", styles).length - 1;
  62. fonts.attr("count", $("font", styles).length); // aggiorna count
  63. // --- crea XF senza bordi, bold + fill azzurro ---
  64. const cellXfs = $("cellXfs", styles);
  65. cellXfs.append(
  66. `<xf xfId="0" applyFont="1" applyFill="1" borderId="0" fontId="${fontId}" fillId="${fillId}">
  67. <alignment vertical="center"/>
  68. </xf>`
  69. );
  70. const xfId = $("cellXfs xf", styles).length - 1;
  71. cellXfs.attr("count", $("cellXfs xf", styles).length); // aggiorna count
  72. // --- applica lo stile alla PRIMA riga (header) ---
  73. $("row:first c", sheet).attr("s", xfId);
  74. };
  75. const alignLeftCells = function (xlsx, config) {
  76. let leftCols = Array.isArray(config.exportLeftCols)
  77. ? [...config.exportLeftCols]
  78. : [];
  79. if (leftCols.length === 0 && dt && dt.columns) {
  80. const headerIdxs = [];
  81. $(dt.columns().header()).each(function (i, th) {
  82. if ($(th).hasClass("export-left")) headerIdxs.push(i);
  83. });
  84. leftCols = headerIdxs;
  85. }
  86. if (leftCols.length) {
  87. const sheet = xlsx.xl.worksheets["sheet1.xml"];
  88. const styles = xlsx.xl["styles.xml"];
  89. const cellXfs = $("cellXfs", styles);
  90. cellXfs.append(
  91. `<xf xfId="0" applyAlignment="1">
  92. <alignment horizontal="left" vertical="center"/>
  93. </xf>`
  94. );
  95. const leftXfId = $("cellXfs xf", styles).length - 1;
  96. cellXfs.attr("count", $("cellXfs xf", styles).length);
  97. leftCols.forEach(function (colIdx) {
  98. const colLetter = colIdxToLetter(colIdx);
  99. $('row:not(:first) c[r^="' + colLetter + '"]', sheet).each(
  100. function () {
  101. const cell = $(this);
  102. const sAttr = cell.attr("s");
  103. if (sAttr === undefined) {
  104. cell.attr("s", leftXfId);
  105. } else {
  106. const xfs = $("cellXfs xf", styles);
  107. const baseXf = xfs.eq(parseInt(sAttr, 10)).clone();
  108. if (baseXf.find("alignment").length === 0) {
  109. baseXf.append(
  110. '<alignment horizontal="left" vertical="center"/>'
  111. );
  112. } else {
  113. baseXf
  114. .find("alignment")
  115. .attr("horizontal", "left")
  116. .attr("vertical", "center");
  117. }
  118. cellXfs.append(baseXf);
  119. const newId = $("cellXfs xf", styles).length - 1;
  120. cellXfs.attr(
  121. "count",
  122. $("cellXfs xf", styles).length
  123. );
  124. cell.attr("s", newId);
  125. }
  126. }
  127. );
  128. });
  129. }
  130. };
  131. const forceAllCellsAsText = function (xlsx) {
  132. const sheet = xlsx.xl.worksheets["sheet1.xml"];
  133. if (!sheet) return;
  134. $("worksheet sheetData row c", sheet).each(function () {
  135. const $cell = $(this);
  136. const text = readCellText($cell, sheet, xlsx);
  137. // sostituisco contenuto con inline string
  138. $cell.attr("t", "inlineStr");
  139. $cell.find("v").remove();
  140. $cell.find("is").remove();
  141. const safe = escapeXmlText(text);
  142. $cell.append(`<is><t>${safe}</t></is>`);
  143. // NON tocco l’attributo 's' (stile) se già esiste
  144. });
  145. };
  146. // --- CLIENT-SIDE ---
  147. if (!isServerSide || !hasAjax) {
  148. // Esporta tutte le righe lato client
  149. const origExportOptions = config.exportOptions
  150. ? { ...config.exportOptions }
  151. : {};
  152. config.exportOptions = config.exportOptions || {};
  153. config.exportOptions.modifier = {
  154. ...(config.exportOptions.modifier || {}),
  155. page: "all",
  156. };
  157. // PDF: tabella full-width
  158. // const isPdf = button[0].className.indexOf('buttons-pdf') >= 0;
  159. // const origCustomize = config.customize;
  160. // if (isPdf) {
  161. // config.customize = function(doc) {
  162. // const table = doc.content[1] && doc.content[1].table ? doc.content[1].table : null;
  163. // if (table && table.body && table.body[0]) {
  164. // table.widths = Array(table.body[0].length).fill('*');
  165. // }
  166. // if (typeof origCustomize === 'function') origCustomize(doc);
  167. // };
  168. // }
  169. const cls = button[0].className;
  170. if (cls.includes("buttons-copy")) {
  171. $.fn.dataTable.ext.buttons.copyHtml5.action.call(
  172. self,
  173. e,
  174. dt,
  175. button,
  176. config,
  177. cb
  178. );
  179. } else if (cls.includes("buttons-excel")) {
  180. const origCustomize = config.customize;
  181. config.filename = config.title;
  182. config.title = null;
  183. config.customize = function (xlsx) {
  184. if (typeof origCustomize === "function") origCustomize(xlsx);
  185. customizeHeaderExcel(xlsx);
  186. alignLeftCells(xlsx, config);
  187. forceAllCellsAsText(xlsx);
  188. };
  189. ($.fn.dataTable.ext.buttons.excelHtml5.available(dt, config)
  190. ? $.fn.dataTable.ext.buttons.excelHtml5
  191. : $.fn.dataTable.ext.buttons.excelFlash
  192. ).action.call(self, e, dt, button, config, cb);
  193. config.customize = origCustomize; // ripristina
  194. } else if (cls.includes("buttons-csv")) {
  195. ($.fn.dataTable.ext.buttons.csvHtml5.available(dt, config)
  196. ? $.fn.dataTable.ext.buttons.csvHtml5
  197. : $.fn.dataTable.ext.buttons.csvFlash
  198. ).action.call(self, e, dt, button, config, cb);
  199. } else if (cls.includes("buttons-pdf")) {
  200. ($.fn.dataTable.ext.buttons.pdfHtml5.available(dt, config)
  201. ? $.fn.dataTable.ext.buttons.pdfHtml5
  202. : $.fn.dataTable.ext.buttons.pdfFlash
  203. ).action.call(self, e, dt, button, config, cb);
  204. } else if (cls.includes("buttons-print")) {
  205. $.fn.dataTable.ext.buttons.print.action.call(
  206. self,
  207. e,
  208. dt,
  209. button,
  210. config,
  211. cb
  212. );
  213. }
  214. // Ripristina config
  215. config.exportOptions = origExportOptions;
  216. // if (isPdf) config.customize = origCustomize;
  217. if (typeof cb === "function") cb();
  218. return;
  219. }
  220. // --- SERVER-SIDE + AJAX: fetch completo poi export ---
  221. const info = dt.page.info();
  222. const oldStart = info.start;
  223. const oldLength = info.length;
  224. const targetLength = info.recordsTotal > 0 ? info.recordsTotal : 2147483647;
  225. dt.one("preXhr", function (e2, s, data) {
  226. data.start = 0;
  227. data.length = targetLength;
  228. dt.one("preDraw", function (e3, stg) {
  229. const cls = button[0].className;
  230. if (cls.includes("buttons-copy")) {
  231. $.fn.dataTable.ext.buttons.copyHtml5.action.call(
  232. self,
  233. e,
  234. dt,
  235. button,
  236. config,
  237. cb
  238. );
  239. } else if (cls.includes("buttons-excel")) {
  240. const origCustomize = config.customize;
  241. config.filename = config.title;
  242. config.title = null;
  243. config.customize = function (xlsx) {
  244. if (typeof origCustomize === "function")
  245. origCustomize(xlsx);
  246. customizeHeaderExcel(xlsx);
  247. alignLeftCells(xlsx, config);
  248. forceAllCellsAsText(xlsx);
  249. };
  250. ($.fn.dataTable.ext.buttons.excelHtml5.available(dt, config)
  251. ? $.fn.dataTable.ext.buttons.excelHtml5
  252. : $.fn.dataTable.ext.buttons.excelFlash
  253. ).action.call(self, e, dt, button, config, cb);
  254. config.customize = origCustomize;
  255. } else if (cls.includes("buttons-csv")) {
  256. ($.fn.dataTable.ext.buttons.csvHtml5.available(dt, config)
  257. ? $.fn.dataTable.ext.buttons.csvHtml5
  258. : $.fn.dataTable.ext.buttons.csvFlash
  259. ).action.call(self, e, dt, button, config, cb);
  260. } else if (cls.includes("buttons-pdf")) {
  261. // Full-width PDF
  262. // const origCustomize = config.customize;
  263. // config.customize = function(doc) {
  264. // const t = doc.content[1] && doc.content[1].table ? doc.content[1].table : null;
  265. // if (t && t.body && t.body[0]) t.widths = Array(t.body[0].length).fill('*');
  266. // if (typeof origCustomize === 'function') origCustomize(doc);
  267. // };
  268. ($.fn.dataTable.ext.buttons.pdfHtml5.available(dt, config)
  269. ? $.fn.dataTable.ext.buttons.pdfHtml5
  270. : $.fn.dataTable.ext.buttons.pdfFlash
  271. ).action.call(self, e, dt, button, config, cb);
  272. // config.customize = origCustomize;
  273. } else if (cls.includes("buttons-print")) {
  274. $.fn.dataTable.ext.buttons.print.action.call(
  275. self,
  276. e,
  277. dt,
  278. button,
  279. config,
  280. cb
  281. );
  282. }
  283. dt.one("preXhr", function (e4, s2, data2) {
  284. data2.start = oldStart;
  285. data2.length = oldLength;
  286. stg._iDisplayStart = oldStart;
  287. });
  288. setTimeout(() => {
  289. dt.ajax.reload(() => {
  290. if (typeof cb === "function") cb();
  291. }, false);
  292. }, 0);
  293. return false;
  294. });
  295. });
  296. dt.ajax.reload();
  297. }