纯前端如何实现excel的导入导出功能

194 阅读3分钟

将表格的数据导出为excel

我们以antd的表格为例

image-20220921142353730.png

 <a-table :columns="columns" :data-source="dataSource" :pagination="false"> </a-table>
const columns = [
  {
    title: '姓名',
    dataIndex: 'userName',
    key: 'userName',
    align: 'center',
  },
  {
    title: '邮箱',
    dataIndex: 'email',
    key: 'email',
    align: 'center',
  },
  {
    title: '生日',
    dataIndex: 'birthday',
    key: 'birthday',
    align: 'center',
  }
]
​
const dataSource = [
  {
    userName: '盖伦',
    email: 'Anissa.Hintz@yahoo.com',
    birthday: '1998年03月02日 07:49',
  },
  {
    userName: '德莱厄斯',
    email: 'Melvina_Stiedemann@gmail.com',
    birthday: '1995年08月07日 15:21',
  },
  {
    userName: '内瑟斯',
    email: 'Jalon_Beer64@hotmail.com',
    birthday: '2008年09月17日 19:29',
  },
];
具体步骤
1.安装xlsx.js插件
yarn add xlsx
2.封装一个exportExcel.ts文件
import * as XLSX from "xlsx";
​
/**
 *
 * @param columns 表头
 * @param dataSource 表格数据
 * @param fileName 导出的excel文件名
 * @param sheetName excel的sheet名
 */
export const exportJsonToExcel = (
  columns,
  dataSource,
  fileName = "数据表.xlsx",
  sheetName = "sheet"
) => {
  // 将数据格式修改成excel需要的格式
  const data = dataSource.map((item) => {
    const excelItem: any = {}
    // 获取每条数据的key值
    const itemKeys = Object.keys(item)
    // 将原key值修改成表头中的title
    itemKeys.forEach((key) => {
      const excelItemKey = columns.find(
        (column: any) => key === column.key
      ).title
      excelItem[excelItemKey] = item[key]
    });
    return excelItem
  });
​
  // 讲格式化后的数据导出为excel
  downloadExcel(data, fileName, sheetName)
}
/**
 *
 * @param data format后的数据
 * @param fileName 导出的excel文件名
 * @param sheetName excel的sheet名字
 */
export const downloadExcel = (data, fileName: string, sheetName: string) => {
  const jsonWorkSheet = XLSX.utils.json_to_sheet(data)
​
  const workBook = {
    SheetNames: [sheetName],
    Sheets: {
      [sheetName]: jsonWorkSheet,
    },
  }
​
  return XLSX.writeFile(workBook, fileName)
}
3.使用
<template>
  <a-button type="primary" @click="download">导出表格</a-button>
  <a-table
    :columns="state.columns"
    :data-source="state.dataSource"
    :pagination="false"
  ></a-table>
</template>
​
<script lang="ts" setup>
import { exportJsonToExcel } from "@/utils/exportExcel"
import { reactive } from "vue";
const state = reactive({
  columns: [
    {
      title: "姓名",
      dataIndex: "userName",
      key: "userName",
      align: "center",
    },
    {
      title: "邮箱",
      dataIndex: "email",
      key: "email",
      align: "center",
    },
    {
      title: "生日",
      dataIndex: "birthday",
      key: "birthday",
      align: "center",
    },
  ],
  dataSource: [
    {
      userName: "盖伦",
      email: "Anissa.Hintz@yahoo.com",
      birthday: "1998年03月02日 07:49",
    },
    {
      userName: "德莱厄斯",
      email: "Melvina_Stiedemann@gmail.com",
      birthday: "1995年08月07日 15:21",
    },
    {
      userName: "内瑟斯",
      email: "Jalon_Beer64@hotmail.com",
      birthday: "2008年09月17日 19:29",
    },
  ],
});
​
const download = () => exportJsonToExcel(state.columns, state.dataSource);
</script>
​
4.导出效果

image-20220921144808540.png

将excel转化成json

具体步骤
1.安装xlsx.js插件
yarn add xlsx
2.封装一个uploadExcel.ts
import * as XLSX from "xlsx";
​
/**
 *
 * @param file file文件
 */
export const readerExcel = (file) => {
  const fileReader = new FileReader();
​
  // 以二进制的方式读取表格内容
  fileReader.readAsBinaryString(file);
​
  // 表格内容读取完成
  return new Promise((resolve, reject) => {
    fileReader.onload = (event) => {
      try {
        const fileData = event.target.result;
        const workbook = XLSX.read(fileData, {
          type: "binary",
        });
        // 表格是有序列表,因此可以取多个 Sheet,这里取第一个 Sheet
        const wsname = workbook.SheetNames[0];
        // 将表格内容生成 json 数据
        const sheetJson = XLSX.utils.sheet_to_json(workbook.Sheets[wsname]);
        // 处理数据
        resolve(getExcelData(sheetJson))
      } catch (e) {
        console.log(e);
        reject(false)
      }
    }
  });
}
​
/**
 *
 * @param sheetJson excel内容转化成json的数据
 */
export const getExcelData = (sheetJson) => {
  let header = [],
    dataSource = []
  if (!sheetJson.length) {
    return { header, dataSource };
  }
​
  // 判断excel最顶部是否为摘要
  const hasExcelSummary = Object.keys(sheetJson[0]).includes("__EMPTY")
​
  if (hasExcelSummary) {
    const firstItem = sheetJson.shift()
    const dataKeys = Object.values(firstItem)
    header = dataKeys.map((item) => {
      return {
        align: "center",
        dataIndex: item,
        key: item,
        title: item,
      }
    });
​
    sheetJson.forEach((item) => {
      const arr = {}
      Object.values(item).forEach((value, index) => {
        arr[dataKeys[index]] = value;
      })
      dataSource.push(arr);
    })
  } else {
    header = Object.keys(sheetJson[0]).map((item) => {
      return {
        align: "center",
        dataIndex: item,
        key: item,
        title: item,
      }
    });
    dataSource = sheetJson;
  }
  return { header, dataSource };
}
​
3.使用
<template>
  <a-upload
            accept=".csv, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
            name="file"
            :show-upload-list="false"
            :custom-request="customRequest"
            >
    <a-button>
      <upload-outlined></upload-outlined>
      上传excel
    </a-button>
    </a-upload>
​
  <a-table
    :columns="state.columns"
    :data-source="state.dataSource"
    :pagination="false"
  ></a-table>
</template>
​
<script lang="ts" setup>
import { UploadOutlined } from "@ant-design/icons-vue"
import { readerExcel } from "@/utils/uploadExcel"
import { reactive } from "vue";
const state = reactive({
  columns: [],
  dataSource: [],
});
​
const customRequest = async (event) => {
  const file = event.file;
  const { header, dataSource } = await readerExcel(file)
  state.columns = header;
  state.dataSource = dataSource;
};
</script>
​
4.导入效果

image-20220921155549278.png

完整代码

1.vue文件
<template>
  <a-space>
    <a-button type="primary" @click="download">导出表格</a-button>
    <a-upload
      accept=".csv, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
      name="file"
      :show-upload-list="false"
      :custom-request="customRequest"
    >
      <a-button>
        <upload-outlined></upload-outlined>
        上传excel
      </a-button>
    </a-upload>
  </a-space>
  <a-table
    :columns="state.columns"
    :data-source="state.dataSource"
    :pagination="false"
  ></a-table>
</template><script lang="ts" setup>
import { UploadOutlined } from "@ant-design/icons-vue"
import { exportJsonToExcel } from "@/utils/exportExcel"
import { readerExcel } from "@/utils/uploadExcel"
import { reactive } from "vue";
const state = reactive({
  columns: [
    {
      title: "姓名",
      dataIndex: "userName",
      key: "userName",
      align: "center",
    },
    {
      title: "邮箱",
      dataIndex: "email",
      key: "email",
      align: "center",
    },
    {
      title: "生日",
      dataIndex: "birthday",
      key: "birthday",
      align: "center",
    },
  ],
  dataSource: [
    {
      userName: "盖伦",
      email: "Anissa.Hintz@yahoo.com",
      birthday: "1998年03月02日 07:49",
    },
    {
      userName: "德莱厄斯",
      email: "Melvina_Stiedemann@gmail.com",
      birthday: "1995年08月07日 15:21",
    },
    {
      userName: "内瑟斯",
      email: "Jalon_Beer64@hotmail.com",
      birthday: "2008年09月17日 19:29",
    },
  ],
});
​
const download = () => exportJsonToExcel(state.columns, state.dataSource);
​
const customRequest = async (event) => {
  const file = event.file;
  const { header, dataSource } = await readerExcel(file)
  state.columns = header;
  state.dataSource = dataSource;
};
</script>
2.@/utils/exportExcel.ts文件
import * as XLSX from "xlsx";
​
/**
 *
 * @param columns 表头
 * @param dataSource 表格数据
 * @param fileName 导出的excel文件名
 * @param sheetName excel的sheet名
 */
export const exportJsonToExcel = (
  columns,
  dataSource,
  fileName = "数据表.xlsx",
  sheetName = "sheet"
) => {
  // 将数据格式修改成excel需要的格式
  const data = dataSource.map((item) => {
    const excelItem: any = {}
    // 获取每条数据的key值
    const itemKeys = Object.keys(item)
    // 将原key值修改成表头中的title
    itemKeys.forEach((key) => {
      const excelItemKey = columns.find(
        (column: any) => key === column.key
      ).title
      excelItem[excelItemKey] = item[key]
    });
    return excelItem
  });
​
  // 讲格式化后的数据导出为excel
  downloadExcel(data, fileName, sheetName)
}
/**
 *
 * @param data format后的数据
 * @param fileName 导出的excel文件名
 * @param sheetName excel的sheet名字
 */
export const downloadExcel = (data, fileName: string, sheetName: string) => {
  const jsonWorkSheet = XLSX.utils.json_to_sheet(data)
​
  const workBook = {
    SheetNames: [sheetName],
    Sheets: {
      [sheetName]: jsonWorkSheet,
    },
  }
​
  return XLSX.writeFile(workBook, fileName)
}
​
3.@/utils/uploadExcel文件
import * as XLSX from "xlsx";
​
/**
 *
 * @param file file文件
 */
export const readerExcel = (file) => {
  const fileReader = new FileReader();
​
  // 以二进制的方式读取表格内容
  fileReader.readAsBinaryString(file);
​
  // 表格内容读取完成
  return new Promise((resolve, reject) => {
    fileReader.onload = (event) => {
      try {
        const fileData = event.target.result;
        const workbook = XLSX.read(fileData, {
          type: "binary",
        });
        // 表格是有序列表,因此可以取多个 Sheet,这里取第一个 Sheet
        const wsname = workbook.SheetNames[0];
        // 将表格内容生成 json 数据
        const sheetJson = XLSX.utils.sheet_to_json(workbook.Sheets[wsname]);
        // 处理数据
        resolve(getExcelData(sheetJson))
      } catch (e) {
        console.log(e);
        reject(false)
      }
    }
  });
}
​
/**
 *
 * @param sheetJson excel内容转化成json的数据
 */
export const getExcelData = (sheetJson) => {
  let header = [],
    dataSource = []
  if (!sheetJson.length) {
    return { header, dataSource };
  }
​
  // 判断excel最顶部是否为摘要
  const hasExcelSummary = Object.keys(sheetJson[0]).includes("__EMPTY")
​
  if (hasExcelSummary) {
    const firstItem = sheetJson.shift()
    const dataKeys = Object.values(firstItem)
    header = dataKeys.map((item) => {
      return {
        align: "center",
        dataIndex: item,
        key: item,
        title: item,
      }
    });
​
    sheetJson.forEach((item) => {
      const arr = {}
      Object.values(item).forEach((value, index) => {
        arr[dataKeys[index]] = value;
      })
      dataSource.push(arr);
    })
  } else {
    header = Object.keys(sheetJson[0]).map((item) => {
      return {
        align: "center",
        dataIndex: item,
        key: item,
        title: item,
      }
    });
    dataSource = sheetJson;
  }
  return { header, dataSource };
}
​