React / Vue 实现 Excel 数据导入

React

1. Excel数据格式化

主要是三个步骤:使用input/antd-upload组件上传需要导入的文件——将excel数据处理为json数据——将json数据处理成antd-table组件所需的数据(主要是增加唯一的key)。

(1)使用antd-upload组件上传需要导入的文件。

// 不使用默认的上传方式
const handleBeforeUpload = (file: any) => {
    // 处理上传的文件 file 数据
    console.log(file);
    return false;
};

return(
    <Upload
      name="file"
      accept={accept}
      maxCount={1}
      showUploadList={{ showRemoveIcon: false }}
      beforeUpload={handleBeforeUpload}
    >
      <div className="content-btn">
        <span className="iconfont import-icon"></span>
        上传文件
      </div>
    </Upload>
)

(2)将excel数据处理为json数据。

// Excel 数据转为 json 数据
export function importsExcel(file: any) {
  //使用promise导入
  return new Promise((resolve, reject) => {
    // 通过FileReader对象读取文件
    const fileReader = new FileReader();
    //异步操作  excel文件加载完成以后触发
    fileReader.onload = (event) => {
      try {
        const { result } = event.target as any;
        // 以二进制流方式读取得到整份excel表格对象
        const workbook = XLSX.read(result, { type: "binary" });
        // 存储获取到的数据
        let data: any = []; 
        // 遍历每张工作表进行读取
        for (const sheet in workbook.Sheets) {
          if (Object.prototype.hasOwnProperty.call(workbook.Sheets, sheet)) {
            data = data.concat(
              // 将工作表转换为json数据
              XLSX.utils.sheet_to_json(workbook.Sheets[sheet])
            );
          }
        }
        // 如果Excel文件里只有一张数据工作表(比如:data),也可以不遍历,直接获取数据
        // if (Object.prototype.hasOwnProperty.call(workbook.Sheets, "data")) {
        //    // 将工作表转换为json数据            
        //    data = XLSX.utils.sheet_to_json(workbook.Sheets[sheet])
        //  }
        resolve(data); //导出数据
      } catch (e) {
        // 这里可以抛出文件类型错误不正确的相关提示
        reject("导入失败");
      }
    };
    // 以二进制方式打开文件
    fileReader.readAsBinaryString(file);
  });
}

(3)将json数据处理成antd-table组件所需的数据。

// 手动上传
const handleBeforeUpload = (file: any) => {
  // 数据处理excel=>json
  importsExcel(file).then((res: any) => {
    const ids: any = [];
    const arr = res.map((item: any, index: number) => {
      // 利用时间戳+索引,生成唯一的ID,也可以直接使用index
      const id = (Date.now() + index).toString();
      item.id = id;
      ids.push(id);
      return item;
    });
    // 表格数据
    setData(arr);
    // 导入数据空,禁用「导入」按钮
    setDisable(res.length === 0);
  });
  return false;
};

2. 可编辑表格

导入数据的表格支持编辑才是关键!!!

这里用的是 antd pro 的 EditableProTable 组件,之前的文章里有介绍过proTable的详细用法(antd 高级组件 ProComponents 之 高级表格 ProTable)。

// columns,表格列一定是要根据Excel模板提前定义好的
const columns = [{
      title: "评委手机号",
      dataIndex: "评委手机号",
      width: 166,
    },
    {
      title: "评委邮箱",
      dataIndex: "评委邮箱",
      width: 166,
    },{
      title: "操作",
      valueType: "option",
      width: "80px",
      fixed: "right",
      render: (text: string, record: any, _: any, action: any) => {
        return (
          <Space>
            <span
              onClick={() => {
                action?.startEditable?.(record.id);
              }}
            >编辑</span>
            <span
              onClick={() => {
                setData((d) => d.filter((item) => item.id !== record.id));
              }}
            >删除</span>
          </Space>
        );
      },
    }]
// 导入数据,根据需求处理数据
const handleImport = () => {
  // data
}
// 页面元素
<EditableProTable
  columns={columns}
  rowKey="id"
  scroll={{ x: 800, y: 180 }}
  value={data}
  onChange={setData}
  recordCreatorProps={false}
  editable={{
    type: "multiple",
    editableKeys,
    actionRender: (row, config, defaultDom) => [defaultDom.save, defaultDom.cancel],
    onChange: setEditableRowKeys,
  }}
/>
<Button className="organ-btn" type="primary" disabled={disable} onClick={handleImport}>开始导入</Button>

Vue

1. Excel数据格式化

处理逻辑与 React 一样,也是三个步骤:使用input/element-upload组件上传需要导入的文件——将excel数据处理为json数据——将json数据处理成element-table组件所需的数据(主要是增加唯一的key)。

(1)使用element-upload组件上传需要导入的文件。

// 页面元素
<template>
    <el-upload
      class="upload-file"
      action=""
      :accept="accept"
      :limit="1"
      :before-upload="handleBeforeUpload"
    >
      <el-button size="small" type="primary">上传文件</el-button>
    </el-upload>
</template>
// 不使用默认的上传方法
handleBeforeUpload(file) {
  // 拿到上传的文件 file 数据
  console.log(file);
  return false;
}

(2)将excel数据处理为json数据(与react的处理逻辑一样)。

(3)将json数据处理成element-table组件所需的数据。

// 手动上传
const handleBeforeUpload = (file) => {
  // 数据处理excel=>json
  importsExcel(file).then((res) => {
    const ids = [];
    const arr = res.map((item, index) => {
      // 利用时间戳+索引,生成唯一的ID
      const id = (Date.now() + index).toString();
      item.id = id;
      ids.push(id);
      return item;
    });
    // 表格数据
    this.form.list = arr;
  });
  return false;
};

2. 可编辑表格

导入数据的表格支持编辑才是关键!!!

由于 element 并没有现成的支持编辑的表格组件,所以需要利用插槽的能力来实现编辑功能。

实现思路:将表格的单元格作为表单项,通过状态控制当前行是否处于编辑状态:编辑状态下,单元格内容用el-input渲染;非编辑状态下,单元格内容用span渲染。

// 页面元素
<el-form :model="form" ref="form">
  <el-table :data="form.list" height="250" border>
    <el-table-column
      v-for="column in tableItems"
      :key="column.label"
      :label="column.label"
      :prop="column.prop"
    >
      <template slot-scope="scope">
        <el-form-item
          :rules="column.rules"
          :prop="'list.' + scope.$index + '.' + column.prop"
        >
          <el-input
            clearable
            placeholder="请输入内容"
            v-show="scope.row.show"
            v-model="scope.row[column.prop]"
          ></el-input>
          <span v-show="!scope.row.show">
            {{ scope.row[column.prop] }}
          </span>
        </el-form-item>
      </template>
    </el-table-column>
    <el-table-column label="操作">
      <template slot-scope="scope">
        <el-button @click="scope.row.show = !scope.row.show">{{
          scope.row.show ? "保存" : "编辑"
        }}</el-button>
        <el-button @click="handleDelete(scope.row)">删除</el-button>
      </template>
    </el-table-column>
  </el-table>
</el-form>
<el-button
  class="organ-btn"
  size="medium"
  type="primary"
  @click="handleImport"
>
  开始导入
</el-button>

// tableItems,表格列一定是要根据Excel模板提前定义好的
data() {
  return {
    form: {
      tableItems: [
        {
          label: "姓名",
          key: "name",
          prop: "姓名*",
          rules: [
            {
              required: 1,
              message: "未填写!"
            }
          ]
        },
        {
          label: "身份证号",
          key: "idCardNo",
          prop: "身份证号*",
          rules: [
            {
              required: 1,
              message: "未填写!"
            },
            {
              pattern: RegCard,
              message: "格式不正确!"
            }
          ]
        }
      ]
    }
  };
},
// 导入数据,根据需求处理数据
handleImport() {
  // this.form.list
},

 

posted @ 2022-11-10 19:46  shellon  阅读(1302)  评论(0编辑  收藏  举报