注意:这里是前端使用xlsx插件,将前端上传的.xls、.xlsx文件,解析得到原始列表数据
我的是vue3项目
1.安装依赖
pnpm add xlsx@0.18.5
2.组件封装
src/components/MyExcelData/index.vue
<template>
<div v-loading="loading">
<div class="drop-area" @dragenter="highlight" @dragover="highlight" @dragleave="unHighlight" @drop="handleDrop"
@click="handleClick">
<el-icon :size="67" color="#9ea1a9" class="el-icon--upload"><upload-filled /></el-icon>
<div class="desc" v-if="props.msg">{{ props.msg }}</div>
<input type="file" ref="fileInput" @change="handleFiles" :multiple="multipleFlag" accept=".xlsx, .xls"
style="display: none;">
</div>
</div>
</template>
<script setup>
import { defineProps } from 'vue'
import { UploadFilled } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { ref } from 'vue';
import * as XLSX from 'xlsx';
defineOptions({
name: "MyExcelData"
})
const props = defineProps({
headerList: { type: Array, default: () => [] }, // 表头英文列表
msg: { type: String, default: () => '' }, // 描述
})
const emit = defineEmits(["setExcelData"])
let loading = ref(false)
const tableData = ref([]);
const tableHeader = ref([]);
// const fileList = ref([]) // 当前组件内的文件集合
const inputList = ref([]) // 这个是input拿到的list 不一定调用上传接口
const fileInput = ref(null); // 上传ref
const multipleFlag = ref(false) // 是否多选
const highlight = (e) => {
e.preventDefault();
e.stopPropagation();
e.target.classList.add('hover');
};
const unHighlight = (e) => {
e.target.classList.remove('hover');
};
// 拖拽
const handleDrop = (e) => {
e.preventDefault();
e.stopPropagation();
unHighlight(e);
const files = e.dataTransfer.files;
inputList.value = [] // 必须这几步都置空 不然无效
for (let i = 0; i < files.length; i++) {
inputList.value.push({
...files[i],
name: files[i].name,
size: files[i].size,
type: files[i].type
});
}
uploadFile();
};
// 点击
const handleClick = () => {
// console.log(fileInput.value);
fileInput.value.click(); // 使用 ref 的 value 直接访问 DOM 元素
};
// 获取点击文件
const handleFiles = (e) => {
const files = e.target.files || e;
// console.log('files', files, typeof files, files[0]);
inputList.value = [] // 必须这几步都置空 不然无效
for (let i = 0; i < files.length; i++) {
inputList.value.push({
file: files[i],
name: files[i].name,
size: files[i].size,
type: files[i].type
});
}
uploadFile();
};
// 上传文件
const uploadFile = () => {
// console.log('inputList', inputList.value);
const file = inputList.value[0].file
// console.log('上传接口的文件', file);
handleFileUpload(file)
};
const handleFileUpload = (event) => {
loading.value = true
tableData.value = [];
tableHeader.value = [];
// const file = event.target.files[0];
const file = event
const reader = new FileReader();
reader.onload = (e) => {
const data = new Uint8Array(e.target.result);
const workbook = XLSX.read(data, { type: 'array' });
const sheetName = workbook.SheetNames[0];
const sheet = workbook.Sheets[sheetName];
const jsonData = XLSX.utils.sheet_to_json(sheet, { header: 1 });
// console.log('jsonData', jsonData);
tableHeader.value = props.headerList.length ? props.headerList : jsonData[0]
//tableData.value = jsonData.slice(1);
// 遍历总数居中除第一项的每个数组
for (let i = 1; i < jsonData.length; i++) {
let obj = {};
// 遍历arr1第一项的数组,将其元素作为key,与当前遍历的数组对应位置的元素作为value,构建对象
for (let j = 0; j < tableHeader.value.length; j++) {
obj[tableHeader.value[j]] = jsonData[i][j];
}
tableData.value.push(obj); // 将构建好的对象添加到arr2中
}
// console.log('tableData.value', tableData.value);
// 过滤掉属性值全是undefined的对象
function removeItemsWithUndefinedValues(arr) {
return arr.filter(obj => {
return Object.values(obj).some(value => value !== undefined);
});
}
tableData.value = removeItemsWithUndefinedValues(tableData.value)
// console.log(tableData.value)
emit('setExcelData', { tableHeader: tableHeader.value, tableData: tableData.value })
ElMessage.success('解析成功')
loading.value = false
};
reader.onerror = (e) => {
ElMessage.error('解析失败', e)
emit('setExcelData', { tableHeader: tableHeader.value, tableData: [] })
loading.value = false; // 可以在这里取消加载状态或者进行其他错误处理操作
};
reader.readAsArrayBuffer(file);
// return 掉 不调用接口的报错
return false
};
</script>
<style lang="scss" scoped>
.drop-area {
width: 440px;
height: 185px;
border: 1px dashed #dcdfe6;
border-radius: 6px;
text-align: center;
cursor: pointer;
padding-top: 40px;
}
.drop-area:hover {
border-color: #11716f;
}
</style>
3.子组件使用
注意点:
headerList就是列表数据的表头;一定要保证headerList的长度和数据列对应
<!-- 导入组件 -->
<MyExcelData :headerList="['tableName', 'tableType', 'tableAuthority', 'tableNameDesc']"
@setExcelData="setExcelData" :msg="'仅支持导入表格格式,扩展名.xls、.xlsx,导入成功后,表信息将全部更新,请核对导入数据'"></MyExcelData>
// 解析数据
const setExcelData = (val) => {
console.log('解析数据', val)
}