el-form与el-upload结合上传带附件的表单数据(前端篇)

1.写在之前

本文前端采用Vue + element-plus技术栈,前端项目参考yudao-ui-admin-vue3项目与
Geeker-Admin项目。

这篇文章是el-form与el-upload结合上传带附件的表单数据(后端篇)-CSDN博客姐妹篇,后端篇文章主要讲的是后端的实现逻辑,前端篇稍微简单一些,其实最主要的就是封装el-upload组件,供具体的表单组件调用。

2.封装el-upload组件

废话不多说,直接上代码。

<template><div class="upload-file"><el-upload:multiple="props.limit > 1"name="file"v-model:file-list="_fileList":show-file-list="true":auto-upload="autoUpload":action="updateUrl":headers="uploadHeaders":limit="props.limit":drag="drag":before-upload="beforeUpload":on-exceed="handleExceed":on-success="handleFileSuccess":on-error="excelUploadError":on-remove="handleRemove":on-preview="handlePreview":accept="fileType.join(',')":data="{ bucket: props.bucket }":disabled="disabled"class="upload-file-uploader"><el-button v-if="!disabled" type="primary"><Icon icon="ep:upload-filled" />选取文件</el-button></el-upload></div>
</template>
<script lang="ts" setup>
import { propTypes } from '@/utils/propTypes'
import { getAccessToken } from '@/utils/auth'
import type { UploadUserFile, UploadProps, UploadRawFile, UploadFile } from 'element-plus'
import { downloadFile } from '@/api/infra/file'
import download from '@/utils/download'defineOptions({ name: 'UploadFile' })const message = useMessage() // 消息弹窗
const emit = defineEmits(['update:fileList'])const props = defineProps({fileList: propTypes.array.def([]),title: propTypes.string.def('文件上传'),updateUrl: propTypes.string.def(import.meta.env.VITE_UPLOAD_URL),fileType: propTypes.array.def([]), // 文件类型, 例如['png', 'jpg', 'jpeg']fileSize: propTypes.number.def(500), // 大小限制(MB)limit: propTypes.number.def(5), // 数量限制autoUpload: propTypes.bool.def(true), // 自动上传drag: propTypes.bool.def(false), // 拖拽上传isShowTip: propTypes.bool.def(true), // 是否显示提示bucket: propTypes.string.def('operation'), //默认存储到operation bucket中disabled: propTypes.bool.def(false)
})
// ========== 上传相关 ==========
const uploadList = ref<UploadUserFile[]>([])
const _fileList = ref<UploadUserFile[]>(props.fileList)
const uploadHeaders = ref({Authorization: 'Bearer ' + getAccessToken()
})// 监听 props.fileList 列表默认值改变
watch(() => props.fileList,(n: UploadUserFile[]) => {_fileList.value = n}
)// 文件上传之前判断
const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {if (_fileList.value.length >= props.limit) {message.error(`上传文件数量不能超过${props.limit}个!`)return false}let fileExtension = ''if (file.name.lastIndexOf('.') > -1) {fileExtension = file.name.slice(file.name.lastIndexOf('.'))}const isImg = props.fileType.some((type: string) => {if (file.type.indexOf(type) > -1) return truereturn !!(fileExtension && fileExtension.indexOf(type) > -1)})const isLimit = file.size < props.fileSize * 1024 * 1024if (!isImg) {message.error(`文件格式不正确, 请上传${props.fileType.join('/')}格式!`)return false}if (!isLimit) {message.error(`上传文件大小不能超过${props.fileSize}MB!`)return false}
}
const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => {// 因为后端返回的res经过统一处理拦截 此处需要根据res返回的code判断是否真的上传成功let code: number = res.codelet data = res.dataif (code == 200) {let name = data.name //后端生成的文件唯一标识//前端存储中 如果有response相同的文件,说明有相同内容的文件 需要清楚所有 只保存一个 即本次上传的文件_fileList.value = _fileList.value.filter((item) => item.name !== name)uploadList.value.push({ name: data.name, url: data.url, response: data.response })_fileList.value = _fileList.value.concat(uploadList.value)_fileList.value.sort(sortFileList('name'))uploadList.value = []message.success('文件上传成功')emitUpdateModelValue()} else {message.error('文件上传失败')}
}
// 文件数超出提示
const handleExceed: UploadProps['onExceed'] = (): void => {message.error(`上传文件数量不能超过${props.limit}个!`)
}
// 上传错误提示
const excelUploadError: UploadProps['onError'] = (): void => {message.error('导入数据失败,请您重新上传!')
}
// 删除上传文件
const handleRemove = (file) => {const findex = _fileList.value.map((f) => f.name).indexOf(file.name)if (findex > -1) {_fileList.value.splice(findex, 1)emitUpdateModelValue()}
}
const handlePreview: UploadProps['onPreview'] = async (uploadFile: UploadFile) => {console.log(123)const res = await downloadFile(uploadFile.response)console.log(res)download.commonFile(res, uploadFile.name)
}const emitUpdateModelValue = () => {emit('update:fileList', _fileList.value)
}//数组排序 按照名字升序
const sortFileList = (name) => {const rev = 1return function (a, b) {a = a[name]b = b[name]if (a < b) {return rev * -1}if (a > b) {return rev}return 0}
}
</script>
<style scoped lang="scss">
.upload-file-uploader {margin-bottom: 5px;
}:deep(.el-upload-list) {width: 400px;
}:deep(.upload-file-list .el-upload-list__item) {position: relative;margin-bottom: 10px;line-height: 2;border: 1px solid #e4e7ed;
}:deep(.el-upload-list__item-file-name) {max-width: 500px;
}:deep(.upload-file-list .ele-upload-list__item-content) {display: flex;justify-content: space-between;align-items: center;color: inherit;
}:deep(.ele-upload-list__item-content-action .el-link) {margin-right: 10px;
}
</style>

代码组件个人理解没有什么好讲的。开启自动上传,上传成功拿到后端的数据返回,构造数据,如果有名称相同的文件,全部删除,只使用最新的上传文件数据。上传成功后,更新表单绑定的文件数据。

3.关于文件的下载

这里想要说一下文件的下载,前期看了很多实现,有使用a标签用文件的URL实现下载的,有直接使用window.open(URL)实现的,我在实际下载中,遇到两个问题,第一是遇到浏览器能处理的文件,例如MP4的视频文件,pdf的文本文件,会直接打开,不会下载,第二个问题是下载的名称不能自己指定,按照网上查找的方法指定也不起作用,最后我选择的第二节中代码方法,先获取文件内容,后下载文件。

commonFile: (data: Blob, fileName: string) => {download0(data, fileName, 'application/octet-stream')}const download0 = (data: Blob, fileName: string, mineType: string) => {// 创建 blobconst blob = new Blob([data], { type: mineType })// 创建 href 超链接,点击进行下载window.URL = window.URL || window.webkitURLconst href = URL.createObjectURL(blob)const downA = document.createElement('a')downA.href = hrefdownA.download = fileNamedownA.click()// 销毁超连接window.URL.revokeObjectURL(href)
}

4.表单使用

先上一段代码,上传的文件类型为file-type规定的文件类型。

<tr><td><span :class="{ required: required }">附件</span></td><td colspan="3"><el-form-item prop="files"><UploadFilev-model:file-list="form.files":file-type="['.jpg', '.png', '.docx', '.pdf', '.mp4']":disabled="!required"/></el-form-item><el-form-item><span style="color: red; font-size: 12px">这是一些注意消息,比如上传的文件个数,上传的文件类型,上传的文件大小,上传的文件注意事项</span></el-form-item></td></tr>

5.实际效果展示

6.写在最后

其实感觉前端只要有第二节封装组件的代码,代码一看就一目了然,就是在文件下载时候,多花了一点小心思。本篇文章只是简单笼统的介绍了一下前端实现传文件,具体的表单设计其实有很多立方需要讲,后期的话,如果有时间,会录一个实际效果展示视频,敬请期待。如果有不对的地方,还请看到本篇文章的您不吝赐教。

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

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

相关文章

JavaEE:线程池精讲

目录 一.什么是线程池 二.线程池的实现原理 &#x1f388;为什么要有工厂模式&#xff1f; 三.线程池的构造方法解读 &#x1f388;线程池的拒绝策略 四.自己实现一个线程池 一.什么是线程池 简单来说&#xff0c;线程池就好比一块鱼塘&#xff0c;鱼塘中的每条鱼就是一个线程…

SQL注入绕过正则及无列名注入

渗透测试 一、select\b[\s\S]*\bfrom正则二、科学计数法绕过三、过滤information四、无列名注入1、利用 join-using 注列名。2、无列名查询 五、报错注入7大常用函数1.ST_LatFromGeoHash()&#xff08;mysql>5.7.x&#xff09;payload 2.ST_LongFromGeoHash&#xff08;mysq…

针对这两个趋势,3.0全新新零售商业模式可以采取以下策略:

国内市场确实存在“消费升级”和“消费降级”两个趋势&#xff0c;这是由于不同消费者群体的需求和购买力存在差异。消费升级主要发生在高端市场&#xff0c;消费者愿意为高品质、高价值、高价格的商品和服务付出更多。而消费降级则主要发生在中低端市场&#xff0c;消费者更加…

学习k8s

学习k8s 我为什么要用k8s 和其他部署方式的区别是什么? 传统部署方式 java --> package --> 放到服务器上 --> Tomcat 如果是同时进行写操作,会存在并发问题. 用户 --网络带宽–> 服务器 -->服务 同一个服务器上,多个服务: 网络资源的占用 内存的占用 cpu的占…

《点云处理》 点云去噪

前言 通常从传感器&#xff08;3D相机、雷达&#xff09;中获取到的点云存在噪点&#xff08;杂点、离群点、孤岛点等各种叫法&#xff09;。噪点产生的原因有不同&#xff0c;可能是扫描到了不想要扫描的物体&#xff0c;可能是待测工件表面反光形成的&#xff0c;也可能是相…

c# OpenCV 基本绘画(直线、椭圆、矩形、圆、多边形、文本)(四)

我们将在这里演示如何使用几何形状和文本注释图像。 Cv2.Line() 绘制直线 Cv2.Ellipse() 绘制椭圆Cv2.Rectangle() 绘制矩形Cv2.Circle() 绘制圆Cv2.FillPoly() 绘制多边形Cv2.PutText() 绘制文本 一、绘制直线 Cv2.Line(image, start_point, end_point, color, thickness) …

IntelliJ IDE 插件开发 | (三)消息通知与事件监听

系列文章 IntelliJ IDE 插件开发 |&#xff08;一&#xff09;快速入门IntelliJ IDE 插件开发 |&#xff08;二&#xff09;UI 界面与数据持久化IntelliJ IDE 插件开发 |&#xff08;三&#xff09;消息通知与事件监听 前言 在前两篇文章中讲解了关于插件开发的基础知识&…

C语言:前缀和

【模板】前缀和_牛客题霸_牛客网 (nowcoder.com) 前缀和&#xff1a;将数组小于该前缀的数加起来的新数组。 例&#xff1a; 该数组为1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5。前缀和为1&#xff0c;3&#xff0c;6&#xff0c;10&#xff0c;15 。 如果要求…

1-完全理解以太坊智能合约

了解区块链 区块链技术的核心概念是分布式账本&#xff0c;它是许多参与者共享的特定类型的数据库。 这个特殊的数据库只是一个交易列表&#xff0c;记录着网络中发生的每笔交易。每个人都可以拥有自己的交易列表备份&#xff0c;再加上强有力的货币激励措施消除各方之间信任…

比 style gan 更好的 style gan2

上一篇博客介绍了style gan 原理&#xff0c;但是 style gan 的结果会有水珠伪影&#xff0c;作者实验后发现是 Adain 导致的&#xff0c;AdaIN对每一个feature map的通道进行归一化&#xff0c;这样可能破坏掉feature之间的信息。当然实验证明发现&#xff0c;去除AdaIN的归一…

广州华锐互动VRAR:利用VR开展新能源汽车触电安全演练,降低培训成本和风险

随着新能源汽车行业的快速发展&#xff0c;相关的安全培训也变得越来越重要。其中&#xff0c;触电急救培训对于保障驾驶员和乘客的安全具有重要意义。传统培训方式存在一些不足&#xff0c;而利用VR技术进行培训则具有很多优势。 利用VR技术开展新能源汽车触电安全演练可以在模…

2023 英特尔On技术创新大会直播 | 窥探未来科技的边界

2023 英特尔On技术创新大会直播 | 窥探未来科技的边界 写在最前面观后感其他有趣的专题课程 写在最前面 嘿&#xff0c;你是不是对科技和创新充满好奇&#xff1f;2023 英特尔 On 技术创新大会线上活动邀请你一起探索最前沿的科技世界&#xff01; 这不仅是一场普通的聚会&…

AWS Linux安装桌面并远程访问

文章目录 小结问题及解决参考 小结 在AWS Linux安装了桌面并进行远程访问。 问题及解决 需要使用过程桌面访问AWS Linux&#xff0c;这里在AWS服务器安装并使用Amazon Linux 2 MATE desktop。 检查OS版本&#xff1a; [ec2-userip-10-0-3-241 ~]$ grep PRETTY_NAME /etc/o…

WordCloud—— 词云

【说明】文章内容来自《机器学习入门——基于sklearn》&#xff0c;用于学习记录。若有争议联系删除。 wordcloud 是python的第三方库&#xff0c;称为词云&#xff0c;也成文字云&#xff0c;可以根据文本中的词频以直观和艺术化的形式展示文本中词语的重要性。 依赖于pillow …

Windows本地搭建开源企业管理套件Odoo并实现公网访问

文章目录 前言1. 下载安装Odoo&#xff1a;2. 实现公网访问Odoo本地系统&#xff1a;3. 固定域名访问Odoo本地系统 前言 Odoo是全球流行的开源企业管理套件&#xff0c;是一个一站式全功能ERP及电商平台。 开源性质&#xff1a;Odoo是一个开源的ERP软件&#xff0c;这意味着企…

零刻EQ12 N100 2.5G双网口 All In One新手教程

零刻EQ12 N100 2.5G双网口 All In One新手教程 前言1.硬件配置2.准备工作2.1. ESXI8.0U2镜像2.2. Rufus磁盘工具下载2.3. ikuai镜像下载2.4. StarWindConverter虚拟磁盘格式转换工具下载2.5. OpenWrt镜像下载2.6. 黑群晖RR引导镜像下载(DSM7.2)2.7. 需要准备的硬件2.8. 格式化需…

《每天一分钟学习C语言·二》

1、当使用const关键字变量就无法修改可当常量来用。常量指针不能通过指针来改变变量的值&#xff0c;但可以通过其他引用来改变变量的值常量指针也可以指向其他变量地址&#xff0c;如 int a5,b6; const int *pt &a; *pt6; //错误 a6; //正确 pt&b; //正确指针常量指…

Databend 开源周报第 124 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 新增对 Delta 和…

使用下载代替物理串口输出-STM32 Debug (printf) Viewer

使用下载代替物理串口输出-STM32 Debug 硬件要求配置方法代码要求打印输出结果 硬件要求 STM32的PB9、PB10引脚的串口1通常用作其他功能使用后&#xff0c;无法通过printf()函数打印输出想要调试输出查看变量或调试信息。现已使用另外一种方法实现printf()函数打印输出。 ST…

R语言采集获取58商铺出租转让信息

前两篇文章给我一个朋友分析出店铺商品以及地址房源信息&#xff0c;后来去看了下店铺房租有点贵&#xff0c;还是毛坯房&#xff0c;要自己装修&#xff0c;本着节约成本的原则。熬了个通宵&#xff0c;给他采集了一些转租商铺数据&#xff0c;因为数据比较多&#xff0c;过于…