草庐IT

基于vue 实现 excel导出导入功能

小羊卷 2023-11-16 原文

第一步下包

excel导入导出功能需要我们下载哪些包?

在vue-element-admin中有着详细的文档查看:

Excel 导出

Excel 的导入导出都是依赖于js-xlsx来实现的。

js-xlsx的基础上又封装了Export2Excel.js来方便导出数据。

#使用

由于 Export2Excel不仅依赖js-xlsx还依赖file-saverscript-loader

所以我们需要下载三个包,和一个js文件 Export2Excel

npm install xlsx file-saver -S
npm install script-loader -S -D

第二步:各种功能的实现

1.1:基本表头导出功能

1.2:复杂表头导出功能

1.3:excel表格导入功能

1.1.1:excel导出逻辑

逆向思维思考:

1-最终要拿到的是一个充满数据的文件

2-如何导出一个文件:用到excel.export_json_to_excel

3-export_json_to_excel方法需要那些参数:

	multiHeader:// 多级表头

​    header:, //表头 必填

​     data: //具体数据 必填

​     filename: //表格名字

​     merges: //合并单元格

​     autoWidth: true, // 是否自适应宽度

​     bookType: 'xlsx', //导出格式

4-了解参数的数据类型,接下来将数据转成能够导出的数据类型,也就是处理数据

5-先有数据才能处理数据,所以应该是要拿数据

到现在导出的逻辑已经清楚,那么开始正向梳理一下逻辑

1-发请求拿数据

2-定义方法处理数据

3-调用export_json_to_excel方法导出表格

4-查看导出的表格是否满意

接下来我将用后台人资项目来演示基本的数据导出excel表格

// 1- 获取数据并且处理完的数据
    async onExportFn() {
      // 1-请求获取所有的数据
      // {data} 是对请求拿到的数据进行结构
      const { data } = await FindStaffList({ page: 1, size: this.total })
      // 1-1 此时list里面保存的是所有的员工列表信息
      const list = data.rows
      console.log(list)
      // 2-2 对数据进行处理 此时FilnalData 拿到的就我们要的数据格式  
      // 此处我封装成了一个函数 onSortList 并且将拿到的list数据传入该方法
      return this.onSortList(list)
    },
    

注意一点在这里需要data的数据类型是一个二维数组[[],[],[],....[]] 在vue-element-admin 中有详细的文档,搜索“导出” 或者搜索xlsx即可

 // 2-2 对数据进行处理  在这里只是讲获取数据的方法,这是最简单的一种,也可以用map对数据进行处理,方法不一,只列出最简单易理解的

onSortList(list) {
      // 2-1 先定义排列顺序  这里通常有一个学术名叫做对照表
      const sortOrder = [
        'username',
        'mobile',
        'timeOfEntry',
        'formOfEmployment',
        'correctionTime',
        'workNumber',
        'departmentName',
      ]
      // FilnalData 用来存放最后的数据
      const FilnalData = []
      list.forEach((item) => {
        const arr = []
        sortOrder.forEach((obj) => {
          if (obj === 'formOfEmployment') {
            arr.push(item[obj] === 1 ? '正式' : '非正式')
          } else {
            arr.push(item[obj])
          }
        })
        FilnalData.push(arr)
      })
      return FilnalData
    },
// 3- 此时数据已经处理完毕,那么就可以调用export_json_to_excel方法来进行导出表格
async onOrdinaryExport() {
      this.ExportLoading = true
      // onExportFn() 这里是一个primise异步方法,所以要用await
      const FilnalData = await this.onExportFn()
      //  import('@/vendor/Export2Excel') 懒加载  导入后,拿到excel对象 的对象中有export_json_to_excel 方法
      import('@/vendor/Export2Excel').then((excel) => {
        excel.export_json_to_excel({
          header: [
        '姓名',
        '手机号',
        '入职日期',
        '聘用形式',
        '转正日期',
        '工号',
        '部门',
      ], //表头 必填
          data: FilnalData, //具体数据 必填
          filename: '员工信息表格',
          autoWidth: true,
          bookType: 'xlsx',
        })
      })
      this.ExportLoading = false
    },

如上操作,即可完美导出一份普通的带有表头的表格

1.2.1复杂表头导出功能

复杂表头和基本表头的导出,思考的逻辑是一样的,所以不再赘述

复杂表头的导出,相比基本表头导出,只有在export_json_to_excel中传递的参数多了两个 一个是 multiHeader(多级表头),一个是merges(合并)

通过分析export_json_to_excel.js文件的源代码,我们可以分析出,multiHeader是二维数组,merges是一维数组,数组中的内容是字符串,而且是带有:的字符串

所以,根据要求,我们可以自由合并单元格

filename = filename || 'excel-list'
  data = [...data]
  data.unshift(header)

  for (let i = multiHeader.length - 1; i > -1; i--) {
    data.unshift(multiHeader[i])
  }

部分源码如图。

data中的数据,首先会先把传过来的数据data解构后放入data中,然后对多级表头进行循环,循环结束,将每个数据push到data中,因为data中的数据是个数组中放的每一个元素是数组

所以multiHeader[i]是数组,所以multiHeader 是二维数组

复杂表头代码如下,与一般表头只有在导出的时候有差别

async onComplexExport() {
      this.ComplexExportLoading = true
      // 调用onExportFn方法获取处理完成后的数据  该方法,上面有
      const FilnalData = await this.onExportFn()
      
      import('@/vendor/Export2Excel').then((excel) => {
        excel.export_json_to_excel({
          multiHeader: [['姓名', '主要信息', '', '', '', '', '部门']],
          header: [
        'username',
        'mobile',
        'timeOfEntry',
        'formOfEmployment',
        'correctionTime',
        'workNumber',
        'departmentName',
      ], //表头 必填
          data: FilnalData, //具体数据 必填
          filename: '复杂表头的员工信息表格',
          // 要合并的单元格
          merges: ['A1:A2', 'B1:F1', 'G1:G2'],
          autoWidth: true,
          bookType: 'xlsx',
        })
      })
      this.ComplexExportLoading = false
    },

1.3.1:excel表格导入功能

Excel 导入

封装了UploadExcelExcel 导入组件,支持点击和拖拽上传,同样它也是依赖js-xlsx的。

所以要实现导入功能

需要下载三个包,和一个vueUploadExcel组件

npm install xlsx file-saver -S
npm install script-loader -S -D

UploadExcel它提供了两个回调函数:beforeUpload:你可以在上传之前做一些自己的特殊判断,如判断文件的大小是否大于 1 兆?若大于 1 兆则停止解析并提示错误信息。

  • onSuccess 解析成功时候会触发的回调函数,它会返回表格的表头和内容。

  • handleSuccess({ results, header }) {
          this.tableData = results
          this.tableHeader = header
        }

    导入的功能,总体来说,要比导出简单一些,更加容易理解一下

    总结思路

    反向思考

    1-最终要实现的效果,给当前页面添加几行数据

    2-要实现给当前页面添加几行数据,并且是我们看的到数据,因为只有数据才能改变视图,所以我们一定要发请求,才能够将数据添加上

    3--发请求,必然要牵扯到请求的参数,那么就要看参数是什么格式的

    4-有了参数的格式,那么我们就需要将一个方法,用来将我们得到的数据,转变成参数需要的数据类型

    5-要转变,首先要先拿到表格里面的数据,所以这就需要用到我们的UploadExcel组件给我们提供的一个方法onSuccess

    到现在导出的逻辑已经清楚,那么开始正向梳理一下逻辑

    1-下载组件,在页面中导入,使用onSuccess方法,拿到表格的数据 tableData

    2-定义一个方法,对拿到的数据进行处理,将数据处理成我们发请求需要的数据

    3-定义接口,导入接口,发请求

    4-调用接口,重新获取数据,渲染视图

    逻辑清楚,代码开始

    以下是我对UploadExcel组件的一些源码的分析与整理,只有简单的标注和样式的修改

    <template>
      <div class="upload-excel">
        <div class="btn-upload">
          <el-button
            :loading="loading"
            size="mini"
            type="primary"
            @click="handleUpload"
          >
            点击上传
          </el-button>
        </div>
        <!-- 隐藏域 -->
        <!-- 2-监听表单的change事件,点击了button按钮,触发 表单点击事件  当选中了文件后,会触发change事件 -->
        <input
          ref="excel-upload-input"
          class="excel-upload-input"
          type="file"
          accept=".xlsx, .xls"
          @change="handleClick"
        />
        <div
          class="drop"
          @drop="handleDrop"
          @dragover="handleDragover"
          @dragenter="handleDragover"
        >
          <i class="el-icon-upload" />
          <span>将文件拖到此处</span>
        </div>
      </div>
    </template>
    
    <script>
    import * as XLSX from 'xlsx'
    
    export default {
      props: {
        beforeUpload: Function, // eslint-disable-line
        onSuccess: Function, // eslint-disable-line
      },
      data() {
        return {
          loading: false,
          excelData: {
            header: null,
            results: null,
          },
        }
      },
      methods: {
        // 8-1 generateData  方法,接收两个参数,一个是处理完的表格头数据,一个是表格内容数据
        generateData({ header, results }) {
          this.excelData.header = header
          this.excelData.results = results
          // 8-2 数据处理完成,进入逻辑判断,父传入onSuccess方法  则调用方法,并且传入读取后的表格数据
          this.onSuccess && this.onSuccess(this.excelData)
        },
        handleDrop(e) {
          e.stopPropagation()
          e.preventDefault()
          if (this.loading) return
          const files = e.dataTransfer.files
          if (files.length !== 1) {
            this.$message.error('Only support uploading one file!')
            return
          }
          const rawFile = files[0] // only use files[0]
    
          if (!this.isExcel(rawFile)) {
            this.$message.error(
              'Only supports upload .xlsx, .xls, .csv suffix files'
            )
            return false
          }
          this.upload(rawFile)
          e.stopPropagation()
          e.preventDefault()
        },
        handleDragover(e) {
          e.stopPropagation()
          e.preventDefault()
          e.dataTransfer.dropEffect = 'copy'
        },
        handleUpload() {
          // 1- 当点击button按钮的时候触发表单的点击事件
          this.$refs['excel-upload-input'].click()
        },
        handleClick(e) {
          // 3-1 选中的文件的数组
          const files = e.target.files
          //3-2 拿到具体的文件
          const rawFile = files[0]
          if (!rawFile) return
          // 3-3 触发upload方法  同时把当前拿到的文件传过去
          this.upload(rawFile)
        },
        upload(rawFile) {
          // 4-1 参数rawFile是当前选中的文件   表单清空
          this.$refs['excel-upload-input'].value = null
          // 4-2 this.beforeUpload prop传入的方法  表示加载前 有没有选中文件
          if (!this.beforeUpload) {
            this.readerData(rawFile)
            return
          }
          // 4-3 将选中的文件,传给父 父可以做一些拦截操作,可以限制文件的大小,类型等等 如果不满足条件返回false  默认返回true
          const before = this.beforeUpload(rawFile)
          if (before) {
            this.readerData(rawFile)
          }
        },
        // 5-1 满足条件,进入readerData  rawFile是当前选中的文件
        readerData(rawFile) {
          this.loading = true
          return new Promise((resolve, reject) => {
            const reader = new FileReader()
            reader.onload = (e) => {
              const data = e.target.result
              const workbook = XLSX.read(data, { type: 'array' })
              const firstSheetName = workbook.SheetNames[0]
              const worksheet = workbook.Sheets[firstSheetName]
              const header = this.getHeaderRow(worksheet)
              const results = XLSX.utils.sheet_to_json(worksheet)
              // 7- 在这一步,确认数据已经处理完成  跳转到新的方法generateData()中,并且把处理完的数据{ header, results } 解构后传给这个方法
              this.generateData({ header, results })
              this.loading = false
              resolve()
            }
            reader.readAsArrayBuffer(rawFile)
          })
        },
        // 6  这一块是处理数据  不用详细深究
        getHeaderRow(sheet) {
          const headers = []
          const range = XLSX.utils.decode_range(sheet['!ref'])
          let C
          const R = range.s.r
          /* start in the first row */
          for (C = range.s.c; C <= range.e.c; ++C) {
            /* walk every column in the range */
            const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]
            /* find the cell in the first row */
            let hdr = 'UNKNOWN ' + C // <-- replace with your desired default
            if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
            headers.push(hdr)
          }
          return headers
        },
        isExcel(file) {
          return /\.(xlsx|xls|csv)$/.test(file.name)
        },
      },
    }
    </script>
    
    <style scoped lang="scss">
    .upload-excel {
      display: flex;
      justify-content: center;
      margin-top: 100px;
      .excel-upload-input {
        display: none;
        z-index: -9999;
      }
      .btn-upload,
      .drop {
        border: 1px dashed #bbb;
        width: 350px;
        height: 160px;
        text-align: center;
        line-height: 160px;
      }
      .drop {
        line-height: 80px;
        color: #bbb;
        i {
          font-size: 60px;
          display: block;
        }
      }
    }
    </style>
    

  • 接受onSuccess回调函数中,对数据进行处理

onSuccess(excelData) {
      // console.log(excelData)  在这里打印的是除了表头之外的数据 也就是我们需要的数据
      // 定义一个数据对照表
      const contrastObj = {
        入职日期: 'timeOfEntry',
        姓名: 'username',
        手机号: 'mobile',
        工号: 'workNumber',
        转正日期: 'correctionTime',
      }
      // 定义一个空数组,用来保存最后需要发请求的数据
      const excelList = []
      // 对拿到的数据进行遍历
      excelData.results.forEach((item) => {
        const obj = {}
        for (const key in item) {
          if (key === '转正日期' || key === '入职日期') {
            obj[contrastObj[key]] = formatDate(item[key], '-')
          } else {
            obj[contrastObj[key]] = item[key]
          }
        }
        excelList.push(obj)
      })
      // console.log(excelList)
      // 在这里已经完成数据的整理,调接口,发送请求
    },

至此一个完整体系 的 基于vue的表格的导入导出总结完毕,学习愉快

有关基于vue 实现 excel导出导入功能的更多相关文章

  1. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  2. ruby-on-rails - Cucumber 是否只是 rspec 的包装器以帮助将测试组织成功能? - 2

    只是想确保我理解了事情。据我目前收集到的信息,Cucumber只是一个“包装器”,或者是一种通过将事物分类为功能和步骤来组织测试的好方法,其中实际的单元测试处于步骤阶段。它允许您根据事物的工作方式组织您的测试。对吗? 最佳答案 有点。它是一种组织测试的方式,但不仅如此。它的行为就像最初的Rails集成测试一样,但更易于使用。这里最大的好处是您的session在整个Scenario中保持透明。关于Cucumber的另一件事是您(应该)从使用您的代码的浏览器或客户端的角度进行测试。如果您愿意,您可以使用步骤来构建对象和设置状态,但通常您

  3. ruby - 检查是否通过 require 执行或导入了 Ruby 程序 - 2

    如何检查Ruby文件是否是通过“require”或“load”导入的,而不是简单地从命令行执行的?例如:foo.rb的内容:puts"Hello"bar.rb的内容require'foo'输出:$./foo.rbHello$./bar.rbHello基本上,我想调用bar.rb以不执行puts调用。 最佳答案 将foo.rb改为:if__FILE__==$0puts"Hello"end检查__FILE__-当前ruby​​文件的名称-与$0-正在运行的脚本的名称。 关于ruby-检查是否

  4. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  5. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  6. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  7. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  8. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  9. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  10. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

随机推荐