excel-export.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import * as XLSX from 'xlsx'
  2. /**
  3. * 导出Excel文件
  4. * @param {Array} data - 导出的数据
  5. * @param {String} filename - 文件名
  6. * @param {String} sheetName - 工作表名称
  7. */
  8. export const exportToExcel = (data:any, filename:string = 'export.xlsx', sheetName:string = 'Sheet1') => {
  9. // 创建工作簿
  10. const wb = XLSX.utils.book_new()
  11. // 创建工作表
  12. const ws = XLSX.utils.json_to_sheet(data)
  13. // 添加工作表到工作簿
  14. XLSX.utils.book_append_sheet(wb, ws, sheetName)
  15. // 导出文件
  16. XLSX.writeFile(wb, filename)
  17. }
  18. /**
  19. * 导出多工作表Excel文件
  20. * @param {Object} sheetData - 包含多个工作表数据的对象 {sheetName1: data1, sheetName2: data2}
  21. * @param {String} filename - 文件名
  22. */
  23. export const exportMultiSheetExcel = (sheetData:any, filename:string = 'export.xlsx') => {
  24. const wb = XLSX.utils.book_new()
  25. // 遍历添加多个工作表
  26. Object.keys(sheetData).forEach(sheetName => {
  27. const ws = XLSX.utils.json_to_sheet(sheetData[sheetName])
  28. XLSX.utils.book_append_sheet(wb, ws, sheetName)
  29. })
  30. XLSX.writeFile(wb, filename)
  31. }
  32. /**
  33. * 自定义表头的Excel导出
  34. * @param {Array} data - 数据
  35. * @param {Array} headers - 表头配置 [{key: 'name', title: '姓名'}, ...]
  36. * @param {String} filename - 文件名
  37. * @param {String} sheetName - 工作表名称
  38. */
  39. export const exportWithCustomHeaders = (data:any, headers:any, filename:string = 'export.xlsx', sheetName:string = 'Sheet1') => {
  40. // 准备导出数据,将key映射为title
  41. const exportData = data.map(item => {
  42. const row = {}
  43. headers.forEach(header => {
  44. row[header.title] = item[header.key]
  45. })
  46. return row
  47. })
  48. const wb = XLSX.utils.book_new()
  49. const ws = XLSX.utils.json_to_sheet(exportData)
  50. XLSX.utils.book_append_sheet(wb, ws, sheetName)
  51. XLSX.writeFile(wb, filename)
  52. }
  53. /**
  54. * 高级对象数组处理 - 支持展开模式
  55. * @param {Array} data - 数据
  56. * @param {Array} headers - 表头配置
  57. * 普通字段: {key: 'name', title: '姓名'}
  58. * 对象数组展开: {
  59. * key: 'projects',
  60. * title: '项目',
  61. * arrayType: 'object',
  62. * expand: true, // 展开模式
  63. * fieldMappings: { // 字段映射
  64. * name: '项目名称',
  65. * role: '项目角色'
  66. * }
  67. * }
  68. * @param {String} filename - 文件名
  69. * @param {String} sheetName - 工作表名称
  70. */
  71. export const exportWithExpandedObjectArrays = (data:any, headers:any, filename:string = 'export.xlsx', sheetName:string = 'Sheet1') => {
  72. // 首先处理展开的对象数组
  73. let processedData = [...data]
  74. let processedHeaders = [...headers]
  75. // 找出需要展开的对象数组字段
  76. const expandFields = headers.filter(header =>
  77. header.arrayType === 'object' && header.expand
  78. )
  79. if (expandFields.length > 0) {
  80. // 计算每个对象数组的最大长度
  81. expandFields.forEach(field => {
  82. const maxLength = Math.max(...data.map(item => {
  83. const arr = item[field.key]
  84. return Array.isArray(arr) ? arr.length : 0
  85. }))
  86. // 移除原始字段
  87. processedHeaders = processedHeaders.filter(h => h.key !== field.key)
  88. // 添加展开后的字段
  89. for (let i = 0; i < maxLength; i++) {
  90. const baseKey = `${field.key}[${i}]`
  91. const baseTitle = `${field.title}[${i + 1}]`
  92. if (field.fieldMappings) {
  93. // 使用字段映射
  94. Object.keys(field.fieldMappings).forEach(sourceField => {
  95. processedHeaders.push({
  96. key: `${baseKey}.${sourceField}`,
  97. title: `${baseTitle} - ${field.fieldMappings[sourceField]}`
  98. })
  99. })
  100. } else {
  101. // 默认所有字段
  102. processedHeaders.push({
  103. key: `${baseKey}`,
  104. title: baseTitle
  105. })
  106. }
  107. }
  108. })
  109. // 重新处理数据,展开对象数组
  110. processedData = data.map(item => {
  111. const newItem = { ...item }
  112. expandFields.forEach(field => {
  113. const arr = item[field.key]
  114. if (Array.isArray(arr)) {
  115. for (let i = 0; i < arr.length; i++) {
  116. const obj = arr[i]
  117. if (typeof obj === 'object' && obj !== null) {
  118. if (field.fieldMappings) {
  119. // 使用字段映射
  120. Object.keys(field.fieldMappings).forEach(sourceField => {
  121. newItem[`${field.key}[${i}].${sourceField}`] = obj[sourceField]
  122. })
  123. } else {
  124. // 默认所有字段
  125. Object.keys(obj).forEach(sourceField => {
  126. newItem[`${field.key}[${i}].${sourceField}`] = obj[sourceField]
  127. })
  128. }
  129. }
  130. }
  131. }
  132. })
  133. return newItem
  134. })
  135. }
  136. // 处理其他字段
  137. const exportData = processedData.map(item => {
  138. const row = {}
  139. processedHeaders.forEach(header => {
  140. if (header.arrayType === 'object' && !header.expand) {
  141. // 非展开的对象数组处理
  142. const value = item[header.key]
  143. if (Array.isArray(value)) {
  144. if (header.extractFields && header.extractFields.length > 0) {
  145. const extractedValues = value.map(obj => {
  146. if (typeof obj === 'object' && obj !== null) {
  147. return header.extractFields.map(field => obj[field]).join(' ')
  148. }
  149. return obj
  150. })
  151. row[header.title] = extractedValues.join(header.joinWith || ', ')
  152. } else {
  153. const extractedValues = value.map(obj => {
  154. if (typeof obj === 'object' && obj !== null) {
  155. return Object.keys(obj).map(key => `${key}: ${obj[key]}`).join(' ')
  156. }
  157. return obj
  158. })
  159. row[header.title] = extractedValues.join(header.joinWith || ', ')
  160. }
  161. } else {
  162. row[header.title] = value
  163. }
  164. } else if (!header.expand) {
  165. // 普通字段或普通数组
  166. const value = item[header.key]
  167. if (Array.isArray(value)) {
  168. row[header.title] = value.join(header.joinWith || ', ')
  169. } else {
  170. row[header.title] = value
  171. }
  172. } else {
  173. // 展开的字段,直接使用
  174. row[header.title] = item[header.key]
  175. }
  176. })
  177. return row
  178. })
  179. const wb = XLSX.utils.book_new()
  180. const ws = XLSX.utils.json_to_sheet(exportData)
  181. XLSX.utils.book_append_sheet(wb, ws, sheetName)
  182. XLSX.writeFile(wb, filename)
  183. }