在Vite项目中实现Excel快速导入数据到数据库

引言

在一些工厂的MES项目中,总会有这样的抱怨:手动在表单一个个录入数据太麻烦了,就不能传一个表格进去批量导入吗?当然可以!笔者在此以常见的若依框架为例,在其前端的Vite项目之中实现一个这样的功能。

准备工作

引入必要的依赖,装不上可以改用国内特供的cnpm

bash 复制代码
 npm install -S file-saver xlsx
 
 npm install -D script-loader # 可选
  1. npm install -S file-saver xlsx

    • file-saver:一个用于在浏览器中实现文件下载的 JavaScript 库,支持将数据保存为本地文件
    • xlsx:即 SheetJS 库,用于解析和生成 Excel 文件(.xlsx, .xls 等格式)
    • -S:是 --save 的缩写,会将依赖记录到 package.jsondependencies 中,表示是生产环境需要的依赖
  2. npm install -D script-loader

    • script-loader:webpack 的一个加载器,用于在全局作用域中执行指定的 JavaScript 文件
    • -D:是 --save-dev 的缩写,会将依赖记录到 package.jsondevDependencies 中,仅用于开发环境
    • 只有当你需要加载非模块化的老旧 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表格里面的数据已经被添加进去了。

相关推荐
程序员海军13 小时前
2025年上半年前端技术圈生态总结分享
前端·vue.js·react.js
几度风雨见丹心14 小时前
vue+elementUI 进行表格行内新增及校验,同行其他输入框数据影响当前输入框校验结果
前端·vue.js·elementui
开发者小天14 小时前
在Ant Design Vue 中使用图片预览的插件
前端·javascript·vue.js·前端框架
_月下闲人14 小时前
已掌握 Vue2 的开发者,快速上手 Vue3
前端·javascript·vue.js
无光末阳14 小时前
前端 vue3+router 比较好用的自定义日志插件
前端·vue.js
四季豆豆豆14 小时前
办公任务分发项目 laravel vue mysql 第一章:核心功能构建 API
vue.js·mysql·laravel
云霄IT14 小时前
vue3前端开发的基础教程——快速上手
前端·javascript·vue.js
前端缘梦14 小时前
深入浅出 Vue 的 Diff 算法:最小化 DOM 操作的魔法
前端·vue.js·面试
月伤5914 小时前
Element Plus 表格表单校验功能详解
前端·javascript·vue.js