一级表头:↓
二级表头↓
三级表头↓
思路:封装一个导入的组件,然后全局引用,可以导入基础表,二级表头,三级表头。
封装的组件的作用:接收用户上传的文件
xslx插件的任务:把用户上传的文件转换成 后端需要的数据结构
本文没有做的: 掉接口把数据传过去
项目结构↓
tableData↓
tableData: [
{
date: "2016/05/02",
name: "王小虎",
address: "上海市普陀区金沙江路 1518 弄",
age: 1,
sex: "男",
},
{
date: "2016-05-04",
name: "王小虎",
address: "上海市普陀区金沙江路 1517 弄",
age: 1,
sex: "男",
},
{
date: "2016-05-01",
name: "王小虎",
address: "上海市普陀区金沙江路 1519 弄",
age: 1,
sex: "男",
},
{
date: "2016-05-03",
name: "王小虎",
address: "上海市普陀区金沙江路 1516 弄",
age: 1,
sex: "男",
},
],
UploadExcel/index.vue ↓
<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
// console.log(XLSX)
const workbook = XLSX.read(data, { type: 'array' })
console.log(workbook)
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="less">
.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>
main.js中全局注册组件↓
import UploadExcel from './components/UploadExcel/index.vue'
export default {
// 插件的初始化, 插件给你提供的全局的功能, 都可以在这里配置
install(Vue) {
// 进行组件的全局注册
Vue.component('UploadExcel', UploadExcel) // 注册导入excel组件
}
}
在需要使用的页面:
导入并使用↓
<upload-excel :on-success="handleSuccess" />
components:{uploadExcel},
method↓ 分别含有基础表头 和二级表头,三级表头,其中处理时间的 函数暂时没使用,因为是直接读取字符串了
methods: {
//使用组件进行导入
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)
);
},
// 基本表 ↓
/**
* results excel表格的内容
// [ {'姓名':'小张', '手机号': '13712345678'}, {.....} ]
// 目标
// [ {'username':'小张', 'mobile': '13712345678'}, {.....} ]
*/
// transExcel(results) {
// const userRelations = {
// '日期': 'date',
// '地址': 'address',
// '姓名': 'name',
// '年龄': 'age',
// '性别': 'sex',
// }
// return results.map(item => {
// const obj = {}
// // 1. 取出这个对象所有的属性名: ['姓名', ‘手机号’]
// // 2. 遍历这个数组,通过 中文名去 userRelations 找对应英文名, 保存值
// const zhKeys = Object.keys(item)
// zhKeys.forEach(zhKey => {
// const enKey = userRelations[zhKey]
// // 如果是时间格式,就要做转换
// // if (enKey === 'date') {
// // obj[enKey] = new Date(this.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)
// },
// 导入二级表头
// transExcel(results) {
// return results.map((item, index) => {
// var obj = {};
// // for (var i = index; i < results.length; i++) {
// obj = {
// // 键名为绑定 el 表格的关键字,值则是 ws[i][对应表头名]
// date: results[index]["日期"],
// name: results[index]["姓名"],
// address: results[index]["其他"],
// age: results[index][""],
// sex: results[index]["_1"],
// };
// return obj;
// });
// },
// handleSuccess({ results, header }) {
// console.log("从当前excel文件中读出的内容是", results);
// const arr = this.transExcel(results);
// console.log("转换之后的格式是", arr);
// },
// 导入三级表头 其实跟2级一样,只是obj需要修改
transExcel(results) {
return results.map((item, index) => {
var obj = {};
// for (var i = index; i < results.length; i++) {
obj = {
// 键名为绑定 el 表格的关键字,值则是 ws[i][对应表头名]
date: results[index]["日期"],
name: results[index]["姓名"],
address: results[index]["_1"],
age: results[index]["其他"],
sex: results[index][""],
};
return obj;
});
},
handleSuccess({ results, header }) {
console.log("从当前excel文件中读出的内容是", results);
const arr = this.transExcel(results);
console.log("转换之后的格式是", arr);
},
}
注意:
对于1级表头来说: 处理前 :4条数据 处理后 4条数据 (不含中文表头信息)
对于2级表头来说: 处理前 :5条数据 处理后5条数据 (含中文2级表头信息,拿到处理后的数据需要再用数组的方法除去索引为0的信息)
对于3级表头来说: 处理前 :6条数据 处理后 6条数据 (含中文2级和3级表头信息,拿到处理后的数据需要再用数组的方法除去索引为0和1的信息)