前言
关于Excel 数据导入和导出,比起纯后端解析,前端更有优势,一来是现在的个人电脑的性能已经有了长足的进步,二来,服务端的资源本就金贵,后端服务的瓶颈就是业务系统平台的瓶颈。
核心技术栈
xlsx
npm中搜索xlsx,就可以发现xlsx的所有功能。xlsx应该是bug最少也最稳定的,此外,还有xlsx-style和js-xlsx,它们在原有的功能基础上,添加了对导出样式的控制,让导出的Excel更加满足业务需要。这些都是或多或少的引用了xlsx的官方代码,在此基础上,进行了扩展开发。具体此处不再详细介绍,如需了解,请前往查看。
思路
- 前端读取excel文件,转为数组;
- 将数组进行截取,通过for循环分批传给后端,后端进行数据的检测;
- 后端检测无误后,前端再次for循环将数据传给后端,后端进行导入;
- 导入完成,前端调用列表接口,刷新数据;
用到的依赖库
npm install xlsx --save
部分源码
<template>
<div>
<el-button @click="importDialogFlag=true">导入</el-button>
<el-dialog :visible.sync="importDialogFlag" title="导入" center>
<div style="margin-bottom:20px;display:flex">
<el-upload class="upload" ref="upload" action="/" :multiple="true" :show-file-list="false" :on-change="selectExcel" :limit=1 :auto-upload="false">
<el-button icon="el-icon-upload" size="small" type="primary">选择文件</el-button>
</el-upload>
<el-tag style="margin-left:10px;" v-show="fileName">{{fileName}}</el-tag>
</div>
<div style="margin-bottom:20px">
<el-button type="success" size="small" @click="imports()" >开始导入</el-button>
<div style="margin-top:20px">
<el-progress :text-inside="true" :stroke-width="18" :percentage="percentNum" ></el-progress>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import XLSX from 'xlsx';
export default{
data(){
return{
importDialogFlag:false,//导入弹窗开关
fileName:'',//导入的文件名
importData:[],//表格数据
percentNum:0,//进度条百分比
importMsg:'',//错误信息
}
},
methods: {
//选择文件后的操作
async selectExcel(file){
const types = file.name.split(".")[1];//types为选中文件的类型
const fileType = ["xlsx", "xlc", "xlm", "xls", "xlt", "xlw", "csv"].some(item => item === types);//判断选择的文件是否为支持的格式,返回Boolean
if (!fileType) {
this.$message('文件格式错误');
this.$refs["upload"].clearFiles();
return;
}
this.fileName = file.name;//文件名赋值
let res=await this.file2Xce(file);//res为读取到的表格数据
if (res && res.length > 0) {
res[0].sheet.map(item => {
this.importData.push(item);
});
}
},
//读取表格文件的方法,这是一个promise,返回表格数据
file2Xce(file) {
return new Promise(function(resolve, reject) {
// 创建FileReader对象,将文件内容读入内存
const reader = new FileReader();
reader.onload = function(e) {
const data = e.target.result;
this.wb = XLSX.read(data, {
type: "binary"
});
const result = [];
this.wb.SheetNames.forEach(sheetName => {
result.push({
sheetName: sheetName,
sheet: XLSX.utils.sheet_to_json(this.wb.Sheets[sheetName])
});
});
resolve(result);
};
reader.readAsBinaryString(file.raw);
});
},
imports(){
let cyclic; //循环次数
let cyclicNum = 1; // 每次导入的条数
let importDatas=[]; //每次传入的数据
if (this.importData.length == 0) {
this.$message('请选择文件');
return;
}
cyclic = Math.ceil(this.importData.length / cyclicNum);
for (let i = 0; i < cyclic; i++) { //第一次for循环进行数据检测
let x = i * cyclicNum;
let y = (i + 1) * cyclicNum;
if (y > this.importData.length) {
//判断截取的Y值
y = this.importData.length;
}
importDatas = this.importData.slice(x, y); //用slice方法分批传数据
this.percentNum = Number((((i + 1) / cyclic) * 100).toFixed(2)); //进度条的百分比
if (this.percentNum >= 100) {//判断百分比
this.percentNum = 100;
}
/* 开始调用接口 */
// let res = await equipmentImportTest_api({
// deviceImportList: importsData
// });
/* 对错误信息进行赋值 */
// if (res.data.code == 512) {
// this.errMsg = true;
// this.importMsg = this.importMsg.concat(res.data.msg[0].errorMessage);
// this.importMsgs = this.importMsgs.concat(
// res.data.msg[1].errorMessage
// );
// this.importMsgss = this.importMsgss.concat(
// res.data.msg[2].errorMessage
// );
// }
}
if (!this.importMsg) {//判断没有错误信息
this.percentNum = 1;//清空进度条
for (let i = 0; i < cyclic; i++) {//第二次for循环正式导入
let x = i * cyclicNum;
let y = (i + 1) * cyclicNum;
if (y > this.importData.length) {
y = this.importData.length;
}
importDatas = this.importData.slice(x, y); //分批传数据
// let res = await equipmentImport_api({//调用导入接口
// deviceImportList: importDatas
// });
this.percentNum = Number((((i + 1) / cyclic) * 100).toFixed(2)); //进度条的百分比
if (this.percentNum >= 100) {
this.percentNum = 100;
}
}
}
}
},
}
</script>
网友评论