基于Vue2技术栈读取Excel数据并回显在页面控件上

在Vue 2中导入Excel数据并将其回显在页面上,需要使用 XLSX 库, 这个库可以用来解析 Excel文件。首先先下载 XLSX 库依赖,其次封装上传Excel的组件,最后在父组件中使用子组件。

关于 XLSX 的详细介绍移步:https://www.cnblogs.com/ajaemp/p/12880847.html

准备工作就绪,需要在父组件上添加一个按钮触发文件上传的弹框打开或关闭。

复制代码
<!-- 触发批量弹出框按钮 -->
<div>
   <el-button type="primary" size="mini" @click="showImportDataDialog">批量导入</el-button>
</div>

在data中定义变量uploadVisible 的值为 false, 然后在 methods 中定义 showImportDataDialog 方法。

复制代码
 // 显示缺失项批量上传窗口
    showImportDataDialog() {
      this.uploadVisible = true
    },
  1. 安装 XLSX 库

    npm install xlsx

  2. 封装上传Excel的组件

这里我将上传附件的组件视为公共组件,所以在 src/components 目录下新建了一个 FileUpload 目录,在该目录下新建 XXX.vue 组件。该组件的主要作用就是父组件点击批量导入按钮时,打开一个对话框,可以下载导入模板,并且支持拉拽的方式上传Excel数据,然后将读取到的Excel数据以集合的方式返回给父组件。通过$emit () 触发父组件的事件。

复制代码
<!-- 读取Excel数据在前端页面回显 --->
 <template>
  <!-- 上传弹窗对话框, 模板数据导入 -->
  <el-dialog
    :title="dialogTitle"
    :modal="false"
    append-to-body
    :visible.sync="dialogVisible"
    width="500px"
    @close="closeDialog">
    
    <el-row>
      无模板?点击
      <el-button size="medium" type="text"  @click="downFile">模板下载</el-button>
    </el-row>
    <!-- Element UI 上传组件 -->
    <el-upload 
      class="upload-demo" 
      ref="upload"
      drag 
      action
      :limit="1"
      :file-list="fileList"
      :auto-upload="false"
      :before-upload="beforeUpload"
      :on-change="handleFileChange"
      :extra-params="extraParams"
      :dialog-title="dialogTitle">
      <!-- :http-request="uploadHttpRequest" -->
      <i class="el-icon-upload"></i>
      <div class="el-upload__text">
        将文件拖到此处,或<em>点击上传</em>
      </div>
    </el-upload>
    
    <!-- 弹窗底部按钮 -->
    <span slot="footer" class="dialog-footer">
      <el-button type="primary" size="mini" @click="uploadMultipleFiles" :loading="uploading">{{ uploading ? '上传中...' : '开始上传' }}</el-button>
      <!-- <el-button type="primary" size="mini" @click="submitUpload()" :loading="uploading">{{ uploading ? '上传中...' : '开始上传' }}</el-button> -->
      <el-button size="mini" @click="cancelUpload">取 消</el-button>
    </span>
  </el-dialog>
</template>

<script>
import * as XLSX from 'xlsx';
import { reqFileUpload, reqDownTemplate } from '@/api/common.js'
import { resolve } from 'core-js/fn/promise';
export default {
  name: 'ShowImportData',
  props: {
    // 控制弹窗显示/隐藏
    visible: {
      type: Boolean,
      default: false
    },
    // 上传类型,用于区分不同业务
    dialogTitle: {
      type: String,
      default: '导入数据'
    },
    // 模板名称
    tempFileName: {
      type: String,
      default: '导入数据模板'
    },
    // 额外的参数
    extraParams: {
      Type: Object,
      default: () => ({})
    },
    

  },
  data() {
    return {
      dialogVisible: this.visible, // 弹窗显示状态
      fileList: [],               // 文件
      uploading: false,           // 上传状态
    };
  },
  watch: {
    // 监听visible变化,同步到dialogVisible
    visible(newVal) {
      this.dialogVisible = newVal;
    },
    // 监听dialogVisible变化,通知父组件
    dialogVisible(newVal) {
      this.$emit('update:visible', newVal);
    }
  },
  methods: {
    handleFileChange(file, fileList) {
      // 保存所有原始文件对象
      this.fileList = fileList.map(f => f.raw);
    },

     // 上传文件之前的钩子:判断上传文件格式、大小等,若返回false则停止上传
    beforeUpload(file) {
        // 文件类型
        const isType = file.type === "application/vnd.ms-excel";
        const isTypeComputer =
            file.type ===
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
        const fileType = isType || isTypeComputer;
        if (!fileType) {
            this.$message.error("上传文件只能是xls/xlsx格式!");
        }
        // 文件大小限制为10M
        /* const fileLimit = file.size / 1024 / 1024 < 10;
        if (!fileLimit) {
            this.$message.error("上传文件大小不超过10M!");
        }
        return fileType && fileLimit; */

        return fileType;
        
    },

    async uploadMultipleFiles() {
      if (this.fileList.length == 0) {
       this.$message.warning('请选择文件')
       return false;
      }
      this.uploading = true; // 显示按钮加载中
      try {
         //得到文件
        const file = this.fileList[0]
        const jsonData = await this.readExcelFile(file)  // 调用excel处理方法
        if (!jsonData || jsonData.length === 0) {
          this.$message.warning('Excel文件中没有数据');
          return;
        }
        // 通过事件向父组件传递数据
        this.$emit('import-success', jsonData);
        this.$message.success(`成功导入 ${jsonData.length} 条数据`);
         // 清空文件列表
        this.fileList = [];
        // this.$refs.upload.clearFiles()
      } catch(error) {
        console.error('导入失败:', error);
        this.$message.error('导入失败:' + error.message);
        this.$emit('import-error', error);
      } finally {
        this.uploading = false;  // 显示按钮立即上传
      }
    },

    // excel 处理
    readExcelFile(file) {
      return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.readAsArrayBuffer(file);  // 一次性将整个 Excel 文件读取到内存中
        reader.onload = (e) => {
          try {
            const data = new Uint8Array(reader.result);
            const workbook = XLSX.read(data, { type: 'array' });
            // 因为excle分很多页数,找到你对应的那一页
            // 假设我们只读取第一个工作表
            const firstSheetName = workbook.SheetNames[0];
            const worksheet = workbook.Sheets[firstSheetName];
            // 将工作表转换为JSON数组并赋值
            const jsonData = XLSX.utils.sheet_to_json(worksheet);
            // console.log('解析后的数据:', jsonData);
            resolve(jsonData);
          } catch(error) {
            reject(new Error('解析Excel失败:' + error.message));
          }
        };
      } 
    )},

    //  // 自定义上传方法,param是默认参数,可以取得file文件信息,具体信息如下图
    // async uploadHttpRequest(param) {
    //     console.log(1)
    //     console.log(param.file)
    //   this.uploading = true; // 显示按钮加载中
    //   // FormData对象,添加参数只能通过append('key', value)的形式添加
    //   const formData = new FormData();
    //   // 添加文件对象
    //   formData.append("file", param.file);
    //   /*const config = {
    //     headers: {
    //       "Content-Type": "multipart/form-data"
    //     }
    //   };*/

    //   console.log(param.file)
    // //   const res = await reqFileUpload(this.uploadUrl, formData);
    // //   if (res.code == 200) {
    // //       this.$emit("closeUpload");
    // //       this.uploading = false;
    // //       this.$message.success(res.message);
    // //   } else {
    // //     this.$refs.upload.clearFiles(); // 清除上传文件对象
    // //     this.fileList = []; // 清空选择的文件列表
    // //     this.uploading = false;
    // //     this.$message.error(res.message)
    // //   }
    // },

    // 上传至服务器
    submitUpload() {
      this.$refs.upload.submit();
    },
    cancelUpload() {
      this.uploading = false;  // 按钮重置
      this.$refs.upload.clearFiles(); // 清除上传文件对象
      this.fileList = []; // 清空选择的文件列表
      this.$emit("close-upload");
    },
    closeDialog() {
      this.$refs.upload.clearFiles(); // 清除上传文件对象
      this.fileList = []; // 清空选择的文件列表
      this.$emit('close-upload');
    },
    // 下载模板
    async downFile() {
      try {
        const response = await reqDownTemplate(this.tempFileName);
        // 创建 Blob URL
        const blobUrl = URL.createObjectURL(response);
        // 创建下载链接并触发下载
        const link = document.createElement('a');
        link.href = blobUrl;
        link.download = this.tempFileName;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        // 释放 Blob URL 内存
        URL.revokeObjectURL(blobUrl);
      } catch (error) {
        console.log(error)
      }
    },
  } 
};
</script>

<style scoped>
.upload-demo {
  text-align: center;
}
.el-upload-dragger {
  width: 100%;
}
</style>
  1. 父组件使用子组件

在父组件中引入子组件,然后注册该组件,之后就可以使用该组件。

在script 块内引入子组件

复制代码
import ShowImportData from '@/components/FileUpload/showImportData.vue'  // 读取excel数据并返回

注册组件

复制代码
 components: {
    ShowImportData
  },

在父组件中使用组件

其中closeUpload (关闭上传附件弹出框)、 importError (读取失败时触发父组件的方法)、importSuccess (读取失败时触发父组件的方法)都是子组件触发父组件的执行方法。

复制代码
<!-- 批量导入上传组件 -->
<show-import-data
    :visible.sync="uploadVisible"
    :temp-file-name="templateFileName"
    :accept="'.xlsx,.xls'"
    @close-upload="closeUpload"
    @import-error="importError"
    @import-success="importSuccess"/>

在methods 中的方法

复制代码
 // 关闭上传组件弹框
    closeUpload() {
      this.uploadVisible = false
    },
    // 处理子组件触发的导入数据成功败的函数
    importSuccess(data) {
      // 关闭上传弹窗
      this.closeUpload()
      // 生成数据行并赋值,通过excel表头数据读取该列单元格的值
      data.forEach(item => {
        this.correctionForm.rowList.push({
          rowIndex: this.nextId++,
          state: item.状态, // 状态
          fileList: [], // 附件列表
        })
      })
    },
  
    // 处理子组件触发的导入数据失败的函数
    importError(error) {
      this.$message.error(error.message || '导入失败');
      this.closeUpload()
    },
相关推荐
暗冰ཏོ12 天前
《2026 Vue2 + Vue3 完整学习指南:基础语法、路由缓存、登录拦截、项目实战与面试题》
前端·vue.js·vue·vue3·vue2
Json____1 个月前
node-电商商城平台实战项目(管理端+用户端)
node·vue2·express·element-ui·电商商城
路光.1 个月前
ReferenceError:Can‘t find variable:structureClone
前端·javascript·html·vue2
曲幽2 个月前
Vue 3 组合式 API 香是香,但从Vue2迁移时你可别像我当初一样踩进这 3 个深坑里
vue3·vue2·web·watch·data·this·reactive·setup·ref
雨季mo浅忆2 个月前
第四项目梳理
前端·面试·vue2
雨季mo浅忆2 个月前
2999第三项目梳理
vue2·vant
儒雅的烤地瓜2 个月前
Vue | 一文详解Vue3中的Setup()函数
vue.js·vue3·vue2·组合式api·setup函数·option api
A_nanda2 个月前
Vue项目升级
前端·vue3·vue2
雨季mo浅忆3 个月前
el-upload二次封装带表格校验组件
javascript·vue2