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.mzph.cn/news/233528.shtml

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

相关文章

ROS学习笔记(七)---参数服务器

ROS学习笔记文章目录 01. ROS学习笔记(一)—Linux安装VScode 02. ROS学习笔记(二)—使用 VScode 开发 ROS 的Python程序&#xff08;简例&#xff09; 03. ROS学习笔记(三)—好用的终端Terminator 04. ROS学习笔记(四)—使用 VScode 启动launch文件运行多个节点 05. ROS学习笔…

vscode颜色主题插件one dark Pro安装

1.点击扩展图标→搜索“one dark Pro”→第一个点击安装 2.安装成功后&#xff0c;不要忘了点击设置颜色主题 3.看下效果&#xff1a;

MySQL数据库:表的约束

目录 一. 空属性null 二. 默认值default 三. 列描述comment 四. 填充零zerofill 五. 主键primary key 六. 自增长auto_increment 七. 唯一键unique 八. 外键foreign key 一. 空属性null 对于表中的数据&#xff0c;如果在进行插入的时候不显示地给定值&#xff0c;那么…

vp与vs联合开发-通过FrameGrabber连接相机

添加控件 1.CogRecordDisplay 控件 用于显示图像 初始化相机对象方法 //启动窗体时 调用初始化相机方法 //封装相机关闭方法 //窗体关闭时 调用相机关闭方法 拍照 设置采图事件 // 保存图像 设置曝光按钮事件 1.可变参数

css实现0.5px宽度/高度显——属性: transform: scale

在大多数设备上&#xff0c;实际上无法直接使用 CSS 来精确地创建 0.5 像素的边框。因为大多数屏幕的最小渲染单位是一个物理像素&#xff0c;所以通常只能以整数像素单位渲染边框。但是&#xff0c;有一些技巧可以模拟出看起来像是 0.5 像素的边框。 这里介绍使用&#xff1a…

云原生系列2-GitLab和Jenkins

1、GitLab类似github&#xff0c;是个私有仓库 1、GitLab安装&#xff0c;至少8G内存4核cpu # 查找Gitlab镜像 docker search gitlab/gitlab-ce # gitlab镜像拉取 docker pull gitlab/gitlab-ce # 查看镜像 docker images # 本机先建3个目录&#xff0c;为了gitlab容器通过挂…

新增工具箱管理功能、重构网站证书管理功能,1Panel开源面板v1.9.0发布

2023年12月18日&#xff0c;现代化、开源的Linux服务器运维管理面板1Panel正式发布v1.9.0版本。 在这一版本中&#xff0c;1Panel引入了新的工具箱管理功能&#xff0c;包含Swap分区管理、Fail2Ban管理等功能。此外&#xff0c;1Panel针对网站证书管理功能进行了全面重构&…

【数据结构】迷宫问题报告+源码C/C++

之前帮别人写的一个报告&#xff0c;是关于栈的迷宫问题。内容不多&#xff0c;代码在最后。分享给大家&#xff0c;喜欢可以点赞关注。原创无偿分享&#xff0c;勿商用。 迷宫求解 设计目的 仅认识到栈是一种特殊的线性表是远远不够的&#xff0c;本次实习的目的在于使学生…

【Linux基础】3. 文件基本属性

文章目录 【 1. 文件的属主和属组 】【 2. 显示文件的类型、权限 】2.1 文件类型2.2 文件权限 【 3. 更改文件属性 】3.1 chgrp 更改文件属组3.2 chown 更改文件所有者3.3 更改文件权限3.3.1 数字法更改文件权限3.3.2 符号法更改文件权限 【 1. 文件的属主和属组 】 Linux 系统…

Vue的脚手架

脚手架配置 脚手架文档&#xff1a;Vue CLI npm config set registry https://registry.npm.taobao.org vue.config.js配置选项&#xff1a; 配置参考 | Vue CLI ref选项 ref和id类似&#xff0c;给标签打标识。 document.getElementById(btn); this.$ref.btn; 父子组…

Kafka 分级存储在腾讯云的实践与演进

导语 腾讯云消息队列 Kafka 内核负责人鲁仕林为大家带来了《Kafka 分级存储在腾讯云的实践与演进》的精彩分享&#xff0c;从 Kafka 架构遇到的问题与挑战、Kafka 弹性架构方案类比、Kafka 分级存储架构及原理以及腾讯云的落地与实践四个方面详细分享了 Kafka 分级存储在腾讯云…

基于JAVA的海南旅游景点推荐系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户端2.2 管理员端 三、系统展示四、核心代码4.1 随机景点推荐4.2 景点评价4.3 协同推荐算法4.4 网站登录4.5 查询景点美食 五、免责说明 一、摘要 1.1 项目介绍 基于VueSpringBootMySQL的海南旅游推荐系统&#xff…

STM32 RTC总结

RTC入侵检测Tamper RTC Tamper功能就是&#xff0c;MCU在Tamper管脚检测到一个指定边缘信号&#xff08;可配置&#xff09;时&#xff0c;就主动清除所有备份寄存器数据的功能。如果需要&#xff0c;可以使能Tamper中断&#xff0c;在每次检测到Tamper信号后执行指定代码。 在…

用23种设计模式打造一个cocos creator的游戏框架----(二十一)组合模式

1、模式标准 模式名称&#xff1a;组合模式 模式分类&#xff1a;结构型 模式意图&#xff1a;将对象组合成树型结构以表示“部分-整体”的层次结构。Composite 使得用户对单个对象和组合对象的使用具有一致性。 结构图&#xff1a; 适用于&#xff1a; 1、想表示对象的部分…

算法通关村第十关—快速排序(青铜)

快速排序 快排的基本过程 快速排序是将分治法运用到排序问题的典型例子  快速排序基本思想是&#xff1a;通过一个标记pivot元素将n个元素的序列划分为左右两个子序列left和right,.其中left中的元素都比pivot小&#xff0c;right的都比pivot的大&#xff0c;然后再次对Ieft和r…

关于折线回归

一、说明 今天的帖子主要是关于使用折线回归找到最佳值。即将某条曲线分解成包络线段&#xff0c;然后用分段回归方式优化。但它也涉及使用 SAS 和 R 的剂量反应研究和样条曲线。这不是第一篇关于这些主题的文章&#xff0c;但我确实想在其中添加折线。只是因为它还在使用。 二…

使用docker-compose搭建docker私服与配置WebUI

简介 本文介绍了使用docker compose 搭建 docker私服 环境 Docker version 24.0.6, build ed223bc Docker Compose version v2.21.0 正文 一、创建registry文件夹 我的路径是/usr/loca/docker/registry 二、创建并编写docker-compose.yml version: "3.9" services…

ios备忘录怎么导入华为 方法介绍

作为一个常常需要在不同设备间切换的人&#xff0c;我深知备忘录的重要性。那些突如其来的灵感、重要的会议提醒、甚至是生活中的琐碎小事&#xff0c;我们都习惯性地记录在备忘录里。但当我决定从iPhone转向华为时&#xff0c;一个问题困扰了我&#xff1a;如何将那些珍贵的备…

智能优化算法应用:基于学校优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于学校优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于学校优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.学校优化算法4.实验参数设定5.算法结果6.…

Axure中继器完成表格的增删改查的自定义元件(三列表格与十列表格)

目录 一、中继器 1.1 定义 1.2 特点 1.3 适用场景 二、三列表格增删改查 2.1 实现思路 2.2 效果演示 三、十列表格增删改查 3.1 实现思路 3.2 效果演示 一、中继器 1.1 定义 在Axure中&#xff0c;"中继器"通常指的是界面设计中的一个元素&#xff0c;用…