export.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. import { saveAs } from "file-saver";
  2. // import XLSX from "xlsx";
  3. import XLSX from "xlsx-style";
  4. function generateArray(table) {
  5. var out = [];
  6. var rows = table.querySelectorAll("tr");
  7. var ranges = [];
  8. for (var R = 0; R < rows.length; ++R) {
  9. var outRow = [];
  10. var row = rows[R];
  11. var columns = row.querySelectorAll("td");
  12. for (var C = 0; C < columns.length; ++C) {
  13. var cell = columns[C];
  14. var colspan = cell.getAttribute("colspan");
  15. var rowspan = cell.getAttribute("rowspan");
  16. var cellValue = cell.innerText;
  17. if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
  18. //Skip ranges
  19. ranges.forEach(function (range) {
  20. if (
  21. R >= range.s.r &&
  22. R <= range.e.r &&
  23. outRow.length >= range.s.c &&
  24. outRow.length <= range.e.c
  25. ) {
  26. for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
  27. }
  28. });
  29. //Handle Row Span
  30. if (rowspan || colspan) {
  31. rowspan = rowspan || 1;
  32. colspan = colspan || 1;
  33. ranges.push({
  34. s: {
  35. r: R,
  36. c: outRow.length
  37. },
  38. e: {
  39. r: R + rowspan - 1,
  40. c: outRow.length + colspan - 1
  41. }
  42. });
  43. }
  44. //Handle Value
  45. outRow.push(cellValue !== "" ? cellValue : null);
  46. //Handle Colspan
  47. if (colspan)
  48. for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
  49. }
  50. out.push(outRow);
  51. }
  52. return [out, ranges];
  53. }
  54. function datenum(v, date1904) {
  55. if (date1904) v += 1462;
  56. var epoch = Date.parse(v);
  57. return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
  58. }
  59. function sheet_from_array_of_arrays(data, opts) {
  60. var ws = {};
  61. var range = {
  62. s: {
  63. c: 10000000,
  64. r: 10000000
  65. },
  66. e: {
  67. c: 0,
  68. r: 0
  69. }
  70. };
  71. for (var R = 0; R != data.length; ++R) {
  72. for (var C = 0; C != data[R].length; ++C) {
  73. if (range.s.r > R) range.s.r = R;
  74. if (range.s.c > C) range.s.c = C;
  75. if (range.e.r < R) range.e.r = R;
  76. if (range.e.c < C) range.e.c = C;
  77. var cell = {
  78. v: data[R][C]
  79. };
  80. // 如果单元格所在的值为空,让其值为“——”,否则下面设置的边框对其不生效
  81. if (cell.v == null) {
  82. cell.v = "——";
  83. }
  84. var cell_ref = XLSX.utils.encode_cell({
  85. c: C,
  86. r: R
  87. });
  88. if (typeof cell.v === "number") cell.t = "n";
  89. else if (typeof cell.v === "boolean") cell.t = "b";
  90. else if (cell.v instanceof Date) {
  91. cell.t = "n";
  92. cell.z = XLSX.SSF._table[14];
  93. cell.v = datenum(cell.v);
  94. } else cell.t = "s";
  95. ws[cell_ref] = cell;
  96. }
  97. }
  98. if (range.s.c < 10000000) ws["!ref"] = XLSX.utils.encode_range(range);
  99. return ws;
  100. }
  101. function Workbook() {
  102. if (!(this instanceof Workbook)) return new Workbook();
  103. this.SheetNames = [];
  104. this.Sheets = {};
  105. }
  106. function s2ab(s) {
  107. var buf = new ArrayBuffer(s.length);
  108. var view = new Uint8Array(buf);
  109. for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xff;
  110. return buf;
  111. }
  112. export function export_table_to_excel(id) {
  113. var theTable = document.getElementById(id);
  114. var oo = generateArray(theTable);
  115. var ranges = oo[1];
  116. /* original data */
  117. var data = oo[0];
  118. var ws_name = "SheetJS";
  119. var wb = new Workbook(),
  120. ws = sheet_from_array_of_arrays(data);
  121. /* add ranges to worksheet */
  122. // ws['!cols'] = ['apple', 'banan'];
  123. ws["!merges"] = ranges;
  124. /* add worksheet to workbook */
  125. wb.SheetNames.push(ws_name);
  126. wb.Sheets[ws_name] = ws;
  127. var wbout = XLSX.write(wb, {
  128. bookType: "xlsx",
  129. bookSST: false,
  130. type: "binary"
  131. });
  132. saveAs(
  133. new Blob([s2ab(wbout)], {
  134. type: "application/octet-stream"
  135. }),
  136. "test.xlsx"
  137. );
  138. }
  139. // 主要修改内容在这里
  140. export function export_json_to_excel({
  141. title, // 新增的参数,表格标题
  142. multiHeader = [],
  143. header,
  144. data,
  145. filename,
  146. merges = [],
  147. autoWidth = true,
  148. bookType = "xlsx"
  149. } = {}) {
  150. /* original data */
  151. filename = filename || "excel-list";
  152. data = [...data];
  153. data.unshift(header);
  154. for (let i = multiHeader.length - 1; i > -1; i--) {
  155. data.unshift(multiHeader[i]);
  156. }
  157. data.unshift(title); // 表格标题
  158. var ws_name = "SheetJS";
  159. var wb = new Workbook(),
  160. ws = sheet_from_array_of_arrays(data);
  161. if (merges.length > 0) {
  162. if (!ws["!merges"]) ws["!merges"] = [];
  163. merges.forEach(item => {
  164. ws["!merges"].push(XLSX.utils.decode_range(item));
  165. });
  166. }
  167. // 设置单元格宽度
  168. if (autoWidth) {
  169. /*设置worksheet每列的最大宽度*/
  170. const colWidth = data.map(row =>
  171. row.map(val => {
  172. /*先判断是否为null/undefined*/
  173. if (val == null || val == undefined) {
  174. return {
  175. wch: 10
  176. };
  177. } else if (val.toString().charCodeAt(0) > 255) {
  178. /*再判断是否为中文*/
  179. return {
  180. wch: val.toString().length * 2
  181. };
  182. } else {
  183. return {
  184. wch: val.toString().length * 1.5
  185. };
  186. }
  187. })
  188. );
  189. /*以主表第二行为初始值,因为我的第一行是表格标题,会比较长,所以以主表第二行为初始值*/
  190. let result = colWidth[1];
  191. for (let i = 1; i < colWidth.length; i++) {
  192. for (let j = 0; j < colWidth[i].length; j++) {
  193. if (result[j]["wch"] < colWidth[i][j]["wch"]) {
  194. result[j]["wch"] = colWidth[i][j]["wch"];
  195. }
  196. }
  197. }
  198. ws["!cols"] = result;
  199. }
  200. /* add worksheet to workbook */
  201. wb.SheetNames.push(ws_name);
  202. wb.Sheets[ws_name] = ws;
  203. var dataInfo = wb.Sheets[wb.SheetNames[0]];
  204. // 设置单元格框线
  205. const borderAll = {
  206. top: {
  207. style: "thin"
  208. },
  209. bottom: {
  210. style: "thin"
  211. },
  212. left: {
  213. style: "thin"
  214. },
  215. right: {
  216. style: "thin"
  217. }
  218. };
  219. // 给所有单元格加上边框,内容居中,字体,字号,标题表头特殊格式部分后面替换
  220. for (var i in dataInfo) {
  221. if (
  222. i == "!ref" ||
  223. i == "!merges" ||
  224. i == "!cols" ||
  225. i == "!rows" ||
  226. i == "A1"
  227. ) { } else {
  228. dataInfo[i + ""].s = {
  229. border: borderAll,
  230. alignment: {
  231. horizontal: "center",
  232. vertical: "center"
  233. },
  234. font: {
  235. name: "微软雅黑",
  236. sz: 10
  237. }
  238. };
  239. }
  240. }
  241. // 设置表格样式
  242. const arrabc = ["A",
  243. "B",
  244. "C",
  245. "D",
  246. "E",
  247. "F",
  248. "G",
  249. "H",
  250. "I",
  251. "J",
  252. "K",
  253. "L",
  254. "M",
  255. "N",
  256. "O",
  257. "P",
  258. "Q",
  259. "R",
  260. "S",
  261. "T",
  262. "U",
  263. "V",
  264. "W",
  265. "X",
  266. "Y",
  267. "Z"
  268. ]
  269. // 给标题、表格描述信息、表头等部分加上特殊格式
  270. arrabc.some(function (v) {
  271. for (let j = 1; j < multiHeader.length + 3; j++) {
  272. const _v = v + j
  273. if (dataInfo[_v]) {
  274. dataInfo[_v].s = {};
  275. // 标题部分A1-Z1
  276. if (j == 1) {
  277. dataInfo[v + j].s = {
  278. font: {
  279. name: "微软雅黑",
  280. sz: 12,
  281. color: {
  282. rgb: "000000"
  283. },
  284. bold: true,
  285. italic: false,
  286. underline: false
  287. },
  288. alignment: {
  289. horizontal: "center",
  290. vertical: "center"
  291. }
  292. };
  293. } else {
  294. // 表头部分,根据表头特殊格式设置
  295. if (multiHeader.length == 0) {
  296. // multiHeader.length = 0 时表头没有合并单元格,表头只占1行A2-Z2
  297. const fv = v + (multiHeader.length + 2)
  298. dataInfo[fv].s = {
  299. border: borderAll,
  300. font: {
  301. name: "微软雅黑",
  302. sz: 11,
  303. bold: true
  304. },
  305. alignment: {
  306. horizontal: "center",
  307. vertical: "center"
  308. },
  309. fill: {
  310. fgColor: {
  311. rgb: "f0f0f0"
  312. },
  313. },
  314. }
  315. } else if (multiHeader.length == 1) {
  316. // multiHeader.length = 0 时表头有合并单元格,表头只占2行A2-Z2,A3-Z3,这是没有描述信息只有表头合并的
  317. dataInfo[v + j].s = {
  318. border: borderAll,
  319. font: {
  320. name: "微软雅黑",
  321. sz: 11,
  322. },
  323. alignment: {
  324. horizontal: "center",
  325. vertical: "center"
  326. },
  327. fill: {
  328. fgColor: {
  329. rgb: "f0f0f0"
  330. }
  331. },
  332. }
  333. } else {
  334. // multiHeader.length = 0 时表头有合并单元格,表头多行
  335. dataInfo[v + j].s = {
  336. border: borderAll,
  337. font: {
  338. name: "微软雅黑",
  339. sz: 9,
  340. },
  341. alignment: {
  342. horizontal: "left",
  343. vertical: "center"
  344. }
  345. }
  346. }
  347. }
  348. // multiHeader.length + 2 是表头的最后1行
  349. dataInfo[v + (multiHeader.length + 2)].s = {
  350. border: borderAll,
  351. font: {
  352. name: "微软雅黑",
  353. sz: 10,
  354. },
  355. alignment: {
  356. horizontal: "center",
  357. vertical: "center"
  358. },
  359. fill: {
  360. fgColor: {
  361. rgb: "f0f0f0"
  362. }
  363. },
  364. }
  365. }
  366. }
  367. });
  368. var wbout = XLSX.write(wb, {
  369. bookType: bookType,
  370. bookSST: false,
  371. type: "binary"
  372. });
  373. saveAs(
  374. new Blob([s2ab(wbout)], {
  375. type: "application/octet-stream"
  376. }),
  377. `${filename}.${bookType}`
  378. );
  379. }