Vue3+TypeScript+ Element Plus 从Excel文件导入数据,无后端(点击按钮,选择Excel文件,由前端解析数据)

在 Vue 3 + TypeScript + Element Plus 中实现文件导入功能,可以通过以下步骤完成:

1. 安装依赖

bash

复制

下载

npm install xlsx  # 用于解析Excel文件
npm install @types/xlsx -D  # TypeScript类型声明

2. 组件实现

vue

复制

下载

<template><div><!-- 隐藏的原生文件输入 --><input type="file"ref="fileInput"@change="handleFileChange"accept=".xlsx, .xls, .csv"style="display: none"><!-- Element Plus 按钮触发文件选择 --><el-button type="primary"@click="triggerFileInput"><el-icon><upload /></el-icon>导入文件</el-button><!-- 显示导入数据 --><el-table :data="tableData" v-if="tableData.length"><el-table-column v-for="(header, index) in tableHeaders" :key="index":prop="header":label="header"/></el-table></div>
</template><script setup lang="ts">
import { ref } from 'vue';
import * as XLSX from 'xlsx';
import type { UploadInstance } from 'element-plus';
import { ElMessage } from 'element-plus';// 类型定义
interface TableData {[key: string]: any;
}// 响应式数据
const fileInput = ref<HTMLInputElement | null>(null);
const tableData = ref<TableData[]>([]);
const tableHeaders = ref<string[]>([]);// 触发文件选择
const triggerFileInput = () => {if (fileInput.value) {fileInput.value.value = ''; // 重置以允许重复选择相同文件fileInput.value.click();}
};// 处理文件选择
const handleFileChange = (e: Event) => {const input = e.target as HTMLInputElement;if (!input.files?.length) return;const file = input.files[0];const reader = new FileReader();reader.onload = (e) => {try {const data = e.target?.result as ArrayBuffer;processExcel(data);} catch (error) {console.error('文件解析失败:', error);ElMessage.error('文件解析失败');}};reader.readAsArrayBuffer(file);
};// 处理Excel数据
const processExcel = (data: ArrayBuffer) => {const workbook = XLSX.read(data, { type: 'array' });const firstSheetName = workbook.SheetNames[0];const worksheet = workbook.Sheets[firstSheetName];// 转换为JSONconst jsonData: TableData[] = XLSX.utils.sheet_to_json(worksheet);if (jsonData.length > 0) {// 获取表头tableHeaders.value = Object.keys(jsonData[0]);tableData.value = jsonData;ElMessage.success(`成功导入 ${jsonData.length} 条数据`);} else {ElMessage.warning('未找到有效数据');}
};
</script>

3. 功能说明

  1. 文件选择触发

    • 隐藏原生 <input type="file"> 元素

    • 通过Element Plus按钮触发文件选择

  2. 文件处理流程

    • 使用 FileReader 读取文件内容

    • 通过 xlsx 库解析Excel数据

    • 将工作表转换为JSON格式

    • 提取表头和数据

  3. 数据展示

    • 使用Element Plus的 <el-table> 动态渲染数据

    • 自动识别表头生成列

4. 扩展功能建议

  1. 文件类型验证

ts

复制

下载

// 在handleFileChange中添加
const validTypes = ['application/vnd.ms-excel','application/vnd.openxmlformats-officedocument.spreadsheetml.sheet','text/csv'
];if (!validTypes.includes(file.type)) {ElMessage.error('请上传Excel或CSV文件');return;
}
  1. 数据清洗

ts

复制

下载

// 在processExcel后添加数据清洗逻辑
const cleanData = jsonData.map(item => {// 示例:移除空值return Object.fromEntries(Object.entries(item).filter(([_, value]) => value !== null));
});
  1. 上传到服务器

ts

复制

下载

import axios from 'axios';const uploadData = async () => {try {const response = await axios.post('/api/import', {data: tableData.value,headers: tableHeaders.value});ElMessage.success(`服务器导入成功: ${response.data.message}`);} catch (error) {ElMessage.error('服务器导入失败');}
};

5. 注意事项

  1. 大文件处理

    • 添加文件大小限制

    • 使用Web Worker防止界面卡顿

    • 分片读取处理

  2. 安全性

    • 验证文件内容格式

    • 防止XSS攻击(特别是CSV文件)

    • 服务器端二次验证

  3. 用户体验

    • 添加加载状态

    • 显示解析进度

    • 错误文件类型提示

vue

复制

下载

<!-- 加载状态示例 -->
<el-button :loading="isLoading" @click="triggerFileInput"
>导入文件
</el-button>

这个实现方案提供了完整的文件导入流程,包括前端解析和展示,可根据实际需求扩展服务器上传功能。

6.实例代码

点击按钮,选择Excel文件,由前端解析数据,实现从Excel文件导入数据

1、导入的黄金搭档【按钮 + 输入框】,按钮显示充门面,输入框隐藏干实事

2、导入核心功能封装成工具

在组件中使用

ReagentInDialog.vue

<script setup lang="ts" name="ReagentInDialog">import { importExcelFileByClient } from "@/utils/excelUtils";// 文件输入实例对象
const fileInputRef = ref<HTMLInputElement | null>(null);// 导入
const onImportClick = () => {// 模拟点击元素if (fileInputRef.value) {// 重置以允许重复选择相同文件fileInputRef.value.value = "";fileInputRef.value.click();}
};// 点击【导入】触发
const handleImportByClient = async (e: Event) => {// 获取文件对象const input = e.target as HTMLInputElement;if (!input.files?.length) return;const file = input.files[0];// 键值列名映射表const keyColMap: Record<string, string> = {编号: "materialNo",试剂编号: "reagentNo",试剂名称: "reagentName",规格型号: "reagentSpec",单位: "reagentUnit",批号: "batchNo",有效期至: "validityDate",入库数量: "amount",入库金额: "total"};// 导入文件,由前端解析文件,获取数据const dataList = <IReagentInByCkDetail[]>await importExcelFileByClient(file, keyColMap);// 加载数据dataList.forEach((item) => {tableData.value.push({id: -(tableData.value.length + 1),materialNo: (tableData.value.length + 1).toString(),reagentNo: item.reagentNo,reagentName: item.reagentName});});// 等待 DOM 渲染完毕await nextTick();// 全选tableRef.value?.toggleAllSelection();
};</script><template><el-button class="in-btn" type="primary" plain @click="onImportClick">导入</el-button><!-- 文件输入元素,不显示,通过点击按钮【导入】执行 onImportClick,模拟点击该元素,从而触发 handleImportByClient 事件 --><inputref="fileInputRef"type="file"accept=".xls, .xlsx"style="display: none"@change="handleImportByClient" /></template>

导入工具

excelUtils.ts

import { convertFileSize } from "@/utils/pubUtils";
import { ElMessage } from "element-plus";
import * as xlsx from "xlsx";/*** 从Excel文件导入数据,由前端解析文件,获取数据* @param file 导入文件* @param colKeyMap 列名键值映射,key --> value,如:excel中列名为【样品编号】,其键值设置对应为【sampleNo】* @returns 列表数据*/
export async function importExcelFileByClient(file: any, keyColMap: Record<string, string>) {// 定义及初始化需要返回的列表数据let dataList: any[] = [];// 文件校验// 校验文件名后缀if (!/\.(xls|xlsx)$/.test(file.name)) {ElMessage.warning("请导入excel文件!");return dataList;}// 校验文件格式// application/vnd.ms-excel 为 .xls文件// application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 为 .xlsx文件else if (file.type !== "application/vnd.ms-excel" &&file.type !== "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") {ElMessage.warning("excel文件已损坏,请检查!");return dataList;}// 校验文件大小else if (convertFileSize(file.size, "B", "MB") > 1) {ElMessage.warning("文件大小不能超过1MB!");return dataList;}// 文件读取let fileReader = new FileReader();// 以二进制的方式读取文件内容fileReader.readAsArrayBuffer(file);// 等待打开加载完成文件,其实就是执行 fileReader.onloadend = () => {},返回 true 表示成功,false 表示失败let result = await loadedFile(fileReader);if (result) {// 获取文件数据let fileData = fileReader.result;// 读取工作薄 workbooklet workbook = xlsx.read(fileData, { type: "array" });// 表格是有序列表,因此可以取多个 Sheet,这里取第一个 Sheetlet sheet = workbook.SheetNames[0];// 将表格内容生成 json 数据let sheetJson = xlsx.utils.sheet_to_json(workbook.Sheets[sheet]);// 限制最多只能导入1000条数据,预防恶意操作导入超大量数据if (sheetJson.length > 1000) {ElMessage.warning("一次最多只能导入1000条数据!");return dataList;}// 格式化表格json数据 sheetJson,转换成在excel表中看到的那种直观数据dataList = formatSheetJson(sheetJson, keyColMap);}// 返回列表数据return dataList;
}/*** 加载文件* 是否打开加载了文件,因为 fileReader.onloadend 是异步任务,程序执行时,不会执行完 onloadend 内部的代码再往下执行,* 而是执行到 onloadend 内部时,又跳出 onloadend,执行 onloadend 外部的代码* 故将 fileReader.onloadend 用 Promise<boolean> 返回对象包裹,程序执行时用await loadedFile,这样就会执行完 onloadend 内部的代码再往下执行* 【要让 异步任务 不异步执行,可以用一个方法将其包裹,并且该方法返回Promise对象,执行该方法时用 await】* @param fileReader 文件读取器* @returns 响应结果*/
function loadedFile(fileReader: FileReader): Promise<boolean> {return new Promise((resolve, reject) => {// 读取文件,文件读取完成触发该事件fileReader.onloadend = () => {try {// 成功打开加载完文件数据resolve(true);} catch (error) {// 失败reject(false);}};});
}/*** 将表格json数据 sheetJson 转换成列表数据* @param sheetJson 表格json数据* @param colKeyMap 列名键值映射,key --> value,如:excel中列名为【样品编号】,其键值设置对应为【sampleNo】* @returns 列表数据*/
function formatSheetJson(sheetJson: any[], keyColMap: Record<string, string>): any[] {// 无内容,返回空数据if (!sheetJson.length) return [];let result = sheetJson;// 判断是否有表头,有表头的话,sheetJson对象必然有__EMPTY属性let hasTableHead = !!sheetJson[0]["__EMPTY"];// 拥有表头的数据,重新转换列标题if (hasTableHead) {// 获取对象中所有属性的名称let header = sheetJson.shift();// 数据let data: any[] = [];// 遍历对象所有属性(列信息)Object.keys(header).forEach((key) => {// 遍历数据(行信息)sheetJson.forEach((item, index) => {// 构建对象内容let obj = data[index] || {};// 对象增加属性,并给属性赋值数据(行列信息)obj[header[key]] = item[key];// 最终给数据行数据赋值对象内容data[index] = obj;});});result = data;}// 将表格对应的文字转换为 keylet dataList: any[] = [];result.forEach((item) => {let newItem: any = {};Object.keys(item).forEach((key) => {newItem[keyColMap[key]] = item[key];});dataList.push(newItem);});// 返回列表数据return dataList;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/84464.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/84464.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一些torch函数用法总结

1.torch.nonzero(input, *, as_tupleFalse) 作用&#xff1a;在PyTorch中用于返回输入张量中非零元素的位置索引。 返回值&#xff1a;返回一个张量&#xff0c;每行代表一个非零元素的索引。 参数含义&#xff1a; &#xff08;1&#xff09;input:输入的PyTorch 张量。 …

moments_object_model_3d这么理解

这篇文章是我对这个算子的理解,和三个输出结果分别用在什么地方 算子本身 moments_object_model_3d( : : ObjectModel3D, MomentsToCalculate : Moments) MomentsToCalculate:对应三个可选参数,分别是 1, mean_points: 就是点云在xyz方向上坐标的平均值 2, central_m…

性能测试|数据说话!在SimForge平台上用OpenRadioss进行汽车碰撞仿真,究竟多省时?

Radioss是碰撞仿真领域中十分成熟的有限元仿真软件&#xff0c;可以对工程中许多非线性问题进行求解&#xff0c;例如汽车碰撞、产品跌落、导弹爆炸、流固耦合分析等等。不仅可以提升产品的刚度、强度、碰撞的安全性能等&#xff0c;还可以在降低产品研发成本的同时提升研发效率…

数据结构学习——KMP算法

//KMP算法 #include <iostream> #include <string> #include <vector> #include <cstdlib>using namespace std;//next数组值的推导void getNext(string &str, vector<int>& next){int strlong str.size();//next数组的0位为0next[0]0;…

博士,超28岁,出局!

近日&#xff0c;长沙市望城区《2025年事业引才博士公开引进公告》引发轩然大波——博士岗位年龄要求28周岁及以下&#xff0c;特别优秀者也仅放宽至30周岁。 图源&#xff1a;网络 这份规定让众多"高龄"博士生直呼不合理&#xff0c;并在社交平台掀起激烈讨论。 图源…

使用Nuitka打包Python程序,编译为C提高执行效率

在 Python 的世界里&#xff0c;代码打包与发布一直是开发者关注的重要话题。前面我们介绍了Pyinstaller的使用&#xff0c;尽管 PyInstaller 是最常用的工具之一&#xff0c;但对于性能、安全性、兼容性有更高要求的项目&#xff0c;Nuitka 正迅速成为更优的选择。本文将全面介…

基于机器学习的恶意请求检测

好久没写文章了&#xff0c;忙毕业设计ING&#xff0c;终于做好了发出来。 做了针对恶意URL的检测&#xff0c;改进了杨老师这篇参考文献的恶意请求检测的方法 [网络安全自学篇] 二十三.基于机器学习的恶意请求识别及安全领域中的机器学习-CSDN博客 选择使用了XGBoost算法进…

深入理解XGBoost(何龙 著)学习笔记(五)

深入理解XGBoost&#xff08;何龙 著&#xff09;学习笔记&#xff08;五&#xff09; 本文接上一篇&#xff0c;内容为线性回归&#xff0c;介绍三部分&#xff0c;首先介绍了"模型评估”&#xff0c;然后分别提供了线性回归的模型代码&#xff1a;scikit-learn的Linear…

工业级MySQL基准测试专家指南

工业级MySQL基准测试专家指南 一、深度风险识别增强版 风险类型典型表现进阶检测方案K8s存储性能抖动PVC卷IOPS骤降50%使用kubestone进行CSI驱动压力测试HTAP读写冲突OLAP查询导致OLTP事务超时用TPCH+Sysbench混合负载测试冷热数据分层失效压缩表查询耗时激增10倍监控INNODB_C…

Spring WebFlux和Spring MVC的对比

原文网址&#xff1a;Spring WebFlux和Spring MVC的对比-CSDN博客 简介 本文介绍Spring WebFlux和Spring MVC的区别。 Webflux&#xff1a;是异步非阻塞的&#xff08;IO多路复用&#xff09;&#xff0c;基于Netty。适合网络转发类的应用&#xff0c;比如&#xff1a;网关。…

解析401 Token过期自动刷新机制:Kotlin全栈实现指南

在现代Web应用中&#xff0c;Token过期导致的401错误是影响用户体验的关键问题。本文将手把手实现一套完整的Token自动刷新机制&#xff0c;覆盖从原理到实战的全过程。 一、为什么需要Token自动刷新&#xff1f; 当用户使用应用时&#xff0c;会遇到两种典型场景&#xff1a;…

《解构线性数据结构的核心骨架:从存储模型到操作范式的深度解析》

线性数据结构概述 线性数据结构是数据元素按线性顺序排列的集合,每个元素有唯一的前驱和后继(除首尾元素)。常见类型包括数组、队列、链表和栈,每种结构在存储和操作上具有独特特性。 线性表:顾名思义,线性表就是数据排成像一条线的结构。每个线性表上的数据最多只有前和后…

HW蓝队工作流程

HW蓝队工作流程 由多领域安全专家组成攻击队&#xff0c;在保障业务系统安全的前提下&#xff0c;直接在真实网络环境开展对抗&#xff0c;对参演单位目标系进行可控、可审计的网络安全实战攻击&#xff0c;通过攻防演习检验参演单位的安全防护和应急处置能力&#xff0c;提高…

语音相关-浏览器的自动播放策略研究和websocket研究

策略详情 媒体参与度 AudioContext音频API的实现 new Audio音频API的实现 相关实践 网页端 使用new Audio创建的音频对象进行音频播放的时候&#xff0c;如果用户没有与页面进行交互&#xff0c;那么会报错如下&#xff1a; 使用AudioContext创建的对象播放音频&#xff0c;…

Linux操作系统网络服务模块一DHCP服务概述

前言&#xff1a; 在Linux网络服务体系架构中&#xff0c;​DHCP&#xff08;Dynamic Host Configuration Protocol&#xff09;​​ 作为核心服务之一&#xff0c;承担着局域网内主机网络参数动态分配的关键任务。其设计初衷是解决传统手动配置IP地址的效率瓶颈与错误风…

FPGA基础 -- Verilog语言要素之变量类型

Verilog 变量类型&#xff08;Variable Types&#xff09; 一、什么是变量类型&#xff1f; 在 Verilog 中&#xff0c;变量类型用于保存过程赋值结果&#xff08;由 always 或 initial 块赋值&#xff09;&#xff0c;通常用于建模寄存器、状态、计数器等“带记忆”的硬件行为…

使用Haporxy搭建Web群集

目录 一、案例分析 1.案例概述 2.案例前置知识点 2.1 HTTP请求 2.2 负载均衡常用调度算法 2.3常见的Web群集调度器 3.案例环境 3.1本案例环境 二、案例实施 1.搭建两台web服务器 2.安装Haproxy 3.haproxy服务器配置 修改haproxy的配置文件 4.测试web群集 5.haproxy的日…

pikachu靶场通关笔记38 目录遍历(路径遍历)

目录 一、目录遍历 二、源码分析 三、目录遍历与文件包含 四、实战渗透 1、进入靶场 2、目录遍历 &#xff08;1&#xff09;访问ace.min.css &#xff08;2&#xff09;访问fileinclude.php 本系列为《pikachu靶场通关笔记》渗透实战&#xff0c;本文通过对目录遍历源…

现代C++:std::string全方位碾压C字符串

在 C 中引入的 std::string 是对 C 语言中 char* 和 const char* 的一种现代化封装和增强。它不仅解决了 C 字符串的许多缺陷&#xff08;如安全性、内存管理、易用性等&#xff09;&#xff0c;还提供了丰富的 API 来简化字符串操作。本文将从多个维度详细对比 std::string 与…

20250619周四:Atlassian

今天主要把conference上的A xxx的所有资料大体看了一遍&#xff0c;花了两个多小时。 公司的这个conference系统&#xff0c;共实就是一个大型的可多人在线编辑的文件系统。差不多所有的资料都共享在上面。这对于多人参与的项目管理&#xff0c;还是相当方便的。 Atlassian最特…