前端 - excel导入 / 导出功能

article/2025/9/8 14:08:39

1. 导入功能

1.1 前端主导(工作大量在前端)

  • 上传excel文件,把excel文件的内容读出来,还原成最基本的行列结构,按后端的接口要求回传过去。
    在这里插入图片描述
  • 前端读excel文件,调接口

1.2 后端主导(工作大量在后端)

在这里插入图片描述

  • 前端上传excel文件

1.3 实现

1. 安装必要插件

这个插件叫做xlsx

npm install xlsx -S
//或者
yarn add xlsx -S

2. 引入UploadExcel组件并注册全局

  • UploadExcel组件
    • UploadExcel/index.vue
<template><div><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">把excel文件拖到这里<el-button :loading="loading" style="margin-left:16px;" size="mini" type="primary" @click="handleUpload">浏览</el-button></div></div>
</template><script>
import XLSX from 'xlsx'export default {props: {beforeUpload: Function, // eslint-disable-lineonSuccess: Function// eslint-disable-line},data() {return {loading: false,excelData: {header: null,results: null}}},methods: {generateData({ header, results }) {this.excelData.header = headerthis.excelData.results = resultsthis.onSuccess && this.onSuccess(this.excelData)},handleDrop(e) {e.stopPropagation()e.preventDefault()if (this.loading) returnconst files = e.dataTransfer.filesif (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() {this.$refs['excel-upload-input'].click()},handleClick(e) {const files = e.target.filesconst rawFile = files[0] // only use files[0]if (!rawFile) returnthis.upload(rawFile)},upload(rawFile) {this.$refs['excel-upload-input'].value = null // fix can't select the same excelif (!this.beforeUpload) {this.readerData(rawFile)return}const before = this.beforeUpload(rawFile)if (before) {this.readerData(rawFile)}},readerData(rawFile) {this.loading = truereturn new Promise((resolve, reject) => {const reader = new FileReader()reader.onload = e => {const data = e.target.resultconst 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)this.generateData({ header, results })this.loading = falseresolve()}reader.readAsArrayBuffer(rawFile)})},getHeaderRow(sheet) {const headers = []const range = XLSX.utils.decode_range(sheet['!ref'])let Cconst 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 defaultif (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>
.excel-upload-input{display: none;z-index: -9999;
}
.drop{border: 2px dashed #bbb;width: 600px;height: 160px;line-height: 160px;margin: 0 auto;font-size: 24px;border-radius: 5px;text-align: center;color: #bbb;position: relative;
}
</style>

3. 注册

import UploadExcel from './UploadExcel'export default {// 插件的初始化, 插件给你提供的全局的功能, 都可以在这里配置install(Vue) {// 进行组件的全局注册Vue.component('UploadExcel', UploadExcel) // 注册导入excel组件}
}

4. 新建一个公共的导入页面

<template><upload-excel :on-success="handleSuccess" />
</template><script>
export default {name: 'Import',methods: {handleSuccess({ header, results }) {console.log(header, results)}}
}
</script>
  • excel导入插件本质:把excel经过分析转换成js能够识别的常规数据,拿到数据我们可以进行任何操作

数据处理

  • 数据格式转换:将excel解析好的数据经过处理后,转成可以传给接口调用的数据
    在这里插入图片描述

调用接口进行excel上传的重点其实是数据的处理,我们需要按照接口的要求,把excel表格中经过插件处理好的数据处理成后端接口要求的格式

下面是后端接口要求的示例格式
在这里插入图片描述

  • 按接口要求,处理excel导入的数据

处理内容包含:

  • 字段中文转英文。excel中读入的是姓名,而后端需要的是username
  • 日期处理。从excel中读入的时间是一个number值,而后端需要的是标准日期。

5. 我们单独封装一个方法来实现这个转换的功能

/*** results excel表格的内容//        [ {'姓名':'小张', '手机号': '13712345678'}, {.....} ]// 目标//        [ {'username':'小张', 'mobile': '13712345678'}, {.....} ]*/transExcel(results) {const userRelations = {'入职日期': 'timeOfEntry','手机号': 'mobile','姓名': 'username','转正日期': 'correctionTime','工号': 'workNumber','部门': 'departmentName','聘用形式': 'formOfEmployment'}return results.map(item => {const obj = {}// 1. 取出这个对象所有的属性名: ['姓名', ‘手机号’]// 2. 遍历这个数组,通过 中文名去 userRelations 找对应英文名, 保存值const zhKeys = Object.keys(item)zhKeys.forEach(zhKey => {const enKey = userRelations[zhKey]// 如果是时间格式,就要做转换if (enKey === 'correctionTime' || enKey === 'timeOfEntry') {obj[enKey] = new Date(formatExcelDate(item[zhKey]))} else {obj[enKey] = item[zhKey]}})return obj})}handleSuccess({ results, header }) {console.log('从当前excel文件中读出的内容是', results)// results: [{入职日期: 44502, 姓名:xxxx}]// 目标:// results: [{timeOfEntry: 44502, username:xxxx}]// 处理从excel中读入的格式const arr = this.transExcel(results)console.log('转换之后的格式是', arr)
})

6. 日期处理函数

// 把excel文件中的日期格式的内容转回成标准时间
// https://blog.csdn.net/qq_15054679/article/details/107712966
export function formatExcelDate(numb, format = '/') {const time = new Date((numb - 25567) * 24 * 3600000 - 5 * 60 * 1000 - 43 * 1000 - 24 * 3600000 - 8 * 3600000)time.setYear(time.getFullYear())const year = time.getFullYear() + ''const month = time.getMonth() + 1 + ''const date = time.getDate() + ''if (format && format.length === 1) {return year + format + month + format + date}return year + (month < 10 ? '0' + month : month) + (date < 10 ? '0' + date : date)
}

下面我们就可以调接口了

  • 例子
  •   	接口:
    
/*** @description: 导入excel* @param {*} data* @return {*}*/
export function importEmployee(data) {return request({url: '/sys/user/batch',method: 'post',data})
}
  •   - 在页面中使用
    
import { importEmployee } from '@/api/employees'
export default {name: 'Import',methods: {async handleSuccess({ results, header }) {try {console.log('从当前excel文件中读出的内容是', results)// results: [{入职日期: 44502, 姓名:xxxx}]// 目标:// results: [{timeOfEntry: 44502, username:xxxx}]const arr = this.transExcel(results)console.log('转换之后的格式是', arr)// 调用上传的接口,const rs = await importEmployee(arr)console.log('调用上传的接口', rs)// 上传成功之后,回去刚才的页面this.$router.back()this.$message.success('操作成功')} catch (err) {this.$message.error(err.message)}}
}

2. 导出功能

在表格中查询到了我们需要的数据,希望用他们生成excel文件,保存在本地。

2.1 前端主导(工作大量在前端)

在这里插入图片描述

  • 取回数据,保存excel文件

2.2 后端主导(工作大量在后端)

在这里插入图片描述

前端调用接口

2.3 实现

1. 安装依赖

npm install file-saver script-loader --save

2. 导入文件

  • src/vendor/export2Excel.js
/* eslint-disable */
import { saveAs } from 'file-saver'
import XLSX from 'xlsx'function generateArray(table) {var out = [];var rows = table.querySelectorAll('tr');var ranges = [];for (var R = 0; R < rows.length; ++R) {var outRow = [];var row = rows[R];var columns = row.querySelectorAll('td');for (var C = 0; C < columns.length; ++C) {var cell = columns[C];var colspan = cell.getAttribute('colspan');var rowspan = cell.getAttribute('rowspan');var cellValue = cell.innerText;if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;//Skip rangesranges.forEach(function (range) {if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);}});//Handle Row Spanif (rowspan || colspan) {rowspan = rowspan || 1;colspan = colspan || 1;ranges.push({s: {r: R,c: outRow.length},e: {r: R + rowspan - 1,c: outRow.length + colspan - 1}});};//Handle ValueoutRow.push(cellValue !== "" ? cellValue : null);//Handle Colspanif (colspan)for (var k = 0; k < colspan - 1; ++k) outRow.push(null);}out.push(outRow);}return [out, ranges];
};function datenum(v, date1904) {if (date1904) v += 1462;var epoch = Date.parse(v);return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}function sheet_from_array_of_arrays(data, opts) {var ws = {};var range = {s: {c: 10000000,r: 10000000},e: {c: 0,r: 0}};for (var R = 0; R != data.length; ++R) {for (var C = 0; C != data[R].length; ++C) {if (range.s.r > R) range.s.r = R;if (range.s.c > C) range.s.c = C;if (range.e.r < R) range.e.r = R;if (range.e.c < C) range.e.c = C;var cell = {v: data[R][C]};if (cell.v == null) continue;var cell_ref = XLSX.utils.encode_cell({c: C,r: R});if (typeof cell.v === 'number') cell.t = 'n';else if (typeof cell.v === 'boolean') cell.t = 'b';else if (cell.v instanceof Date) {cell.t = 'n';cell.z = XLSX.SSF._table[14];cell.v = datenum(cell.v);} else cell.t = 's';ws[cell_ref] = cell;}}if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);return ws;
}function Workbook() {if (!(this instanceof Workbook)) return new Workbook();this.SheetNames = [];this.Sheets = {};
}function s2ab(s) {var buf = new ArrayBuffer(s.length);var view = new Uint8Array(buf);for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;return buf;
}export function export_table_to_excel(id) {var theTable = document.getElementById(id);var oo = generateArray(theTable);var ranges = oo[1];/* original data */var data = oo[0];var ws_name = "SheetJS";var wb = new Workbook(),ws = sheet_from_array_of_arrays(data);/* add ranges to worksheet */// ws['!cols'] = ['apple', 'banan'];ws['!merges'] = ranges;/* add worksheet to workbook */wb.SheetNames.push(ws_name);wb.Sheets[ws_name] = ws;var wbout = XLSX.write(wb, {bookType: 'xlsx',bookSST: false,type: 'binary'});saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), "test.xlsx")
}export function export_json_to_excel({multiHeader = [],header,data,filename,merges = [],autoWidth = true,bookType = 'xlsx'
} = {}) {/* original data */filename = filename || 'excel-list'data = [...data]data.unshift(header);for (let i = multiHeader.length - 1; i > -1; i--) {data.unshift(multiHeader[i])}var ws_name = "SheetJS";var wb = new Workbook(),ws = sheet_from_array_of_arrays(data);if (merges.length > 0) {if (!ws['!merges']) ws['!merges'] = [];merges.forEach(item => {ws['!merges'].push(XLSX.utils.decode_range(item))})}if (autoWidth) {/*设置worksheet每列的最大宽度*/const colWidth = data.map(row => row.map(val => {/*先判断是否为null/undefined*/if (val == null) {return {'wch': 10};}/*再判断是否为中文*/else if (val.toString().charCodeAt(0) > 255) {return {'wch': val.toString().length * 2};} else {return {'wch': val.toString().length};}}))/*以第一行为初始值*/let result = colWidth[0];for (let i = 1; i < colWidth.length; i++) {for (let j = 0; j < colWidth[i].length; j++) {if (result[j]['wch'] < colWidth[i][j]['wch']) {result[j]['wch'] = colWidth[i][j]['wch'];}}}ws['!cols'] = result;}/* add worksheet to workbook */wb.SheetNames.push(ws_name);wb.Sheets[ws_name] = ws;var wbout = XLSX.write(wb, {bookType: bookType,bookSST: false,type: 'binary'});saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), `${filename}.${bookType}`);
}

3. 给导出按钮添加点击事件

import('@/vendor/Export2Excel').then(excel => {// excel表示导入的模块对象console.log(excel)excel.export_json_to_excel({header: ['姓名', '工资'], // 表头 必填data: [['刘备', 100],['关羽', 500]], // 具体数据 必填filename: 'excel-list', // 文件名称autoWidth: true, // 宽度是否自适应bookType: 'xlsx' // 生成的文件类型})
})以上代码表示:1. 当我们正式点击导出按钮之后,才去加载vendor文件夹中的Export2Excel模块
2. import方法执行完毕返回的是一个promise对象,在then方法中我们可以拿到使用的模块对象
3. 重点关注data的配置部分,我们发现它需要一个严格的二维数组

Excel导出参数说明

参数说明类型可选值默认值
header导出数据的表头Array/[]
data导出的具体数据Array/[[]]
filename导出文件名String/excel-list
autoWidth单元格是否要自适应宽度Booleantrue / falsetrue
bookType导出文件类型Stringxlsx, csv, txt, morexlsx

真实数据实现导出功能

  • 思路
  1. 从后台重新获取数据(这样才能确保是最新的)
  2. 对数据的格式进行转换(后端给的数据字段名都是英文的),以用来做导出

在这里插入图片描述

  •   核心在于把后端接口返回的数据转成Export2Excel这个插件需要的格式 
    

4. 准备表头header数据

因为接口中返回的数据中的key是英文,而我们期望导出的表头是中文,所以提前准备中文和英文的映射关系

const map = {'id': '编号','password': '密码','mobile': '手机号','username': '姓名','timeOfEntry': '入职日期','formOfEmployment': '聘用形式','correctionTime': '转正日期','workNumber': '工号','departmentName': '部门','staffPhoto': '头像地址'
}
具体的表格数据我们需要通过接口从后端获取回来,难点在于如何把后端返回的数据处理成Export2Excel插件需求的二维数组格式。

下面是一个示例:

const dataArr = 
[["13600000001", "吕勇锐", "1992-08-04", "正式", "2020-01-01", "0001", "总裁办"]["13600000002", "袁永安", "1993-08-04", "正式", "2020-01-01", "0002", "总裁办"]
]

5. 补充一个用来处理数据的函数

transData(rows) {// 写代码const map = {'id': '编号','password': '密码','mobile': '手机号','username': '姓名','timeOfEntry': '入职日期','formOfEmployment': '聘用形式','correctionTime': '转正日期','workNumber': '工号','departmentName': '部门','staffPhoto': '头像地址'}// 写代码// header => ['id', '手机号', '用户名', ... ]// data => [//   ["604f764971f93f3ac8f365c2", "13800000002", "管理员",.... ]// ]const enKeys = Object.keys(rows[0])const header = enKeys.map(enKey => {return map[enKey]})const data = rows.map(obj => {return Object.values(obj)})return { header, data }}

最终的代码

// 导出excelasync hExportExcel() {// 1. 获取数据const res = await getEmployeeList(this.pageParams)console.log('有效数据:', res.data.rows)// 2. 处理数据const obj = this.transData(res.data.rows)// 3. 导出import('@/vendor/Export2Excel').then(excel => {excel.export_json_to_excel({header: obj.header, // ['姓名', '工资'], // 表头 必填data: obj.data, // [//   ['刘备', 800],//   ['关羽', 500]// ], // 具体数据 必填filename: 'employee-list', // 文件名称autoWidth: true, // 宽度是否自适应bookType: 'xlsx' // 生成的文件类型})})},transData(rows) {// 写代码const map = {'id': '编号','password': '密码','mobile': '手机号','username': '姓名','timeOfEntry': '入职日期','formOfEmployment': '聘用形式','correctionTime': '转正日期','workNumber': '工号','departmentName': '部门','staffPhoto': '头像地址'}// 写代码// header => ['id', '手机号', '用户名', ... ]// data => [//   ["604f764971f93f3ac8f365c2", "13800000002", "管理员",.... ]// ]const enKeys = Object.keys(rows[0])const header = enKeys.map(enKey => {return map[enKey]})const data = rows.map(obj => {return Object.values(obj)})return { header, data }}

http://chatgpt.dhexx.cn/article/Wa1CrfAj.shtml

相关文章

excel导入功能

------这里只是测试类------实际使用的看下面 需要用到ExcelUtils工具类 ExcelUtils的主要作用是把Excel转化成 List<List<Object>>类型的数据&#xff0c;方便遍历 package tech.niua.common.excelimport;import java.io.IOException; import java.io.InputStream…

Java实现Excel导入导出操作详解

本文转载自 :Java实现Excel导入和导出&#xff0c;看这一篇就够了(珍藏版)_zyqok的博客-CSDN博客_excel导入 java前言最近抽了两天时间&#xff0c;把Java实现表格的相关操作进行了封装&#xff0c;本次封装是基于POI的二次开发&#xff0c;最终使用只需要调用一个工具类中的方…

EasyExcel实现excel导入

文章目录 前言一、使用步骤1.添加依赖&#xff1a;2.创建和实体类对应的用于导入导出的模板类&#xff0c;尽量不要直接使用实体类。每个字段需添加ExcelProperty注解&#xff0c;作为导入导出的识别的依据。注意value值是跟excel里的列名保持一致&#xff0c;不是跟数据库里的…

Excel表格的导入导出——EasyExcel

参考视频 csdn参考地址 一、导入依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.5</version> </dependency>二、实体类 方式一&#xff1a;Excel Property&#xff08;&…

实现Excel的导入、导出

实现Excel的导入、导出 关于excel的操作在工作中经常会遇到&#xff0c;如果只是一次性使用的话&#xff0c;最简单的方式就是通过数据库的可视化工具&#xff08;如Navicat&#xff09;查询结果集之后直接一键生成excel了&#xff0c;当然这只能解燃眉之急&#xff0c;并不是…

EasyExcel复杂excel导入

EasyExcel复杂excel导入 easyexcel官方都是一些简单的导入到处示例&#xff0c;复杂的excel文档导入&#xff0c;还得自己去慢慢琢磨、百度、思考、总结、学习、观察。 代码地址在文档的最后&#xff0c;如果你也遇到这种需求&#xff0c;不妨动动你的小拇指&#xff0c;点个…

导出Excel的方式

*数据是表格的形式&#xff0c;进常用到Excel *在程序中经常可以看到有导出Excel文档&#xff0c;Excel导入数据的情况&#xff0c;现在我就说一下我学到的导出Excel *导出Excel有两种方法&#xff0c;第一种是自己设置表头的&#xff0c;第二种是填充的&#xff0c;现在我说的…

Excel文件导入导出操作

> 注意&#xff01;注意&#xff01;&#xff01;注意&#xff01;&#xff01;&#xff01; 文末有惊喜彩蛋&#xff0c;请注意查收&#xff01;日常开发工作中对于文件的相关操作大家多少都会涉及&#xff1a;上传解析、数据导出等。此篇内容主要分享一下工作中常用的Exce…

Java实现Excel导入导出

一、导入 前言&#xff1a;导入必须用post请求 具体原因在2中叙述 1、Excel导入 总结一下目标&#xff0c;就是要将excel中的数据行、逐一提取&#xff0c;最后得到一个list&#xff0c;这个list的每个元素就是excel的每个数据行的实例&#xff0c;之后的操作就是常规的jav…

Java实现Excel导入

实现前准备&#xff08;导入所需要的依赖&#xff09; <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.22</version></dependency><dependency><groupId>org.apache.…

导入Excel文件的方法

我们在做一些项目的时候通常会遇到有导入文件的这种需求&#xff0c;下面我给大家分享一下导入Excel文件的方法。 首先我们得准备一个模板&#xff0c;下图就是一个简单的EX导入模板&#xff0c; 下图就是一个导入模板&#xff0c; 我们先把导入模板写好&#xff0c; 还有一个…

两种方式导入excel

第一种 easyExcel pom文件导入 com.alibaba easyexcel 2.2.3 然后 /** * 导入用户excel * param * return */ PostMapping(“manage/imporAcc”) Message<?> imporAcc(RequestParam(“accountFile”) MultipartFile file,RequestParam(“role”)String role,Request…

Java实现Excel导入和导出,看这一篇就够了(珍藏版)

目录 目录 前言 1. 功能测试 1.1 测试准备 1.2 数据导入 1.2.1 导入解析为JSON 1.2.2 导入解析为对象&#xff08;基础&#xff09; 1.2.3 导入解析为对象&#xff08;字段自动映射&#xff09; 1.2.4 导入解析为对象&#xff08;获取行号&#xff09; 1.2.5 导入解析…

常见机器学习面试题

参考&#xff1a;http://kubicode.me/2015/08/16/Machine%20Learning/Common-Interview/?fromsinglemessage# http://blog.csdn.NET/heyongluoyao8/article/details/49429629 http://lib.csdn.Net/article/machinelearning/33798 http://www.cnblogs.com/zuochongyan/p/540705…

机器学习面试题60~100

61.说说梯度下降法 LeftNotEasy&#xff0c;本题解析来源&#xff1a;http://www.cnblogs.com/LeftNotEasy/archive/2010/12/05/mathmatic_in_machine_learning_1_regression_and_gradient_descent.html 下面是一个典型的机器学习的过程&#xff0c;首先给出一个输入数据&#…

深度学习机器学习面试题汇——模型优化,轻量化,模型压缩

深度学习机器学习面试题汇——模型优化&#xff0c;轻量化&#xff0c;模型压缩 提示&#xff1a;互联网大厂可能考的面试题 若CNN网络很庞大&#xff0c;在手机上运行效率不高&#xff0c;对应模型压缩方法有了解吗 介绍一下模型压缩常用的方法&#xff1f;为什么用知识蒸馏&…

Python干货:破解40大机器学习面试题(包含初中高级)

机器学习&#xff08;ML&#xff09;是我们世界的未来。在未来的几年中&#xff0c;几乎每种产品都将包含ML组件。ML预计将从2020年的$ 7.3B增长到2024年的$ 30.6B。对ML技能的需求遍及整个行业。 机器学习面试是一个严格的过程&#xff0c;在此过程中&#xff0c;应聘者会评估…

2021机器学习面试必考面试题汇总(附答案详解)

问题&#xff1a;Xgboost、lightGBM和Catboost之间的异同&#xff1f; 树的特征 三种算法基学习器都是决策树&#xff0c;但是树的特征以及生成的过程仍然有很多不同。 CatBoost使用对称树&#xff0c;其节点可以是镜像的。CatBoost基于的树模型其实都是完全二叉树。 XGBoo…

机器学习面试题之——简单介绍最小二乘

1、常用到的最小二乘场合&#xff1a;最小二乘法直线拟合&#xff0c;最小二乘法多项式&#xff08;曲线&#xff09;拟合&#xff0c;机器学习中线性回归的最小二乘法&#xff0c;系统辨识中的最小二乘辨识法&#xff0c;参数估计中的最小二乘法&#xff0c;等等。 2、为什么…

AI人工智能、机器学习 面试题(2022最新版)

人工智能、机器学习面试题总结&#xff0c;侧重于理解&#xff0c;回答供参考&#xff0c;欢迎讨论。 General 深度学习&#xff08;Deep Learning, DL&#xff09;和机器学习&#xff08;Machine Learning, ML&#xff09;的关系是什么&#xff1f; 深度学习是机器学习的子类…