【vue、UI】使用 Vue2 和 Element UI 封装 CSV 文件上传组件,实现csv回显
文章目录
- 前言
- 组件功能概述
- 实现效果
- 组件模板结构
- 组件的核心逻辑
- 1.数据属性定义
- 2.方法拆解
- 3.CSV 文件解析方法
- 4. 错误处理方法
- 组件样式
- 完整组件代码
- 总结
- 待优化的地方
前言
在 Vue2 项目中,我们经常需要封装一些可重用的组件来提升开发效率。本文将介绍如何使用 Vue2 和 Element UI 封装一个用于上传 CSV 文件并在对话框中回显其内容的公共组件。此组件共涉及两个接口:一个用于校验 CSV 文件内容是否合规,另一个用于上传经过校验的 CSV 文件。
组件功能概述
该组件主要包括以下功能:
- 选择 CSV 文件并上传。
- 校验文件内容是否符合要求。
- 将文件内容以表格形式展示。
- 支持对不合规内容进行标记和提示。
- 用户可在确认内容无误后手动点击上传。
实现效果
组件模板结构
首先来看组件的模板部分。
<template><el-dialog:visible.sync="visible"title="上传 CSV 文件"width="50%":before-close="handleClose":close-on-click-modal="false"><el-upload:action="!validateStatus ? validate : action":before-upload="beforeUpload":show-file-list="false":headers="headers"ref="upload":on-success="handleAvatarSuccess":auto-upload="!validateStatus":file-list="fileList":on-error="errorFn"><el-button type="primary" @click="selectFile">选择 CSV 文件</el-button></el-upload><el-tablev-if="tableData.length > 0":data="tableData.slice(1)"style="width: 100%; margin-top: 20px"border><el-table-columnv-for="(header, index) in tableData[0]":key="'header-' + index":prop="'col-' + index":label="header"><template slot-scope="scope"><div :class="{ 'error-cell': scope.row[index].value.isError }">{{ scope.row[index].value.value }}<el-tooltipclass="item"effect="dark"placement="top"v-if="scope.row[index].value.isError"><template slot="content">{{ scope.row[index].value.errorMsg }}</template><i class="el-icon-question"></i></el-tooltip></div></template></el-table-column></el-table><span slot="footer" class="dialog-footer"><el-button @click="handleClose">关 闭</el-button><el-buttontype="primary"@click="handleConfirm":disabled="!validateStatus":title="!validateStatus ? '请上传文件并通过校验' : '点击上传文件'">确定上传</el-button></span></el-dialog>
</template>
在这个模板中,使用了 Element UI 的 el-dialog 作为弹出框,el-upload 作为上传组件,el-table 显示上传的 CSV 文件内容。组件的主要逻辑操作通过各种方法(methods)来实现。
组件的核心逻辑
1.数据属性定义
以下是组件的数据属性,用于存储上传文件的状态、表格数据和 HTTP 请求头信息
data() {return {tableData: [], // 存储解析后的表格数据headers: {Authorization: "Bearer " + getToken(),},validateStatus: false,fileList: [], // 存储上传的文件};
}
tableData
:存储解析后的 CSV 文件数据。headers
:HTTP 请求头,包含授权信息。validateStatus
:文件校验状态,决定文件是否可以被上传。fileList
:用于存储选择的 CSV 文件。
2.方法拆解
selectFile()
方法
selectFile() {this.validateStatus = false;
}
当用户点击“选择 CSV 文件”按钮时,重置 validateStatus
状态为 false
,确保在选择新文件时校验状态被重置
validateData(data)
方法
validateData(data) {const allData = Object.values(data).flat();// 检查是否所有的 isError 都为 falseconst allValid = allData.every((item) => !item.isError);if (allValid) {this.validateStatus = true;this.$message.success("校验通过,可以上传");} else {this.validateStatus = false;this.$message.error("请按要求重新修改上传内容");}
}
该方法用于校验上传的数据,如果数据无误,则设置 validateStatus
为 true
,并显示成功提示;否则显示错误提示。
handleAvatarSuccess(res, file)
方法
handleAvatarSuccess(res, file) {if (res.code === 500) {this.$message.error(res.msg);this.validateStatus = false;return;}if (res.data) {const csvHeaders = this.tableData[0];const fields = Object.keys(res.data);const dataLength = res.data[fields[0]].length;const result = Array.from({ length: dataLength }, (_, index) =>fields.map((field) => ({value: res.data[field][index],isError: false,errorMsg: "",})));this.tableData = [csvHeaders, ...result];this.validateData(res.data);}
}
该方法在文件上传成功后调用,处理上传成功后的逻辑,包括数据解析和更新表格数据。
handleClose()
方法
handleClose() {this.tableData = [];this.$emit("update:visible", false);
}
关闭对话框时,清空表格数据并触发 visible
属性更新事件,关闭对话框。
handleConfirm()
方法
handleConfirm() {const formData = new FormData();formData.append("file", this.fileList[0]);const config = {headers: this.headers,};axios.post(this.action, formData, config).then((response) => {if (response.data.code == 500 && response.data.msg == null) {this.$message.error("文件提交失败,未知原因");} else if (response.data.code == 200) {this.$message({dangerouslyUseHTMLString: true,message: response.data.msg,type: "success",duration: 5000,});this.$emit("upload-success");}}).catch((error) => {this.$message.error("文件提交失败");});
}
该方法在用户确认上传时调用,通过 Axios 发起 POST 请求将文件上传至后端接口。
3.CSV 文件解析方法
beforeUpload(file)
方法
beforeUpload(file) {if (!this.validateStatus) {const reader = new FileReader();reader.onload = (e) => {const decoder = new TextDecoder("gbk");const csvText = decoder.decode(e.target.result);this.tableData = this.parseCSV(csvText);if (this.tableData.length > 0) {return true;} else {return false;}};reader.readAsArrayBuffer(file);this.fileList = [file];} else {return true;}
}
该方法在文件上传之前执行,使用 FileReader 对象解析 CSV 文件内容,并将其转换为表格数据。
parseCSV(text)
方法
parseCSV(text) {const lines = text.split("\n").map((line) => line.trim());if (lines.length === 0) return [];const headers = this.parseLine(lines[0]);const data = lines.slice(1).map((line) => {const cells = this.parseLine(line);const row = headers.map((header, index) => ({value: cells[index] || "",isError: false,errorMsg: "",}));return row;});return [headers, ...data];
}
该方法用于解析 CSV 文件内容,按行拆分并解析每一行内容为表格所需的格式。
4. 错误处理方法
errorFn(err, file, fileList)
方法
errorFn(err, file, fileList) {console.log("🚀 ~ errorFn ~ err:", err);
}
该方法处理文件上传过程中的错误,当前仅简单地打印错误信息。
组件样式
<style scoped>
.el-table th,
.el-table td {text-align: center;padding: 10px;
}.error-cell {background-color: #ffb1b1;color: black;padding: 5px;
}
</style>
此部分为组件的样式定义,确保表格居中对齐,并为有错误的单元格添加红色背景。
完整组件代码
完整的组件代码如下所示。这段代码结合了 Vue2 和 Element UI,封装了一个 CSV 文件上传与显示的功能组件。
<template><el-dialog:visible.sync="visible"title="上传 CSV 文件"width="50%":before-close="handleClose":close-on-click-modal="false"><el-upload:action="!validateStatus ? validate : action":before-upload="beforeUpload":show-file-list="false":headers="headers"ref="upload":on-success="handleAvatarSuccess":auto-upload="!validateStatus":file-list="fileList":on-error="errorFn"><el-button type="primary" @click="selectFile">选择 CSV 文件</el-button></el-upload><el-tablev-if="tableData.length > 0":data="tableData.slice(1)"style="width: 100%; margin-top: 20px"border><el-table-columnv-for="(header, index) in tableData[0]":key="'header-' + index":prop="'col-' + index":label="header"><template slot-scope="scope"><div :class="{ 'error-cell': scope.row[index].value.isError }">{{ scope.row[index].value.value }}<el-tooltipclass="item"effect="dark"placement="top"v-if="scope.row[index].value.isError"><template slot="content">{{ scope.row[index].value.errorMsg }}</template><i class="el-icon-question"></i></el-tooltip></div></template></el-table-column></el-table><span slot="footer" class="dialog-footer"><el-button @click="handleClose">关 闭</el-button><el-buttontype="primary"@click="handleConfirm":disabled="!validateStatus":title="!validateStatus ? '请上传文件并通过校验' : '点击上传文件'">确定上传</el-button></span></el-dialog>
</template><script>
import { getToken } from "@/utils/auth";
import axios from "axios";
export default {props: {visible: {type: Boolean,required: true,},action: {type: String,required: true,},validate: {type: String,required: true,},},data() {return {tableData: [], // 存储解析后的表格数据headers: {Authorization: "Bearer " + getToken(),},validateStatus: false,fileList: [], // 存储上传的文件};},methods: {selectFile() {this.validateStatus = false;},validateData(data) {const allData = Object.values(data).flat();const allValid = allData.every((item) => !item.isError);if (allValid) {this.validateStatus = true;this.$message.success("校验通过,可以上传");} else {this.validateStatus = false;this.$message.error("请按要求重新修改上传内容");}},handleAvatarSuccess(res, file) {if (res.code === 500) {this.$message.error(res.msg);this.validateStatus = false;return;}if (res.data) {const csvHeaders = this.tableData[0];const fields = Object.keys(res.data);const dataLength = res.data[fields[0]].length;const result = Array.from({ length: dataLength }, (_, index) =>fields.map((field) => ({value: res.data[field][index],isError: false,errorMsg: "",})));this.tableData = [csvHeaders, ...result];this.validateData(res.data);}},handleClose() {this.tableData = [];this.$emit("update:visible", false);},handleConfirm() {const formData = new FormData();formData.append("file", this.fileList[0]);const config = {headers: this.headers,};axios.post(this.action, formData, config).then((response) => {if (response.data.code == 500 && response.data.msg == null) {this.$message.error("文件提交失败,未知原因");} else if (response.data.code == 200) {this.$message({dangerouslyUseHTMLString: true,message: response.data.msg,type: "success",duration: 5000,});this.$emit("upload-success");}}).catch((error) => {this.$message.error("文件提交失败");});},beforeUpload(file) {if (!this.validateStatus) {const reader = new FileReader();reader.onload = (e) => {const decoder = new TextDecoder("gbk");const csvText = decoder.decode(e.target.result);this.tableData = this.parseCSV(csvText);if (this.tableData.length > 0) {return true;} else {return false;}};reader.readAsArrayBuffer(file);this.fileList = [file];} else {return true;}},errorFn(err, file, fileList) {console.log("🚀 ~ errorFn ~ err:", err);},parseCSV(text) {const lines = text.split("\n").map((line) => line.trim());if (lines.length === 0) return [];const headers = this.parseLine(lines[0]);const data = lines.slice(1).map((line) => {const cells = this.parseLine(line);const row = headers.map((header, index) => ({value: cells[index] || "",isError: false,errorMsg: "",}));return row;});return [headers, ...data];},parseLine(line) {const result = [];let current = "";let inQuotes = false;for (let i = 0; i < line.length; i++) {const char = line[i];if (char === '"') {inQuotes = !inQuotes;} else if (char === "," && !inQuotes) {result.push(current.trim());current = "";} else {current += char;}}result.push(current.trim());return result;},},
};
</script><style scoped>
.el-table th,
.el-table td {text-align: center;padding: 10px;
}.error-cell {background-color: #ffb1b1;color: black;padding: 5px;
}
</style>
总结
通过本文的介绍,了解了如何使用 Vue2 和 Element UI 封装一个 CSV 文件上传和回显的组件。该组件的设计充分考虑了数据校验和用户体验,使得上传和展示过程更加直观和友好。在实际项目中,可以根据业务需求对该组件进行扩展和定制。希望这篇文章对您有所帮助!
待优化的地方
文件handleAvatarSuccess可以优化,生成的数据有点冗余,过于嵌套了。
相关文章:

【vue、UI】使用 Vue2 和 Element UI 封装 CSV 文件上传组件,实现csv回显
文章目录 前言组件功能概述实现效果组件模板结构组件的核心逻辑1.数据属性定义2.方法拆解3.CSV 文件解析方法4. 错误处理方法 组件样式完整组件代码总结待优化的地方 前言 在 Vue2 项目中,我们经常需要封装一些可重用的组件来提升开发效率。本文将介绍如何使用 Vue…...

erlang学习: Mnesia Erlang数据库2
Mnesia数据库增加与查询学习 -module(test_mnesia).-record(shop, {item, quantity, cost}). -record(cost, {name, price}). -record(design, {info, plan}). %% API -export([insert/3,select/1,start/0]). start() ->mnesia:start().insert(Name, Quantity, Cost) ->…...

电脑文件怎么备份?推荐6个高效便捷的文件备份的方法
在日常使用电脑的过程中,数据备份是一项至关重要的任务。无论是个人用户还是企业用户,都需要确保重要文件的安全性和可恢复性。 以下是推荐的六个高效便捷的文件备份方法,帮助你轻松守住你的文档安全。 1. 使用USB存储设备 USB存储设备如U盘…...

Procdump抓ToDesk密码
目录 前言 1.工具教程 2.转储数据 3.密码获取 4.总结 前言 本文是因为在公众号上看到一篇文章随想着实战中利用ToDesk秀操作失败后,实验环境成功复现后写下。ProcDump[1] 是一个命令行实用工具,其主要用途是监视应用程序的 CPU 峰值,并在…...

ESP8266下载固件→连接阿里云
一、工具准备 1、ESP8266Wifi模块 2、ESP8266下载器 ESP8266-01模块 二、固件配置 CH340串口工具-烧录ESP8266-01固件_esp8266 ch340烧录-CSDN博客文章浏览阅读444次,点赞6次,收藏3次。CH340会有供电不足的问题,因此需要外部供电_esp…...

20240911软考架构-------软考156-160答案解析
每日打卡题156-160答案 156、NoSQL整体框架分为4层,由下至上分为数据持久层、数据分布层、数据逻辑模型层和(1)。(2)定义了数据的存储形式。(3)定义了数据是如何分布的。(4…...

工厂模式与策略模式(golang示例)
一、工厂模式简介 工厂模式是一种创建型设计模式,主要用于封装对象的创建过程。通过使用工厂模式,客户端代码无需直接实例化对象,而是通过工厂类来创建对象。这样可以将对象的创建与使用分离,从而提高代码的灵活性。 1.1 工厂模…...

批量视频如何做成一个二维码(分步骤教程)
原创教程,阿酷TONY,2024.9.11,湖南长沙 批量视频如何做成一个二维码(分步骤教程),场景应用: 1. 一批视频需要按组分类,生成一个二维码,实现扫一个二维码,观看…...

OpengGL教程(三)---使用VAO和VBO方式绘制三角形
本章参考官方教程:learnopengl-cn VertexShader.glsl #version 330 core layout(location 0) in vec3 position; layout(location 1) in vec3 color; uniform mat4 projection; // 投影矩阵 out vec4 ourColor; void main() {gl_Position projection * vec4(p…...

【单片机开发】单片机常用开发工具
【前言】 在嵌入式系统领域,单片机(Microcontroller, MCU)作为核心组件,广泛应用于智能家居、工业控制、汽车电子等众多领域。而单片机开发工具,则是开发者们实现创意、解决问题的重要助手。本文主要讲述目前主流的单…...

一、计算机网络的体系结构
1.1 计算机网络的组成 1)从组成部分上分为:硬件、软件、协议。硬件是指主机、通信链路、交换设备和通信处理机组曾。软件包括各种实现资源共享的软件以及各种软件工具(如网络操作系统、邮件收发程序、FTP程序、聊天软件)。 2&…...

C语言补习课——文件篇
来源:黑马程序员 第157讲 C语言操作文件概述 读取文件:输入流 写文件:输出流 读写的方向判断取决与参照,一般我们站在程序的角度判断读写方向。 第158讲 路径 基本概念 路径就是指文件在电脑中的位置,eg…...

【可测试性实践】C++ 单元测试代码覆盖率统计入门
引言 最近在调研C工程怎么做单元测试和代码覆盖率统计,由于我们工程有使用Boost库,尝试使用Boost.Test来实现单元测试并通过Gcov和Lcov来生成代码覆盖率报告。本文记录完整的搭建测试Demo,希望能带来一定参考。 常用C单测框架对比 特性Goo…...

C++笔记---list
1. list的介绍 list其实就是就是我们所熟知的链表(双向循环带头结点),但其是作为STL中的一个类模板而存在。 也就是说,list是可以用来存储任意类型数据的顺序表,既可以是内置类型,也可以是自定义类型&…...

JavaWeb开发中为什么Controller里面的方法是@RequestMapping?
在Java Web开发中,尤其是在使用Spring MVC框架时,RequestMapping注解被广泛应用于Controller层的方法上,这是因为RequestMapping是Spring MVC提供的一个核心注解,用于将HTTP请求映射到相应的处理器类或处理器方法上。通过这种方式…...

若依移动版使用微信小程序打开失败
现象 解决办法:删掉自带的appid...

精准控图工具 Concept Sliders:超好用的 控制 Lora 适配器
Concept Sliders 你有没有遇到这样的情况?你花费大量时间制作提示和寻找种子,以使用文本到图像模型生成所需的图像。但是,你还需要对生成图像中的属性强度(如眼睛大小或照明)进行更细致、更精细的控制。修改提示会破坏…...

【EI会议征稿通知】第四届材料工程与应用力学国际学术会议(ICMEAAE 2025)
第四届材料工程与应用力学国际学术会议(ICMEAAE 2025) 2025 4th International Conference on Materials Engineering and Applied Mechanics 本次会议将重点讨论材料科学、应用力学等领域的最新研究进展与发展趋势。会议旨在为国内外从事这些领域研究…...

Hadoop安全之Knox
Apache Knox 是一个 REST API 网关,为 Hadoop 集群提供安全的访问方式。Knox 提供了一层保护,简化了对 Hadoop 生态系统(如 HDFS、YARN、Hive、HBase 等)中各个组件的访问,并通过单点登录 (SSO)、认证、授权和审计功能…...

SprinBoot+Vue应急信息管理系统的设计与实现
目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍:CSDN认证博客专家,CSDN平台Java领域优质…...

索尼研究的AI部门将与AI新加坡合作开发大型语言模型
索尼研究公司签署了一项合作协议,以帮助测试和优化东南亚语言一网通(SEA-LION)人工智能(AI)模型,重点关注印度语言。 索尼研究公司的AI部门将与负责开发AI新加坡(AISG)的公司合作&a…...

【OJ刷题】双指针问题
这里是阿川的博客,祝您变得更强 ✨ 个人主页:在线OJ的阿川 💖文章专栏:OJ刷题入门到进阶 🌏代码仓库: 写在开头 现在您看到的是我的结论或想法,但在这背后凝结了大量的思考、经验和讨论 目录 1…...

基于SpringBoot+Vue+MySQL的校园食堂订餐
系统展示 用户前台界面 管理员后台界面 系统背景 随着信息技术的飞速发展和互联网的普及,传统校园食堂的运作模式已难以满足现代学生日益增长的便捷性、个性化需求。学生们希望能够在忙碌的学习生活中,通过更加高效、便捷的方式完成就餐选择,…...

uniapp业务实现
uni.requset添加异常判断提示,以及加载动画 /*** 该函数用于发送网络请求获取数据* 请求失败时会弹出相应的错误提示* 请求成功时会检查返回的数据是否存在错误,并根据错误代码做出相应处理* 如果数据请求成功且无错误,则将返回的数据赋值给pets变量*/fu…...

Windows和Mac命令窗快速打开文件夹
Windows explorer . 和 macOS open . 命令详解 1. Windows explorer . explorer 是 Windows 上的文件资源管理器,用于通过命令行打开文件夹或文件。 常用命令格式: explorer [选项] [目标路径]. 表示当前目录,explorer . 打开当前工作目录…...

智能制造云平台---附源码79117
目 录 摘要 1 绪论 1.1 研究背景和意义 1.2开发技术 1.2.1 Flask框架 1.2.2 Python简介 1.2.3 MySQL数据库 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2总体设计原则 2.3 系统流程分析 2.3.1 用户登录流程 2.3.2 删除信息流程 2.4 系统角色分析 2.5 系…...

降本、创新、合作,谁才是连接器行业破除内卷的关键词?
如果用一个字来评价2024年的汽车行业,那就是「卷」。 ▲中国汽车保有量不断提升 图/Pixabay 长安汽车董事长朱华荣说:“汽车行业的卷,让中国品牌达到了新高度。” 吉利董事长李书福说:“中国汽车工业内卷程度全球第一,…...

可能一拆为二,英特尔为何走到今天这一步?
【科技明说 | 科技热点关注】 近来看到外媒消息说,英特尔迫于经营压力,也不得不铤而走险,欲将英特尔一分为二,即芯片制造与芯片设计分离开,互相剥离,独立发展。 于是乎,英特尔将分拆…...

了解Redis集群概念,集群如何选举主节点
请给胡广一个免费的三连吗?感谢! 1. Redis集群 1.1 集群概念 Redis主从架构和Redis集群架构是两种不同的概念,大家刚接触Redis时经常弄混淆。胡广给大家贴下Redis官网对两者的解释。 (1)Redis主从架构 Redis主从实…...

Ozon跨境商家提升销量的关键:测评补单策略与必备条件
Ozon,自1998年创立以来,已稳居俄罗斯多品类电商领域的领导地位,不仅是俄罗斯最为人所熟知的电商品牌,更是该国电商行业的先驱之一。那么,对于希望在Ozon平台上实现销售爆单的跨境卖家而言,他们需要满足哪些…...