引言
在一些工厂的MES项目中,总会有这样的抱怨:手动在表单一个个录入数据太麻烦了,就不能传一个表格进去批量导入吗?当然可以!笔者在此以常见的若依框架为例,在其前端的Vite项目之中实现一个这样的功能。
准备工作
引入必要的依赖,装不上可以改用国内特供的cnpm
bash
npm install -S file-saver xlsx
npm install -D script-loader # 可选
-
npm install -S file-saver xlsx
file-saver
:一个用于在浏览器中实现文件下载的 JavaScript 库,支持将数据保存为本地文件xlsx
:即 SheetJS 库,用于解析和生成 Excel 文件(.xlsx, .xls 等格式)-S
:是--save
的缩写,会将依赖记录到package.json
的dependencies
中,表示是生产环境需要的依赖
-
npm install -D script-loader
script-loader
:webpack 的一个加载器,用于在全局作用域中执行指定的 JavaScript 文件-D
:是--save-dev
的缩写,会将依赖记录到package.json
的devDependencies
中,仅用于开发环境- 只有当你需要加载非模块化的老旧 JS 库时,才需要安装它,若依框架可以不装。
前端准备
添加一个批量导入的按钮:

vue
<el-button
type="success"
plain
icon="Upload"
@click="handleUpload">
批量导入
</el-button>
点击按钮,弹出对应的对话框:
vue
<el-dialog v-model="openUpload" width="600px" append-to-body>
<template #header>
<div class="titleClass">上传Excel表格</div>
</template>
<el-upload
ref="uploadSheet"
action=""
:on-change="handleChange"
:on-remove="handleRemove"
:on-exceed="handleExceed"
:limit="limitUpload"
:accept="excelAccept"
:auto-upload="false"
:before-upload="beforeUpload"
>
<el-button size="large" type="primary">点击上传</el-button>
<template #tip>
<div class="el-upload__tip text-sm text-gray-500 mt-1">
仅支持 .xlsx 和 .xls 格式的Excel文件,单次最多上传 {{ limitUpload }} 个文件
</div>
</template>
</el-upload>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" size="large" @click="addBySheetData">确认导入</el-button>
</div>
</template>
</el-dialog>

js部分:
JavaScript
const openUpload=ref(false);//控制对话框弹出
const limitUpload = ref(1); //限制只能传一个文件
const uploadSheet = ref(null);// 在组件中获取 el-upload 的引用
function handleUpload() {
openUpload.value = true;
}
// Excel文件类型限制
const excelAccept = ref(
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,' +
'application/vnd.ms-excel'
);
const excelData = ref([]); // 存储解析后的Excel数据
// 读取并解析Excel文件的核心函数
const readExcelFile = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
const rABS = false; // 是否以二进制字符串方式读取
// 扩展FileReader的readAsBinaryString方法(如果不存在)
if (!FileReader.prototype.readAsBinaryString) {
FileReader.prototype.readAsBinaryString = function(f) {
const reader = new FileReader();
reader.onload = (e) => {
const bytes = new Uint8Array(reader.result);
let binary = '';
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
this.onloadend(binary);
};
reader.onerror = (e) => this.onerror(e);
reader.readAsArrayBuffer(f);
};
}
// 文件读取完成后的处理
reader.onload = (e) => {
try {
const data = e.target.result;
// 解析Excel文件
const workbook = XLSX.read(data, {
type: rABS ? 'base64' : 'binary'
});
// 获取第一个工作表的名称
const firstSheetName = workbook.SheetNames[0];
// 将工作表转换为JSON
const worksheet = workbook.Sheets[firstSheetName];
const jsonData = XLSX.utils.sheet_to_json(worksheet);
const deviceId= route.params.deviceId;
// 格式化数据 - 根据你的Excel列名进行调整
const formattedData = jsonData.map(item => ({
deviceId: deviceId,
patrolContent: item['巡检内容'] || '',
patrolCriteria: item['判断标准'] || '',
patrolMethod: item['巡检方法'] || '',
patrolPart: item['巡检部位'] || '',
patrolPeriod: item['周期单位']==='小时' ? 1 : item['周期单位']==='天' ? 2 : 3,
patrolPeriodValue: item['周期值'] || ''
}));
resolve(formattedData);
} catch (error) {
reject(new Error('解析Excel文件失败: ' + error.message));
}
};
// 处理读取错误
reader.onerror = () => {
reject(new Error('文件读取失败'));
};
// 根据rABS选择读取方式
if (rABS) {
reader.readAsArrayBuffer(file);
} else {
reader.readAsBinaryString(file);
}
});
};
// 文件变化时触发
const handleChange = async (file, fileList) => {
// 获取文件扩展名
const fileExt = file.name.split('.').pop().toLowerCase();
// 验证文件扩展名
if (!['xlsx', 'xls'].includes(fileExt)) {
ElMessage.error('请上传Excel格式的文件(.xlsx 或 .xls)');
return false; // 阻止上传
}
// 可选:验证文件大小(例如限制5MB以内)
const fileSize = file.size / 1024 / 1024; // 转为MB
if (fileSize > 5) {
ElMessage.error('文件大小不能超过5MB');
return false; // 阻止上传
}
// 只处理最新添加的文件
if (file.status === 'ready') {
try {
// 解析Excel文件
const data = await readExcelFile(file.raw);
excelData.value = data;
ElMessage.success('文件解析成功');
console.log('解析结果:', data);
// 这里可以添加后续的数据处理逻辑
// 例如:this.handleParsedData(data)
} catch (error) {
ElMessage.error('文件解析失败: ' + error.message);
console.error('解析错误:', error);
}
}
};
// 移除文件时触发
const handleRemove = (file, fileList) => {
console.log('移除文件:', file, fileList);
excelData.value=[];
};
// 超过限制时触发
const handleExceed = (files, fileList) => {
ElMessage.warning(`最多只能上传 ${limitUpload.value} 个文件`);
};
// 上传前验证文件类型
const beforeUpload = (file) => {
// 获取文件扩展名
const fileExt = file.name.split('.').pop().toLowerCase();
// 验证文件扩展名
if (!['xlsx', 'xls'].includes(fileExt)) {
ElMessage.error('请上传Excel格式的文件(.xlsx 或 .xls)');
return false; // 阻止上传
}
// 可选:验证文件大小(例如限制5MB以内)
const fileSize = file.size / 1024 / 1024; // 转为MB
if (fileSize > 5) {
ElMessage.error('文件大小不能超过5MB');
return false; // 阻止上传
}
return true; // 允许上传
};
注意
以下表为例,把需要的表头转化为后端接口能接受的JSON数据,不清楚JSON数据结构的一定要按照接口文档的标准来,把对应的列名转化为JSON数据中的属性名字

less
// 格式化数据 - 根据你的Excel列名进行调整
const formattedData = jsonData.map(item => ({
deviceId: deviceId,
//巡检内容对应patrolContent,下同
patrolContent: item['巡检内容'] || '',
patrolCriteria: item['判断标准'] || '',
patrolMethod: item['巡检方法'] || '',
patrolPart: item['巡检部位'] || '',
//类型也要和接口一致
patrolPeriod: item['周期单位']==='小时' ? 1 : item['周期单位']==='天' ? 2 : 3,
patrolPeriodValue: item['周期值'] || ''
}));
点击确定之后,会用下面的方法导入:
ini
const addBySheetData = async () => {
for (const item of excelData.value) {
//替换成你自己的接口
await addDevicePatrol(item).then(async response => {
open.value = false;
});
}
proxy.$modal.msgSuccess("数据导入成功");
uploadSheet.value.clearFiles();
excelData.value = [];
await getList();
openUpload.value=false;
}
这样就能批量导入数据了!
运行结果
可以看到,上面Excel表格里面的数据已经被添加进去了。