当前位置: 首页 > news >正文

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.lryc.cn/news/572617.html

相关文章:

  • 拓客软件有哪些?
  • AI Agent开发与安全
  • 企业级文档搜索系统架构设计与实践指南
  • 巧用云平台API实现开源模型免费调用的实战教程
  • 数据库从零开始:MySQL 中的 DDL 库操作详解【Linux版】
  • 从生活场景学透 JavaScript 原型与原型链
  • 链接过程使用链接器将该目标文件与其他目标文件、库文件、启动文件等链接起来生成可执行文件。附加的目标文件包括静态连接库和动态连接库。其中的启动文件是什么意思?
  • 【内存】Linux 内核优化实战 - vm.max_map_count
  • Spring AOP @AfterReturning (返回通知)的使用场景
  • MySQL 分页查询列表;Explain ;深度分页 ;管理系统,筛选系统
  • AR 眼镜之-条形码识别-实现方案
  • 【AI时代速通QT】第二节:Qt SDK 的目录介绍和第一个Qt Creator项目
  • AI人工智能与LLM大语言模型有什么区别
  • Node.js 在前端开发中的作用与 npm 的核心理解
  • 1.22Node.js 中操作 Redis
  • Kafka线上集群部署方案:从环境选型到资源规划思考
  • 源易信息:领先GEO供应商的市场布局与服务优势
  • 【生活点滴】车辆过户、新车挂牌
  • 变幻莫测:CoreData 中 Transformable 类型面面俱到(五)
  • 学习华为 ensp 的学习心得体会
  • 百胜软件荣膺零售商业评论“《2024创新零售》优秀服务商TOP”奖项
  • 学习华为 ensp 的学习心得体会(适合新手)
  • Python 数据分析与可视化 Day 2 - 数据清洗基础
  • 如何轻松将照片从 iPhone 传输到 Android?
  • 从“数据困境”到“数据生态”:DaaS重塑三甲医院医疗数据治理
  • 【RTSP从零实践】2、使用RTP协议封装并传输H264
  • 基于Gold-YOLO的聚合-分发机制改进YOLOv8教程
  • 电影感户外柔和光线人像街拍摄影后期Lr调色教程,手机滤镜PS+Lightroom预设下载!
  • 【世纪龙科技】智能网联汽车装调仿真教学软件数智化赋能实训教学
  • 魅族“换血”出牌:手机基本盘站不稳,想靠AI和汽车“改命”