表格数据导入

读取导入Excel表格数据这里采用的是 xlsx 插件

  1. npm i xlsx

读取excel需要通过 XLSX.read(data, {type: type}) 方法来实现,返回一个叫WorkBook的对象,type主要取值如下:

  • base64: 以base64方式读取;
  • binary: BinaryString格式(byte n is data.charCodeAt(n))
  • string: UTF8编码的字符串;
  • buffer: nodejs Buffer;
  • array: Uint8Array,8位无符号数组;
  • file: 文件的路径(仅nodejs下支持);

同时需要用到插件自身的工具类XLSX.utils来对worksheet进行解析

  • XLSX.utils.sheet_to_csv:生成CSV格式
  • XLSX.utils.sheet_to_txt:生成纯文本格式
  • XLSX.utils.sheet_to_html:生成HTML格式
  • XLSX.utils.sheet_to_json:输出JSON格式

下面用一个 vue 的组件来演示一下

  1. <template>
  2. <div class="read_excel_file">
  3. <slot></slot>
  4. <input
  5. class="file-input"
  6. ref="readexcel_input"
  7. type="file"
  8. :accept="SheetJSFT"
  9. @change="change"
  10. />
  11. </div>
  12. </template>
  13. <script>
  14. import XLSX from 'xlsx'
  15. export default {
  16. data() {
  17. return {
  18. SheetJSFT: '.xlsx',
  19. }
  20. },
  21. mounted() {
  22. // 绑定插槽点击触发导入文件
  23. if (this.$slots && this.$slots.default && this.$slots.default.length > 0) {
  24. this.$slots.default[0].elm.addEventListener(
  25. 'click',
  26. this.openExcel.bind(this),
  27. )
  28. }
  29. },
  30. methods: {
  31. // 点击打开导入excel
  32. openExcel() {
  33. let uploadBtn = this.$refs['readexcel_input']
  34. uploadBtn.click()
  35. },
  36. // 文件导入时
  37. change(evt) {
  38. const files = evt.target.files
  39. if (!/\.xlsx$/.test(files[0].name)) {
  40. this.$emit('validate', false)
  41. this.$emit('file-change', {
  42. name: files[0].name,
  43. })
  44. console.error('请选择xlsx格式文件')
  45. return false
  46. } else {
  47. this.$emit('validate', true)
  48. }
  49. if (files && files[0]) this.file(files[0])
  50. },
  51. // 数据读取
  52. file(file) {
  53. const reader = new FileReader()
  54. reader.onload = e => {
  55. const bstr = e.target.result
  56. const wb = XLSX.read(bstr, { type: 'binary' }) // 这里使用type为binary
  57. const wsname = wb.SheetNames[0]
  58. const ws = wb.Sheets[wsname]
  59. const data = XLSX.utils.sheet_to_json(ws, {
  60. header: 1,
  61. // blankrows: false,
  62. }) // 读取json格式
  63. this.formatData(data, file.name, ws['!merges'])
  64. }
  65. reader.readAsBinaryString(file)
  66. },
  67. // 数据格式化
  68. formatData(list, name, merges) {
  69. let arr = []
  70. for (let i = 1; i < list.length; i++) {
  71. if (list[i].length > 0) {
  72. let obj = {}
  73. list[i].map((v, j) => {
  74. obj[list[0][j]] = v
  75. })
  76. arr.push(obj)
  77. }
  78. }
  79. this.$emit('file-change', {
  80. header: list[0],
  81. body: arr,
  82. name: name,
  83. merges: merges,
  84. })
  85. // 必须清空input的value属性,不然第二次选择同样的文件,不会触发change事件。
  86. this.$refs['readexcel_input'].value = ''
  87. },
  88. },
  89. }
  90. </script>
  91. <style lang="scss" scoped>
  92. .read_excel_file {
  93. display: inline-block;
  94. .file-input {
  95. width: 0;
  96. height: 0;
  97. }
  98. }
  99. </style>

组件的具体使用如下

  1. <template>
  2. <div class="cs">
  3. <ReadExcel @file-change="excelFileChange">
  4. <div class="import-button">导入</div>
  5. </ReadExcel>
  6. </div>
  7. </template>
  8. <script>
  9. import ReadExcel from './components/ReadExcel.vue'
  10. export default {
  11. components: {
  12. ReadExcel,
  13. },
  14. methods: {
  15. excelFileChange(data) {
  16. let { merges, body, name, header } = data
  17. console.log(merges, body, name, header)
  18. },
  19. },
  20. }
  21. </script>

接下来,我们尝试导入以下的表格

导出的数据(merges, body, name, header)如下

如此,便拿到了表格中的数据

导出excel表格

这里展示两种导出表格的方法

1、xlsx插件导出

这里还是用到xlsx插件

这里直接做一个小demo展示

  1. function exportExcel(header, body, merges, wscols, wsrows, fileName) {
  2. body.unshift(header)
  3. const ws = XLSX.utils.aoa_to_sheet(body)
  4. const wb = XLSX.utils.book_new()
  5. ws['!cols'] = wscols
  6. ws['!rows'] = wsrows
  7. ws['!merges'] = merges
  8. XLSX.utils.book_append_sheet(wb, ws, 'Sheet1')
  9. // 生成excel
  10. XLSX.writeFile(wb, `${fileName}.xlsx`)
  11. }
  12. // 设置表格头部
  13. let header = [
  14. '*手机号码',
  15. '*订单编号',
  16. '*商品编号',
  17. '*数量',
  18. '*订单支付类型',
  19. '*订单总金额¥',
  20. '*订单实付金额¥',
  21. ]
  22. // 设置表格数据
  23. let body = [
  24. ['18600000002', 'svp000002', 'sp002', '1', '微信支付', '1100', '1100'],
  25. [undefined, undefined, 'sp003', '2', undefined, undefined, undefined],
  26. [undefined, undefined, 'sp004', '1', undefined, undefined, undefined],
  27. ]
  28. // 设置合并单元格
  29. let merges = [
  30. { e: { c: 0, r: 3 }, s: { c: 0, r: 1 } },
  31. { e: { c: 1, r: 3 }, s: { c: 1, r: 1 } },
  32. { e: { c: 4, r: 3 }, s: { c: 4, r: 1 } },
  33. { e: { c: 5, r: 3 }, s: { c: 5, r: 1 } },
  34. { e: { c: 6, r: 3 }, s: { c: 6, r: 1 } },
  35. ]
  36. // 指定每一列的宽度
  37. let wscols = [
  38. { wch: 20 },
  39. { wch: 20 },
  40. { wch: 30 },
  41. { wch: 30 },
  42. { wch: 30 },
  43. { wch: 30 },
  44. { wch: 30 },
  45. ]
  46. // 指定每一行的高度
  47. let wsrows = [{ hpx: 20 }]
  48. exportExcel(header, body, merges, wscols, wsrows, '生成excel文件')

导出结果如下

导出是导出成功,但是只有光秃秃的数据

那么这里如果要设置单元格的样式该怎么做呢?

如果单纯使用xlsx插件是无法设置单元格的样式的,似乎有个xlsx的pro专业版可以做到,但是是收费的; 也有使用免费的xlsx-style实现设置样式

  1. npm i xlsx-style

用xlsx-style确实可以实现设置样式,但是像我们一开始导入的excel文件中的表头中(如下图),有一个单元格中存在两种不同颜色的文本的情况,

这种情况下,笔者用xlsx-style也实现不了,所以这里不详诉xlsx-style的使用方法,我们试下第二种导出excel的方法

2、使用html table标签导出excel

这里是用table标签直接生成excel文件

直接上代码

  1. function tableHtmlCompute(str) {
  2. return (
  3. "<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:x='urn:schemas-microsoft-com:office:excel'><head><!--[if gte mso 9]><xml>" +
  4. '<x:ExcelWorkbook>' +
  5. '<x:ExcelWorksheets>' +
  6. '<x:ExcelWorksheet>' +
  7. '<x:WorksheetOptions><x:Print><x:ValidPrinterInfo /></x:Print></x:WorksheetOptions>' +
  8. '</x:ExcelWorksheet>' +
  9. '</x:ExcelWorksheets>' +
  10. '</x:ExcelWorkbook></xml><![endif]--> ' +
  11. '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head><body>' +
  12. '<table border="1" cellspacing="1" cellpadding="1">' +
  13. '<tr>' + // 这里是表头
  14. '<td style="background:rgb(217,225,242);mso-number-format:@"><span style="color: rgb(255,0,0)">*</span><span>手机号码</span></td>' +
  15. '<td style="background:rgb(217,225,242);mso-number-format:@"><span style="color: rgb(255,0,0)">*</span><span>订单编号</span></td>' +
  16. '<td style="background:rgb(217,225,242);mso-number-format:@"><span style="color: rgb(255,0,0)">*</span><span>商品编号</span></td>' +
  17. '<td style="background:rgb(217,225,242);mso-number-format:@"><span style="color: rgb(255,0,0)">*</span><span>数量</span></td>' +
  18. '<td style="background:rgb(217,225,242);mso-number-format:@"><span style="color: rgb(255,0,0)">*</span><span>订单支付类型</span></td>' +
  19. '<td style="background:rgb(217,225,242);mso-number-format:@"><span style="color: rgb(255,0,0)">*</span><span>订单总金额¥</span></td>' +
  20. '<td style="background:rgb(217,225,242);mso-number-format:@"><span style="color: rgb(255,0,0)">*</span><span>订单实付金额¥</span></td>' +
  21. '</tr>' +
  22. '<tr>' +
  23. '<td style="background:rgb(217,225,242);mso-number-format:@"><span style="color: rgb(255,0,0)">必填项</span><br style="mso-data-placement:same-cell"/><span>1、学员购买商品收货填写的手机号</span></td>' +
  24. '<td style="background:rgb(217,225,242);mso-number-format:@"><span style="color: rgb(255,0,0)">必填项</span><br style="mso-data-placement:same-cell"/><span>1、填写导入的订单在售卖平台的订单编号</span></td>' +
  25. '<td style="background:rgb(217,225,242);mso-number-format:@"><span style="color: rgb(255,0,0)">必填项</span><br style="mso-data-placement:same-cell"/><span>1、上架商品时候填写的“售卖平台商品编号”</span></td>' +
  26. '<td style="background:rgb(217,225,242);mso-number-format:@"><span style="color: rgb(255,0,0)">必填项</span><br style="mso-data-placement:same-cell"/><span>1、购买商品的总数量</span><br style="mso-data-placement:same-cell"/><span>2、填写的数量>=1,且为整数。</span></td>' +
  27. '<td style="background:rgb(217,225,242);mso-number-format:@"><span style="color: rgb(255,0,0)">必填+单选</span><br style="mso-data-placement:same-cell"/><span>1、选项</span><br style="mso-data-placement:same-cell"/><span>微信支付</span><br style="mso-data-placement:same-cell"/><span>支付宝支付</span><br style="mso-data-placement:same-cell"/><span>网银支付</span><br style="mso-data-placement:same-cell"/><span>pos机支付</span><br style="mso-data-placement:same-cell"/><span>银行汇款</span></td>' +
  28. '<td style="background:rgb(217,225,242);mso-number-format:@"><span style="color: rgb(255,0,0)">必填项</span></td>' +
  29. '<td style="background:rgb(217,225,242);mso-number-format:@"><span style="color: rgb(255,0,0)">必填项</span></td>' +
  30. '</tr>' +
  31. str +
  32. '</table></body></html>'
  33. )
  34. }
  35. function excelExport(str) {
  36. let html = tableHtmlCompute(str)
  37. let blob = new Blob([html], {
  38. type: 'text/plain;charset=utf-8',
  39. })
  40. //解决中文乱码问题
  41. blob = new Blob([String.fromCharCode(0xfeff), blob], {
  42. type: blob.type,
  43. })
  44. let a = document.createElement('a')
  45. a.style.display = 'none'
  46. // 利用URL.createObjectURL()方法为 a 元素生成 blob URL
  47. a.href = URL.createObjectURL(blob)
  48. // 设置文件名
  49. a.download = 'excel名字.xlsx'
  50. document.body.appendChild(a)
  51. a.click()
  52. document.body.removeChild(a)
  53. }
  54. let body = [
  55. {
  56. mobile: '18600000002',
  57. orderNumber: 'svp000002',
  58. products: [
  59. {
  60. productNumber: 'sp002',
  61. quantity: 1,
  62. },
  63. {
  64. productNumber: 'sp003',
  65. quantity: 2,
  66. },
  67. {
  68. productNumber: 'sp004',
  69. quantity: 1,
  70. },
  71. ],
  72. paymentType: '微信支付',
  73. total: 1100,
  74. paymenteds: 1100,
  75. },
  76. ]
  77. let header = [
  78. 'mobile',
  79. 'orderNumber',
  80. 'productNumber',
  81. 'quantity',
  82. 'paymentType',
  83. 'total',
  84. 'paymenteds',
  85. ]
  86. let productsHeader = ['productNumber', 'quantity']
  87. let html = ''
  88. body.forEach(e => {
  89. if (e.products && e.products.length) {
  90. e.products.forEach((item, i) => {
  91. let str = '<tr>'
  92. if (i) {
  93. productsHeader.forEach(key => {
  94. str += `<td style="mso-number-format:\\@"><span>${
  95. item[key] ? item[key] : ''
  96. }</span></td>`
  97. })
  98. str += '</tr>'
  99. } else {
  100. // i===0;每一条数据的头部
  101. header.forEach(key => {
  102. if (productsHeader.includes(key)) {
  103. str += `<td style="mso-number-format:\\@"><span>${
  104. item[key] ? item[key] : ''
  105. }</span></td>`
  106. } else {
  107. let val = e[key] ? e[key] : ''
  108. str += `<td style="mso-number-format:\\@" rowspan=${e.products.length}><span>${val}</span></td>`
  109. }
  110. })
  111. str += '</tr>'
  112. }
  113. html += str
  114. })
  115. }
  116. })
  117. excelExport(html)

导出结果如下

这里有以下几个注意点

1、上面有展示了如何在同一单元格内换行,如果需要在同一单元格内换行,需要用到以下代码

  1. <br style="mso-data-placement:same-cell"/>

如果单纯只写br标签,不加后面的style="mso-data-placement:same-cell", 则它会出现两个单元格合并在一起的情况

2、style="mso-number-format:\@" 这个样式可以让单元格的格式被识别为文本,避免不必要的格式自动转换

前端Excel表格导入导出,包括合并单元格,表格自定义样式等的更多相关文章

  1. 在Asp.Net MVC中使用NPOI插件实现对Excel的操作(导入,导出,合并单元格,设置样式,输入公式)

    前言 NPOI 是 POI 项目的.NET版本,它不使用 Office COM 组件,不需要安装 Microsoft Office,目前支持 Office 2003 和 2007 版本. 1.整个Ex ...

  2. Java导出Excel表,POI 实现合并单元格以及列自适应宽度(转载)

    POI是apache提供的一个读写Excel文档的开源组件,在操作excel时常要合并单元格,合并单元格的方法是: sheet.addMergedRegion(new CellRangeAddress ...

  3. NPOI操作EXCEL(五)——含合并单元格复杂表头的EXCEL解析

    我们在第三篇文章中谈到了那些非常反人类的excel模板,博主为了养家糊口,也玩命做出了相应的解析方法... 我们先来看看第一类复杂表头: ...... 博主称这类excel模板为略复杂表头模板(蓝色部 ...

  4. NPOI之Excel——合并单元格、设置样式、输入公式

    首先建立一个空白的工作簿用作测试,并在其中建立空白工作表,在表中建立空白行,在行中建立单元格,并填入内容: //建立空白工作簿 IWorkbook workbook = new HSSFWorkboo ...

  5. NPOI之Excel——合并单元格、设置样式、输入公式、设置筛选等

    首先建立一个空白的工作簿用作测试,并在其中建立空白工作表,在表中建立空白行,在行中建立单元格,并填入内容: //建立空白工作簿 IWorkbook workbook = new HSSFWorkboo ...

  6. .Net用字符串拼接实现表格数据相同时合并单元格

    前言 最近在做项目通过GridView或Repeater绑定数据,如果两行或若干行某列值相同,需要进行合并单元格,但是实现过程中想到了字符串拼接,于是就没用绑定数据控件,而是用了html结合字符串实现 ...

  7. C#导出带有格式的Excel(列宽,合并单元格,显示边框线,加背景颜色等)

    源地址:http://blog.sina.com.cn/s/blog_74f702e60101au55.html 导出excel相关设置:http://blog.csdn.net/wanmingtom ...

  8. php实现导出数据分类合并单元格功能

    <?php $conn = mysql_connect("localhost","root","root"); $db = mysql ...

  9. 复杂的POI导出Excel表格(多行表头、合并单元格)

    poi导出excel有两种方式: 第一种:从无到有的创建整个excel,通过HSSFWorkbook,HSSFSheet HSSFCell, 等对象一步一步的创建出工作簿,sheet,和单元格,并添加 ...

随机推荐

  1. Windows下的Linux子系统

    强调!!!必须是Windows专业版!!! 一.安装运行过程 第一步:打开开发人员模式 第二步:进入 '控制面板 '--'程序'--'启用的Windows功能'--勾选Linux子系统(根据提示进行重 ...

  2. python基础学习之函数进阶【匿名函数、作用域关系、闭包、递归】

    匿名函数 lambda的用法: lambda x:x+1 解释,同等于以下函数 def test(x): return x+1 因为没有函数名,所以称为匿名函数 只适用于简易的逻辑,复杂逻辑无法实现 ...

  3. 策略模式在PHP业务代码的实践

    [大话设计模式]-- 策略者模式(Strategy):它定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变法,不会影响到使用算法的客户. 策略模式的核心就是屏蔽内部策略算法,内部的 ...

  4. es6 模块和commonjs规范模块的区别

    相关代码地址:https://github.com/blank-x/blog-code/tree/main/1-module 引入变量 es6 导入变量只是一个符号链接,是个常量,类似于const 声 ...

  5. python网络编程TCP服务多客户端的服务端开发

    #服务多客户端TCP服务端开发 2 #方法说明 3 """ 4 bind(host,port)表示绑定端口号,host是ip地址,ip地址一般不进 行绑定,表示本机的任何 ...

  6. LookupError: 'hex' is not a text encoding; use codecs.decode() to handle arbitrary codecs

    问题代码: b=b'\x01\x02\x03' x=binascii.b2a_hex(b.decode('hex')[::-1].encode('hex')) python2下是不报错的,因为pyth ...

  7. Macbook 安装kali linux 双系统 2020.3 超详细

    博主折腾了一星期这东西,到现在都还有些坑没解决(最后面会讲).不过最起码系统装上了,可以用了,看到这桌面惊艳了,再点下左上角表示人间值得. 其实我是装了windos 10.macos 和kali三系统 ...

  8. Etcd常用运维命令

    目录 常用命令 常见操作 如何缩容? 如何扩容? 数据目录丢失或被误删除,节点启动失败或者加入集群报错? 操作步骤 操作步骤不正确的各种常见错误日志 常用命令 #查看集群member情况 etcdct ...

  9. SSL证书详解和CFSSL工具使用

    公钥基础设施(PKI) 基础概念 CA(Certification Authority)证书,指的是权威机构给我们颁发的证书. 密钥就是用来加解密用的文件或者字符串.密钥在非对称加密的领域里,指的是私 ...

  10. 拇指记者深入Android公司,打探事件分发机制背后的秘密

    前言 聊到事件分发,很多朋友就会想到view的dispatchTouchEvent,其实在此之前,Android还做了很多工作. 比如跨进程获取输入事件的方式?在dispatchTouchEvent责 ...