vue3设备excel设备导入实现

  <el-upload
    action="#"
    :before-upload="handleBeforeUpload"
    :show-file-list="false"
    accept=".xlsx, .xls"
  >
    <el-button type="primary">导入设备列表</el-button>
  </el-upload>
  1. 使用 el-upload 组件,指定上传地址为 #(即不实际上传)。
  2. 在上传前调用 handleBeforeUpload 方法进行自定义校验或处理。
  3. 隐藏文件列表显示。
  4. 限制只能上传 .xlsx 和 .xls 格式的文件。
  5. 提供一个按钮,点击后触发文件选择对话框。
const handleBeforeUpload = async (file) => {
  // 1. 读取Excel文件
  const reader = new FileReader();
  reader.onload = async (e) => {
    try {
      // 2. 解析Excel数据
      const data = new Uint8Array(e.target.result);
      const workbook = XLSX.read(data, { type: 'array' });
      const worksheet = workbook.Sheets[workbook.SheetNames[0]];
      const jsonData = XLSX.utils.sheet_to_json(worksheet);

      // 3. 数据校验
      if (!jsonData.length) {
        ElMessage.error('文件内容为空');
        return false;
      }

      // 4. 字段校验
      const requiredFields = ['设备名称', '设备编码', '父节点编码'];
      const missingFields = requiredFields.filter(field => !jsonData[0].hasOwnProperty(field));
      if (missingFields.length) {
        ElMessage.error(`缺少必要字段:${missingFields.join(', ')}`);
        return false;
      }

      // 5. 构建校验映射表
      const existingCodes = new Set();
      const existingNames = new Set();
      const walkNodes = (nodes) => {
        nodes.forEach(node => {
          existingCodes.add(node.code);
          existingNames.add(node.label);
          if (node.children) walkNodes(node.children);
        });
      };
      walkNodes(menulist.value);

      // 6. 数据预处理
      const validData = [];
      const errorMessages = [];

      jsonData.forEach((row, index) => {
        // 6.1 空值校验
        if (!row.设备名称 || !row.设备编码) {
          errorMessages.push(`第${index + 2}行:设备名称和编码不能为空`);
          return;
        }

        // 6.2 唯一性校验
        if (existingCodes.has(row.设备编码)) {
          errorMessages.push(`第${index + 2}行:设备编码 ${row.设备编码} 已存在`);
          return;
        }
        if (existingNames.has(row.设备名称)) {
          errorMessages.push(`第${index + 2}行:设备名称 ${row.设备名称} 已存在`);
          return;
        }

        // 6.3 父节点存在性校验
        if (row.父节点编码 && row.父节点编码 !== '根节点' && !existingCodes.has(row.父节点编码)) {
          errorMessages.push(`第${index + 2}行:父节点编码 ${row.父节点编码} 不存在`);
          return;
        }

        validData.push({
          label: row.设备名称,
          code: row.设备编码,
          parentCode: row.父节点编码 === '根节点' ? null : row.父节点编码
        });
        existingCodes.add(row.设备编码);
        existingNames.add(row.设备名称);
      });

      // 7. 错误处理
      if (errorMessages.length) {
        ElMessage.error({
          message: `导入失败,存在问题:\n${errorMessages.join('\n')}`,
          duration: 5000
        });
        return false;
      }

      // 8. 构建树形结构
      const buildTree = (nodes, parentCode = null) => {
        return nodes
          .filter(node => node.parentCode === parentCode)
          .map(node => ({
            ...node,
            children: buildTree(nodes, node.code)
          }));
      };

      // 9. 合并新旧数据(保持响应式)
      const mergedData = [...menulist.value, ...buildTree(validData)];
      
      // 10. 响应式更新(使用深拷贝保证响应式触发)
      menulist.value = JSON.parse(JSON.stringify(mergedData));

      ElMessage.success(`成功导入 ${validData.length} 条设备数据`);
    } catch (error) {
      ElMessage.error(`文件解析失败:${error.message}`);
    }
  };

  reader.readAsArrayBuffer(file);
  return false; // 阻止自动上传
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值