之前写过一篇关于el-upload单文件自定义上传文章: axios+elementui的upload使用http-request自定义文件上传_Suk_阿硕的博客-CSDN博客_http-request自定义文件上传<!-- 关于组件的部分属性: action:上传的地址,和axios没关系,elementui自己发请求 :show-file-list:是否显示上传的文件列表 :on-success:上传成功的回调 :on-error:上传失败的回调 :before-upload:上传之前的回调,可以在这里检测文件类型和大小,还可以给额外参数赋值要带到服务器的数据 i...https://blog.csdn.net/Suk__/article/details/114229334

 之前的逻辑比较简单 直接单文件选一张上传一张,没有文件类型限制,也没有删除,现在需求是表单提交时上传所有选中的文件,而不是选中一张上传一张,并且有文件校验和删除功能,咱一步一步来实现:

1.文件略缩图带删除交互实现:

文件上传部分模板代码: 

<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消耗.

又要一点了 因为很累又困 写的很粗糙比较赶 算是记录一下这次上传需求吧,睡觉😴

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐