加载中...

Vue2实现导出Excel文件,并设置行高,宽度,合并单元格等。

Vue2实现导出Excel文件,并设置行高,宽度,合并单元格等。

一、省流

        Demo

        代码

二、安装xlsx和xlsx-style-fixedver

  1. npm install xlsx
  2. npm install xlsx-style-fixedver

        xlsx免费版只支持基础的生成xlsx文件,并不支持设置各种样式,不过好在有大神封装了xlsx-style这个库,可以让我们白嫖xlsx付费版的功能。

        不过现在xlsx-style这个库现在已经没有人维护了,且xlsx-style不支持修改行高,想要修改行高的话需要修改源码,所以我们现在选择xlsx-style-fixedver这个库,该库对xlsx-style进行了修复,默认支持修改行高。注意:使用xlsx-style-fixedver时必须要设置行高,不然会报错!!!

三、可能遇到的问题

        首先确定下库的版本:

New Image

1、Module not found: Error: Can''t resolve ''fs''

New Image

在vue.config.js中添加如下代码

  1. configureWebpack: {
  2. externals: {
  3. fs: require(''fs'')
  4. },
  5. }

2、jszip is not a constructor

New Image

首先安装下面这个库

npm install node-polyfill-webpack-plugin

在vue.config.js中添加如下代码

  1. const NodePolyfillPlugin = require(''node-polyfill-webpack-plugin'')
  2. configureWebpack: {
  3. plugins: [new NodePolyfillPlugin()]
  4. }

最终vue.config.js代码如下:

  1. const NodePolyfillPlugin = require(''node-polyfill-webpack-plugin'')
  2. module.exports = {
  3. publicPath: ''./'',
  4. lintOnSave: false,
  5. configureWebpack: {
  6. externals: {
  7. fs: require(''fs'')
  8. },
  9. plugins: [new NodePolyfillPlugin()]
  10. }
  11. }

四、封装方法

代码都有注释,且本身也比较简单,应该都是能看懂的

  1. import * as XLSX from ''xlsx/xlsx.mjs''
  2. import XLSXStyle from ''xlsx-style-fixedver''
  3. /**
  4. * 将 String 转换成 ArrayBuffer
  5. * @method 类型转换
  6. * @param {String} [s] wordBook内容
  7. * @return {Array} 二进制流数组
  8. */
  9. function s2ab(s) {
  10. let buf = null
  11. if (typeof ArrayBuffer !== ''undefined'') {
  12. buf = new ArrayBuffer(s.length)
  13. const view = new Uint8Array(buf)
  14. for (let i = 0; i !== s.length; ++i) {
  15. view[i] = s.charCodeAt(i) & 0xFF
  16. }
  17. return buf
  18. }
  19. buf = new Array(s.length)
  20. for (let i = 0; i !== s.length; ++i) {
  21. // 转换成二进制流
  22. buf[i] = s.charCodeAt(i) & 0xFF
  23. }
  24. return buf
  25. }
  26. /**
  27. * 方案一:利用 URL.createObjectURL 下载 (以下选用)
  28. * 方案二:通过 file-saver 插件实现文件下载
  29. * @method 文件下载
  30. * @param {Object} [obj] 导出内容 Blob 对象
  31. * @param {String} [fileName] 文件名 下载是生成的文件名
  32. * @return {void}
  33. */
  34. function saveAs(obj, fileName) {
  35. const aLink = document.createElement(''a'')
  36. // eslint-disable-next-line eqeqeq
  37. if (typeof obj == ''object'' && obj instanceof Blob) {
  38. aLink.href = URL.createObjectURL(obj) // 创建blob地址
  39. }
  40. aLink.download = fileName
  41. aLink.click()
  42. setTimeout(function() {
  43. URL.revokeObjectURL(obj)
  44. }, 100)
  45. }
  46. /**
  47. * @method 数据导出excel
  48. * @param {Object} [data] 工作表数据内容
  49. * @param {String} [name] 导出excel文件名
  50. * @param {Number} [merges] 表头合并列数
  51. * @param {Boolean} [save] 直接下载或返回bolb文件
  52. */
  53. export function exportExcel(data, name, merges, save = true) {
  54. return new Promise((resolve) => {
  55. let index = 0
  56. // 合并单元格 s:开始位置 e:结束位置 r:行 c:列
  57. const datamerges = [
  58. // 实际情况根据业务需求进行
  59. { s: { c: 0, r: 0 }, e: { c: merges, r: 0 }},
  60. { s: { c: 1, r: data.length + 1 }, e: { c: 3, r: data.length + 1 }}
  61. ]
  62. const worksheet1 = XLSX.utils.json_to_sheet(data, { origin: ''A2'' }) // origin:指定某一行开始导入表格数据
  63. const itemWidth = []
  64. const itemHeight = []
  65. worksheet1[''!merges''] = datamerges
  66. worksheet1.A1 = {
  67. t: ''s'',
  68. v: name
  69. }
  70. const total = `B${data.length + 2}`
  71. worksheet1[total] = {
  72. t: ''s'',
  73. v: ''合计''
  74. }
  75. for (const key in worksheet1) {
  76. index++
  77. // 前两行高度为45,其后行高为25
  78. if (index <= 2) {
  79. itemHeight.push({ hch: 45 })
  80. } else {
  81. itemHeight.push({ hch: 25 })
  82. }
  83. // 所有单元格居中
  84. if (key !== ''!cols'' && key !== ''!merges'' && key !== ''!ref'' && key !== ''!rows'') {
  85. worksheet1[key].s = {
  86. alignment: {
  87. horizontal: ''center'',
  88. vertical: ''center''
  89. }
  90. }
  91. }
  92. // 所有单元格列宽为15
  93. itemWidth.push({ wch: 15 })
  94. // A1单元格加粗居中
  95. if (key === ''A1'') {
  96. worksheet1[key].s = {
  97. font: {
  98. bold: true,
  99. sz: 20
  100. },
  101. alignment: {
  102. horizontal: ''center'',
  103. vertical: ''center''
  104. }
  105. }
  106. }
  107. if (key === total) {
  108. worksheet1[key].s = {
  109. font: {
  110. bold: true
  111. },
  112. alignment: {
  113. horizontal: ''center'',
  114. vertical: ''center''
  115. }
  116. }
  117. }
  118. // 表头加粗居中
  119. if (key.replace(/[^0-9]/ig, '''') === ''2'') {
  120. worksheet1[key].s = {
  121. font: {
  122. bold: true
  123. },
  124. alignment: {
  125. horizontal: ''center'',
  126. vertical: ''center''
  127. }
  128. }
  129. }
  130. }
  131. worksheet1[''!cols''] = itemWidth // 列宽
  132. worksheet1[''!rows''] = itemHeight // 行高
  133. // return
  134. const sheetNames = Object.keys({ ''总表'': worksheet1 })
  135. const workbook = {
  136. SheetNames: sheetNames, // 保存的工作表名
  137. Sheets: { ''总表'': worksheet1 } // 与表名对应的表数据
  138. }
  139. // // excel的配置项
  140. const wopts = {
  141. bookType: ''xlsx'', // 生成的文件类型
  142. bookSST: false, // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性
  143. type: ''binary''
  144. }
  145. // attempts to write the workbook
  146. // const wbout = styleXLSX.write(workbook, wopts)
  147. const wbout = XLSXStyle.write(workbook, wopts)
  148. try {
  149. wbout.then(res => {
  150. const wbBlob = new Blob([s2ab(res)], {
  151. type: ''application/octet-stream''
  152. })
  153. if (save) {
  154. saveAs(wbBlob, name + ''.'' + ''xlsx'')
  155. } else {
  156. resolve(wbBlob)
  157. }
  158. })
  159. } catch (error) {
  160. const wbBlob = new Blob([s2ab(wbout)], {
  161. type: ''application/octet-stream''
  162. })
  163. if (save) {
  164. saveAs(wbBlob, name + ''.'' + ''xlsx'')
  165. } else {
  166. resolve(wbBlob)
  167. }
  168. }
  169. })
  170. }

使用方法:

其中testData根据自己的业务需求进行修改就行了。

  1. <template>
  2. <div>
  3. <span class="btn" @click="createExcel">点击生成Excel</span>
  4. </div>
  5. </template>
  6. <script>
  7. import { exportExcel } from ''@/utils''
  8. export default {
  9. name: ''XlsxDown'',
  10. props: {
  11. msg: String
  12. },
  13. methods: {
  14. createExcel() {
  15. // 测试数据
  16. const testData = [
  17. {
  18. ''序号'': 1,
  19. ''表头1'': 1,
  20. ''表头2'': 2,
  21. ''表头3'': 3,
  22. ''表头4'': 4,
  23. ''表头5'': 5,
  24. },
  25. {
  26. ''序号'': 2,
  27. ''表头1'': 11,
  28. ''表头2'': 22,
  29. ''表头3'': 33,
  30. ''表头4'': 44,
  31. ''表头5'': 55,
  32. },
  33. {
  34. ''序号'': 3,
  35. ''表头1'': 111,
  36. ''表头2'': 222,
  37. ''表头3'': 333,
  38. ''表头4'': 444,
  39. ''表头5'': 555,
  40. },
  41. ]
  42. const temp = {
  43. ''序号'': testData.length + 1
  44. }
  45. temp[''表头4''] = 0
  46. temp[''表头5''] = 0
  47. testData.map((item) => {
  48. temp[''表头4''] += parseFloat(item[''表头4''])
  49. temp[''表头5''] += parseFloat(item[''表头5''])
  50. })
  51. testData.push(temp)
  52. exportExcel(testData, ''Excel下载'', Object.keys(testData[0]).length - 1)
  53. }
  54. }
  55. }
  56. </script>
  57. <!-- Add "scoped" attribute to limit CSS to this component only -->
  58. <style scoped>
  59. .btn {
  60. border: 1px solid rgba(30,128,255,.3);
  61. background-color: rgba(30,128,255,.05);
  62. border-radius: 4px;
  63. cursor: pointer;
  64. padding: 10px;
  65. color: #1e80ff;
  66. font-size: 14px;
  67. }
  68. </style>