datatable_export_action.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  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. const origTitle = config.title;
  182. const origFilename = config.filename;
  183. config.filename =
  184. typeof origFilename !== "undefined" ? origFilename : origTitle;
  185. config.title = null;
  186. config.customize = function (xlsx) {
  187. if (typeof origCustomize === "function") origCustomize(xlsx);
  188. customizeHeaderExcel(xlsx);
  189. alignLeftCells(xlsx, config);
  190. forceAllCellsAsText(xlsx);
  191. };
  192. ($.fn.dataTable.ext.buttons.excelHtml5.available(dt, config)
  193. ? $.fn.dataTable.ext.buttons.excelHtml5
  194. : $.fn.dataTable.ext.buttons.excelFlash
  195. ).action.call(self, e, dt, button, config, cb);
  196. config.customize = origCustomize;
  197. config.title = origTitle;
  198. config.filename = origFilename;
  199. } else if (cls.includes("buttons-csv")) {
  200. ($.fn.dataTable.ext.buttons.csvHtml5.available(dt, config)
  201. ? $.fn.dataTable.ext.buttons.csvHtml5
  202. : $.fn.dataTable.ext.buttons.csvFlash
  203. ).action.call(self, e, dt, button, config, cb);
  204. } else if (cls.includes("buttons-pdf")) {
  205. ($.fn.dataTable.ext.buttons.pdfHtml5.available(dt, config)
  206. ? $.fn.dataTable.ext.buttons.pdfHtml5
  207. : $.fn.dataTable.ext.buttons.pdfFlash
  208. ).action.call(self, e, dt, button, config, cb);
  209. } else if (cls.includes("buttons-print")) {
  210. $.fn.dataTable.ext.buttons.print.action.call(
  211. self,
  212. e,
  213. dt,
  214. button,
  215. config,
  216. cb,
  217. );
  218. }
  219. // Ripristina config
  220. config.exportOptions = origExportOptions;
  221. // if (isPdf) config.customize = origCustomize;
  222. if (typeof cb === "function") cb();
  223. return;
  224. }
  225. // --- SERVER-SIDE + AJAX: fetch completo poi export ---
  226. const info = dt.page.info();
  227. const oldStart = info.start;
  228. const oldLength = info.length;
  229. const targetLength = info.recordsTotal > 0 ? info.recordsTotal : 2147483647;
  230. dt.one("preXhr", function (e2, s, data) {
  231. data.start = 0;
  232. data.length = targetLength;
  233. dt.one("preDraw", function (e3, stg) {
  234. const cls = button[0].className;
  235. if (cls.includes("buttons-copy")) {
  236. $.fn.dataTable.ext.buttons.copyHtml5.action.call(
  237. self,
  238. e,
  239. dt,
  240. button,
  241. config,
  242. cb,
  243. );
  244. } else if (cls.includes("buttons-excel")) {
  245. const origCustomize = config.customize;
  246. const origTitle = config.title;
  247. const origFilename = config.filename;
  248. config.filename =
  249. typeof origFilename !== "undefined"
  250. ? origFilename
  251. : origTitle;
  252. config.title = null;
  253. config.customize = function (xlsx) {
  254. if (typeof origCustomize === "function") {
  255. origCustomize(xlsx);
  256. }
  257. customizeHeaderExcel(xlsx);
  258. alignLeftCells(xlsx, config);
  259. forceAllCellsAsText(xlsx);
  260. };
  261. ($.fn.dataTable.ext.buttons.excelHtml5.available(dt, config)
  262. ? $.fn.dataTable.ext.buttons.excelHtml5
  263. : $.fn.dataTable.ext.buttons.excelFlash
  264. ).action.call(self, e, dt, button, config, cb);
  265. config.customize = origCustomize;
  266. config.title = origTitle;
  267. config.filename = origFilename;
  268. } else if (cls.includes("buttons-csv")) {
  269. ($.fn.dataTable.ext.buttons.csvHtml5.available(dt, config)
  270. ? $.fn.dataTable.ext.buttons.csvHtml5
  271. : $.fn.dataTable.ext.buttons.csvFlash
  272. ).action.call(self, e, dt, button, config, cb);
  273. } else if (cls.includes("buttons-pdf")) {
  274. // Full-width PDF
  275. // const origCustomize = config.customize;
  276. // config.customize = function(doc) {
  277. // const t = doc.content[1] && doc.content[1].table ? doc.content[1].table : null;
  278. // if (t && t.body && t.body[0]) t.widths = Array(t.body[0].length).fill('*');
  279. // if (typeof origCustomize === 'function') origCustomize(doc);
  280. // };
  281. ($.fn.dataTable.ext.buttons.pdfHtml5.available(dt, config)
  282. ? $.fn.dataTable.ext.buttons.pdfHtml5
  283. : $.fn.dataTable.ext.buttons.pdfFlash
  284. ).action.call(self, e, dt, button, config, cb);
  285. // config.customize = origCustomize;
  286. } else if (cls.includes("buttons-print")) {
  287. $.fn.dataTable.ext.buttons.print.action.call(
  288. self,
  289. e,
  290. dt,
  291. button,
  292. config,
  293. cb,
  294. );
  295. }
  296. dt.one("preXhr", function (e4, s2, data2) {
  297. data2.start = oldStart;
  298. data2.length = oldLength;
  299. stg._iDisplayStart = oldStart;
  300. });
  301. setTimeout(() => {
  302. dt.ajax.reload(() => {
  303. if (typeof cb === "function") cb();
  304. }, false);
  305. }, 0);
  306. return false;
  307. });
  308. });
  309. dt.ajax.reload();
  310. }