之前的逻辑比较简单 直接单文件选一张上传一张,没有文件类型限制,也没有删除,现在需求是表单提交时上传所有选中的文件,而不是选中一张上传一张,并且有文件校验和删除功能,咱一步一步来实现:
文件上传部分模板代码:
<el-form-item label="上传附件" prop="attachments">
<el-upload ref="uploadRef" :on-change="onUploadChange" multiple class="upload-attachments"
:auto-upload="false" action="#" list-type="picture-card">
<i slot="default" class="el-icon-plus"></i>
<div slot="file" class="upload-item" slot-scope="{file}">
<span class="doc-name" v-if="isFileTypeOfDoc(file)">{
{ file.name }}</span>
<div v-else class="upload-img-wrapper">
<img class="el-upload-list__item-thumbnail" :src="file.url">
</div>
<i @click="onRemoveFile(file)" class="delete-icon">×</i>
</div>
</el-upload>
</el-form-item>
<p class="desc">仅支持jpg、png、jpeg、doc格式,文件大小5M以内</p>
组件的props需要指定的有:
auto-upload: false 取消自动上传 不会已选中就上传
multiple 允许同时选中多个文件
list-type: picture-card 列表视图展示略缩图
on-change: 文件列表状态发生改变的回调,比如你选中文件上传了就会触发
action: '#' 这个是上传文件的接口地址 我们自定义手动上传就不写这个 写个#就行
样式:
// 附件上传
.upload-attachments {
::v-deep .el-upload-list--picture-card {
display: inline-flex;
}
::v-deep .el-upload-list__item {
width: 62px;
height: 62px;
border: none; // 去掉外层的边框
// el-upload-list__item略缩图边框大小
&.is-ready {
overflow: visible; // 不能挡住删除icon
// 略缩图大小
.upload-item {
position: relative;
width: 62px;
height: 62px;
border-radius: 4px;
border: 1px solid #c0ccda;
.doc-name {
position: absolute;
top: 50%;
width: 62px;
transform: translateY(-50%);
text-align: center;
color: $contentColor;
}
.upload-img-wrapper {
width: 60px;
height: 60px;
img {
border-radius: 4px; //圆角图片
}
}
.delete-icon {
position: absolute;
top: -7px;
right: -7px;
width: 14px;
height: 14px;
line-height: 14px;
background-color: $mainColor;
color: #fff;
text-align: center;
border-radius: 7px;
cursor: default;
}
}
}
}
.el-upload {
// 上传卡片按钮大小
::v-deep &.el-upload--picture-card {
width: 62px;
height: 62px;
line-height: 62px;
i {
font-size: 14px;
}
}
}
}
效果:
讲讲模板部分,首先使用的是el-upload组件的file插槽,自定义上传后文件的略缩图列表展示样式,默认没有上传文件时显示+号,定位了一个删除的icon在每个文件的右上角
这里因为除了图片还要支持doc文档格式,所以根据文件名后缀判断了文件类型来分别展示文件名和图片:
然后就是删除功能,就是通过ref拿到upload组件实例调用了组件handleRemove方法传入要删除的file即可
/**
* @description 删除文件
*/
onRemoveFile(file) {
const index = this.$refs.uploadRef.uploadFiles.findIndex(e => e.uid === file.uid);
this.$refs.uploadRef.uploadFiles.splice(index, 1);
},
el-upload组件内部维护了一个上传的文件集合,因为是自定义手动上传,所以选择后是没有马上上传的,file对象存在组件内部的uploadFiles这个数组中:
在每次文件列表发生变化时,我们可以在on-change回调中校验上传的文件大小和类型是否合法:
/**
* @description 用户选择文件后对文件进行校验
*/
onUploadChange(file) {
const fileType = file.raw.type;
const fileSize = file.size / 1024 / 1024;
if (!AllowedUploadFileType.includes(fileType)) {
Notification({
type: 'warning',
message: '非法的文件类型',
duration: 3000
})
this.$refs.uploadRef.handleRemove(file)
return;
}
if (fileSize > 5) {
Notification({
type: 'warning',
message: '文件大小不能超过5M',
duration: 3000,
})
this.$refs.uploadRef.handleRemove(file)
return;
}
},
如果不合法就从数组中删除掉这个文件,就能达到校验上传的文件的目的了.
最后就是点击确认提交表单时统一上传所有文件然后拿到后端返回的文件绝对路径进行提交表单:
/**
* @description 确认提交
*/
onConfirm() {
this.$refs.form.validate(async (isValid) => {
if (!isValid) {
return;
}
this.setLoading({
type: 'confirmLoading',
payload: true
})
try {
// 先上传附件得到附件地址
const readyUploadFiles = this.$refs.uploadRef.uploadFiles;
if (readyUploadFiles.length) {
const fileUrls = await this.fetchBatchUpload(readyUploadFiles);
this.formData.attachments = fileUrls;
}
// 提交反馈
const response = await feedbackAPI.userFeedbackSubmit(this.formData);
if (response.success) {
this.$refs.form.resetFields();
this.$refs.uploadRef.clearFiles();
Object.assign(this.formData, this.$options.data().formData);
this.$message({
type: 'success',
message: '操作成功',
duration: 3000
});
this.onClose();
}
} catch (error) {
this.$message({
type: 'error',
message: `提交用户反馈失败!错误的堆栈信息${error.stack}`,
duration: 5000
});
} finally {
this.setLoading({
type: 'confirmLoading',
payload: false
})
}
});
},
/**
* @description 请求批量上传
* @param {Array<File>} files 已选择的文件列表
*/
async fetchBatchUpload(files) {
const formData = new FormData();
for (const file of files) {
formData.append('files', file.raw);
}
formData.append('type', 'feedback');
const response = await feedbackAPI.uploadBatch(formData);
return response.data;
},
因为接口采用的formData 上传类型上axios上传配置项里需要修改一下Content-Type为multiplepart:
最后formData在append同一个key时不会覆盖掉已有的,在内部会处理成一个数组,也就是上面收集上传文件的参数时append这个files字段可以多次append的原因,http协议对请求头'Content-Type': 'multipart/form-data'的处理就是这样,我们看下请求报文中的显示:
多文件上传时,使用手动上传的好处就是可以节省服务器io操作和请求次数,只有最终提交表单时发送一次上传请求,但是后端接口要支持多文件,然后服务器也不会因为上传文件后不往下继续操作了导致直接上传的无用文件占用磁盘临时文件夹空间和写入磁盘的io消耗.
又要一点了 因为很累又困 写的很粗糙比较赶 算是记录一下这次上传需求吧,睡觉
文章浏览阅读6.8w次,点赞10次,收藏79次。具体分为以下步骤:本地建立仓库:在本地新建文件夹,右键 —&gt; TortoiseSVN —&gt; Create repository here;下载已有仓库:在本地新建文件夹,右键 —&gt; Checkout —&gt; 输入用户名密码、下载的文件路径;更新本地仓库:选中仓库,右键 —&gt; SVN Update;上传本地文件:选中仓库,右键 —&gt; SVN Commit..._svn 上传更新文件
文章浏览阅读104次。php cdi 几天前,在我们的常规代码审查中,我的一位同事提出了一个问题,即如果可能,一次同时调用CDI观察者(这样的方法带有参数@Observes )将发生多次?用于不同的事件实例。 换句话说,在产生少量事件之后,是否有可能同时由多个线程处理以下方法: public void observe(@Observes MyEvent myEvent) { ... } 考虑一下之后,我决定运行一..._.lockcdi
文章浏览阅读251次。最小费用最大流:(SPFA求最长路)(下面是一个求二分图最小权重的例题)375. 蚂蚁将白黑点左右分部,连接所有边,这道题等价于求总边权最小。#include<cstdio>#include<algorithm>#include<cmath>#include<cstring>#include<queue>using..._费用流模板
文章浏览阅读4.3w次。胸有成竹 → 竹柏异心 → 心安理得 → 得薄能鲜 → 鲜为人知 → 知不诈愚 → 愚不可及 → 及宾有鱼 → 鱼帛狐篝 → 篝灯呵冻 → 冻解冰释 → 释车下走 → 走伏无地 → 地北天南 → 南北东西 → 西除东荡 → 荡产倾家 → 家败人亡 → 亡不待夕 → 夕寐宵兴 → 兴不由己 → 己饥己溺 → 溺爱不明 → 明白了当 → 当场出彩 → 彩笔生花 → 花闭月羞 → 羞惭满面 → 面壁九年_车马填门,马中关五
文章浏览阅读227次。特点1、立即执行2、用完释放(function(){})()(function(){}())_js中iief
文章浏览阅读6.8k次。20170214IntelliJ IDEA 2017.1 EAP与异步堆栈跟踪调试器扩展作者钉钉用户名:@Li Zhang封面配图:文章摘要:反应性编程趋势后,我们的代码越来越异步。文章正文:早些时候java8介绍了CompletableFuture(采用Guava’s ListenableFuture),通过Akka, Ratpack, Reactor, RxJava, Vert.x以及其它库实现_ideal 调试 completablefuture
文章浏览阅读429次。【代码】基于ubuntu22.04进行对linuxqtdeploy进行源码编译,解决编译中遇到的多个错误并最终成功生成 linuxdeployqt可执行程序。_linuxdeployqt ubuntu 22
文章浏览阅读553次。tensorboard是个可视化工具,用于观察神经网络训练过程SummaryWriter:首先需要创建一个SummaryWriter示例其中有些参数不常用,我们可以只在参数里写上一个训练保存的路径即可例:writer=SummaryWriter('logs')我们在SummaryWriter中常用的方法有:1. add_scalar2. add_imageadd_scalaradd_scalar(tag, scalar_value, global_step=None, walltime=_tensorboard pytorch
文章浏览阅读2.8w次,点赞6次,收藏15次。一、前言:首先确保PyCharm是专业版而非社区版的,社区版的菜单栏中没有此功能。二、报错:输入账号、密码测试连接的时候(2020版本)(2018版本)三、错误分析:【我的错误原因】:先说结论吧,我个人的原因是因为选的是SFTP协议,因此改换Linux系统的服务器就可以了,因为本人选的是SFTP协议,这个协议需要SSH协议的支持,而SSH协议是Linux才有的,而Windows没有,准确来说是不自带。【其他可能因粗心导致的原因】:当然,如果你的服务器是Linux系统的,那么看一下_pycharm无法联网
文章浏览阅读154次。KMP算法思想朴素的匹配算法是一对一的匹配,每次匹配失败主串和模式串上的指针都回退到开头,但实际上这种回退是没有必要的而KMP算法通过对模式串的处理进而减少回退次数时间复杂度T=O(n+m)实现typedef int Position;#define NotFound -1void BuildMatch(Position match[],char *pattern,int len){ match[0]=-1; for(Position j=1;j<len;_kmp贪心算法
文章浏览阅读3.5k次,点赞3次,收藏12次。Thingsboard的网关通过tb核心服务提供的mqtt以客户端的形式连接到tb上,一个网关的连接同时只能在线一个网关服务。正常情况下,网关下的所有设备上报数据都是通过网关上报,网关下所有设备都是先把数据上报到边缘端的mqtt上,然后网关通过订阅边缘端的mqtt获取设备数据,再统一通过tb的mqtt上传到tb上,上传操作还考虑到了服务掉线的情况,会把数据暂时存起来,待服务连接正常继续上报。Thingsboard官方网关用的python写的,最早官方网关用的java,但是不知道什么原因下架换成pytho_mqtt gateway offline
文章浏览阅读519次。微信表情/emoji表情是个麻烦的东西,即使你能存储,也不一定能完美显示。在iOS以外的平台上,例如PC或者android。如果你需要显示emoji,就得准备一大堆emoji图片并使用第三方前端类库才行。即便如此,还是可能因为emoji图片不够全而出现无法显示的情况,在大多数业务场景下,emoji也不是非要不可的。我们可以适当地考虑干掉它,节约各种成本。```phppublic function ..._mysql去除emj表情