Vue3 实现 Excel 文件导入导出功能详解

330 阅读3分钟

Vue3 实现 Excel 文件导入导出功能详解

本文基于 Element Plus + xlsx 库实现一个支持 Excel 文件导入导出的 Vue3 组件,适合用于教学场景演示前后端交互之外的纯前端文件处理方案。

一、功能概述

该组件实现了以下核心功能:

  • Excel 文件导入:通过文件选择器加载 .xlsx/.xls 格式文件
  • 数据动态渲染:将 Excel 表格内容实时展示在 Vue 响应式表格中
  • 数据格式转换:自动完成 Sheet 数据与 JSON 对象的双向转换
  • Excel 文件导出:将当前表格数据导出为标准 Excel 文件

二、技术栈依赖

  • Vue3 (Composition API 模式)
  • Element Plus (UI 组件库)
  • SheetJS xlsx (Excel 处理库)

需通过 npm 安装依赖:

npm install xlsx element-plus

三、核心代码解析

1. 模板结构 (template 部分)

<template>
  <!-- 导入组件 -->
  <el-upload
    class="upload-demo"
    action=""
    :auto-upload="false"        # 禁用自动上传
    :show-file-list="false"     # 隐藏文件列表
    :on-change="handleImport"   # 文件选择回调
    accept=".xlsx, .xls"        # 限制文件类型
  >
    <el-button type="primary">导入Excel</el-button>
  </el-upload>

  <!-- 导出按钮 -->
  <el-button type="primary" @click="exportExcel">导出</el-button>

  <!-- 数据表格 -->
  <el-table :data="tableData" border style="width: 70vw">
    <!-- 列定义由 handleImport 动态生成 -->
  </el-table>
</template>

2. 脚本逻辑 (script setup 部分)

响应式数据定义
import { ref } from "vue";
import { read, writeFileXLSX, utils } from "xlsx"; // xlsx 核心方法
const tableHeaders = ref([]);  // 存储表头信息
const tableData = ref([...]);  // 存储表格数据
文件导入处理
const handleImport = (file) => {
  // 文件类型校验
  if(!/\.xlsx|\.xls$/.test(file.name)) {
    ElMessage.error("仅支持 Excel 文件");
    return;
  }

  const reader = new FileReader();
  reader.onload = (e) => {
    try {
      // 读取文件内容
      const data = new Uint8Array(e.target.result);
      const workbook = read(data, { type: "array" });
      
      // 解析第一个工作表
      const firstSheetName = workbook.SheetNames[0];
      const worksheet = workbook.Sheets[firstSheetName];
      
      // 转换为二维数组 [[表头], [行数据]...]
      const jsonData = utils.sheet_to_json(worksheet, { header: 1 });
      
      // 提取表头
      tableHeaders.value = jsonData[0];
      
      // 构造对象数组
      tableData.value = jsonData.slice(1).map(row => {
        return tableHeaders.value.reduce((obj, header, index) => {
          obj[header] = row[index] || "";
          return obj;
        }, {});
      });
      
      ElMessage.success("导入成功");
    } catch (error) {
      ElMessage.error(`解析失败: ${error.message}`);
    }
  };
  
  // 以 ArrayBuffer 格式读取文件
  reader.readAsArrayBuffer(file.raw);
};
文件导出处理
const exportExcel = () => {
  // 将对象数组转为工作表
  const worksheet = utils.json_to_sheet(tableData.value);
  
  // 创建工作簿并添加工作表
  const workbook = utils.book_new();
  utils.book_append_sheet(workbook, worksheet, "Sheet1");
  
  // 触发文件下载
  writeFileXLSX(workbook, "data.xlsx");
};

四、关键技术点解析

1. 文件读取原理

  • 使用 FileReaderreadAsArrayBuffer 方法读取二进制数据
  • 通过 Uint8Array 类型数组保证二进制数据完整性
  • xlsx 库支持多种解析模式(array/binary/base64)

2. 数据转换机制

转换方向方法特点
Excel → JSONsheet_to_json(sheet, {header:1})header=1 返回二维数组
JSON → Exceljson_to_sheet(json)需要标准对象数组

**

3. 性能优化建议

  • 大文件处理:添加 loading 状态提示
  • 错误处理:增强异常捕获和用户反馈
  • 类型映射:自定义日期等特殊格式处理

五、完整代码

<template>
  <div>
    <el-upload
      class="upload-demo"
      action=""
      :auto-upload="false"
      :show-file-list="false"
      :on-change="handleImport"
      accept=".xlsx, .xls"
    >
      <el-button type="primary">导入Excel文件</el-button>
    </el-upload>
  </div>
  <el-button type="primary" @click="exportExcel">导出</el-button>
  <el-table :data="tableData" border style="width: 70vw">
    <el-table-column prop="date" label="Date" width="180" />
    <el-table-column prop="name" label="Name" width="180" />
    <el-table-column prop="address" label="Address" />
  </el-table>
</template>

<script setup>
import { ref } from "vue";
import { read, writeFileXLSX, utils } from "xlsx";
import { ElMessage } from "element-plus";
const tableHeaders = ref([]);

const tableData = ref([
  {
    date: "2016-05-03",
    name: "Tom",
    address: "No. 189, Grove St, Los Angeles",
  },
  {
    date: "2016-05-02",
    name: "Tom",
    address: "No. 189, Grove St, Los Angeles",
  },
  {
    date: "2016-05-04",
    name: "Tom",
    address: "No. 189, Grove St, Los Angeles",
  },
  {
    date: "2016-05-01",
    name: "Tom",
    address: "No. 189, Grove St, Los Angeles",
  },
]);

// 处理文件导入
const handleImport = (file) => {
  // 检查文件类型
  const isExcel = /\.(xlsx|xls)$/.test(file.name);
  if (!isExcel) {
    ElMessage.error("只能上传Excel文件!");
    return;
  }

  const reader = new FileReader();
  reader.onload = (e) => {
    try {
      const data = new Uint8Array(e.target.result);
      const workbook = read(data, { type: "array" });
      const firstSheetName = workbook.SheetNames[0];
      const worksheet = workbook.Sheets[firstSheetName];

      // 转换JSON数据
      const jsonData = utils.sheet_to_json(worksheet, { header: 1 });

      // 处理表头
      tableHeaders.value = jsonData[0];

      // 处理表格内容(跳过表头)
      tableData.value = jsonData.slice(1).map((row) => {
        return tableHeaders.value.reduce((obj, header, index) => {
          obj[header] = row[index] || "";
          return obj;
        }, {});
      });

      ElMessage.success("文件导入成功");
    } catch (error) {
      ElMessage.error("文件解析失败: " + error.message);
    }
  };
  reader.readAsArrayBuffer(file.raw);
};

const exportExcel = () => {
  const worksheet = utils.json_to_sheet(tableData.value);
  const workbook = utils.book_new();
  utils.book_append_sheet(workbook, worksheet, "Sheet1");
  writeFileXLSX(workbook, "data.xlsx");
};
</script>

六、扩展应用场景

  1. 添加多 sheet 支持
  2. 实现列名映射配置
  3. 结合 axios 实现服务端文件处理
  4. 添加数据校验和清洗功能