特性:
- 支持批量上传文件、文件夹
- 可自定义headers
- 可自定义过滤上传格式
- 可自定义上传API接口
- 支持drag属性开启可拖拽上传文件、文件夹
- hideMessageSuccessTip:支持隐藏顶部提示,隐藏成功提示(默认不隐藏),主要应用于上传大批量文件的时候,会出现接连不断的提示框,会挡住整个屏幕,体验不好!
- 支持limit限制一次性上传文件数,并抛出exceed事件
sgUpload源码
<template> <div :class="$options.name" :dragenter="isDragenter"> <!-- 上传按钮_________________________________________________________ --> <!-- 上传文件 --> <el-upload ref="uploadFile" multiple :show-file-list="false" :headers="headers" :accept="accept.toString()" :action="actionUrl" :data="actionData" :before-upload="beforeUpload" :on-progress="uploadProgress" :on-success="uploadSuccess" :on-error="uploadError" :name="name" :auto-upload="autoUpload" :limit="limit" :on-exceed="exceed" :on-change="change" :on-remove="remove" > </el-upload> <!-- 上传文件夹 --> <el-upload ref="uploadFolder" multiple :show-file-list="false" :headers="headers" :action="actionUrl" :data="actionData" :before-upload="beforeUpload" :on-progress="uploadProgress" :on-success="uploadSuccess" :on-error="uploadError" :drag="(drag === '' || drag) && !__checkDisabledDrag()" :name="name" :auto-upload="autoUpload" :limit="limit" :on-exceed="exceed" :on-change="change" :on-remove="remove" > </el-upload> <!-- _________________________________________________________ --> <!-- 上传托盘(右下角) --> <sgUploadTray v-model="showUploadTray" :data="uploadList" @stopUpload="stopUpload" @dragStart="$emit(`dragUploadTrayStart`, true)" @dragEnd="$emit(`dragUploadTrayEnd`, true)" v-if="!(hideUploadTray === '' || hideUploadTray) && !sgUploadTray" resizeable /> </div> </template> <script> import sgUploadTray from "@/vue/components/admin/sgUploadTray"; export default { name: "sgUpload", components: { sgUploadTray }, data() { return { // 上传---------------------------------------- name: `FILE`, //上传的文件字段名 headers: { kkToken: localStorage.token }, //获取token(注意仔细看后端接受token的字段名是不是叫做“token”) accept: `.${["png", "jpg", "jpeg", "bmp", "gif", "svg"].join(",.")}`, //默认只支持图片格式上传 actionUrl: `#`, actionData: {}, dur: 100, percent: 100, uploadList: [], showUploadTray: false, uploadFileBtn: null, //上传文件 uploadFolderBtn: null, //上传文件夹 isDragenter: false, //是否拖入 leaveEvents: [ "mouseenter", "mouseover", "mousemove", "mouseout", "blur", "visibilitychange", ], minSize: 0, //支持最小上传文件大小(单位MB) maxSize: null, //支持最大上传文件大小(单位MB) autoUpload: true, //是否在选取文件后立即进行上传 limit: 1000, //限制上传文件个数(默认是1000个) currentRef: "", //当前ref // ---------------------------------------- }; }, props: [ "data", //上传可选参数 "hideUploadTray", //不显示上传托盘 "hideUploadTrayWhenDrag", //拖拽上传的时候隐藏上传托盘(默认显示) "hideMessageSuccessTip", //隐藏成功提示(默认不隐藏),主要应用于上传大批量文件的时候,会出现接连不断的提示框,会挡住整个屏幕,体验不好! "drag", //是否支持拖拽上传文件(本组件仅支持拽上传单个或多个文件,不支持拖拽上传文件夹。如需支持拖拽上传文件夹请使用【sgUploadFolder】组件) "disabledWhenShowSels", //当出现对应['sel','sel','sel',...]的时候,屏蔽拖拽上传(譬如出现element的v-modal) "sgUploadTray", //引用外部公共托盘组件dom(这种方式主要是为了解决同一个页面有多个上传托盘组件导致冲突的问题) ], watch: { data: { handler(d) { if (d) { d.name && (this.name = d.name); d.headers && (this.headers = d.headers); d.accept && (this.accept = d.accept); d.actionUrl && (this.actionUrl = d.actionUrl); d.actionData && (this.actionData = d.actionData); d.minSize && (this.minSize = d.minSize); //支持最小上传文件大小(单位MB) d.maxSize && (this.maxSize = d.maxSize); //支持最大上传文件大小(单位MB) d.limit && (this.limit = d.limit); //限制上传文件个数(默认是1000个) typeof d.autoUpload !== "undefined" && (this.autoUpload = d.autoUpload); } }, deep: true, immediate: true, }, drag: { handler(d) { if (d === "" || d) { this.addEvents(); } else { this.removeEvents(); } }, deep: true, immediate: true, }, showUploadTray(newValue, oldValue) { if (this.sgUploadTray) { this.sgUploadTray.show = newValue; } }, uploadList: { handler(d) { if (this.sgUploadTray) { this.sgUploadTray.uploadList || (this.sgUploadTray.uploadList = []); let uploadList = this.sgUploadTray.uploadList; d.forEach((newVal) => { // 避免重复添加到上传列表 if (!uploadList.some((oldVal) => oldVal.uid == newVal.uid)) { uploadList.unshift(newVal); if (this.hideUploadTrayWhenDrag === "" || this.hideUploadTrayWhenDrag) { this.sgUploadTray.show = false; //不显示右下角上传托盘 } else { this.sgUploadTray.show = true; //显示右下角上传托盘 } } }); } }, deep: true, // immediate: true, }, }, mounted() { this.$nextTick(() => { this.uploadFileBtn = this.$refs.uploadFile.$children[0].$refs.input; this.uploadFolderBtn = this.$refs.uploadFolder.$children[0].$refs.input; this.uploadFolderBtn && (this.uploadFolderBtn.webkitdirectory = true); //让el-upload支持上传文件夹 }); }, destroyed() { this.removeEvents(); }, methods: { // 取消上传文件请求 abortUploadFile(file) { this.$refs.uploadFile.abort(file); }, // 取消上传文件夹请求 abortUploadFolder(file) { this.$refs.uploadFolder.abort(file); }, __checkDisabledDrag(d) { let aa = this.disabledWhenShowSels || []; aa && (Array.isArray(aa) || (aa = [aa])); let r = []; for (let i = 0, len = aa.length; i < len; i++) { let a = aa[i]; let dom = document.querySelector(a); if (dom) { r.push(dom); return true; } } return r.length !== 0; }, // 监听---------------------------------------- addEvents(d) { this.removeEvents(); addEventListener("dragenter", this.dragenter); this.leaveEvents.forEach((v) => addEventListener(v, this.leave)); }, removeEvents(d) { removeEventListener("dragenter", this.dragenter); this.leaveEvents.forEach((v) => removeEventListener(v, this.leave)); }, dragenter(d) { this.isDragenter = true; this.currentRef = "uploadFolder"; }, leave(d) { this.isDragenter = false; }, // 上传按钮触发---------------------------------------- triggerUploadFile(d) { this.currentRef = "uploadFile"; this.uploadFileBtn && this.uploadFileBtn.click(); }, triggerUploadFolder(d) { this.currentRef = "uploadFolder"; this.uploadFolderBtn && this.uploadFolderBtn.click(); }, // 判断是否相同uid same_uid_lastModified(uid, file) { return uid == this.getFileID(file); }, // 获取uid getFileID(file) { return file.uid || file.lastModified || (file.raw || {}).lastModified; }, // 上传文件---------------------------------------------------------------- // 真的加载 showLoading(file) { this.$emit(`showLoading`, file); let fileData = this.uploadList.find((v) => this.same_uid_lastModified(v.uid, file)); //拖拽上传的时候没有uid fileData.percent = file.percentage || 0; }, getUploadListFileData(file = {}) { return this.uploadList.find((v) => this.same_uid_lastModified(v.uid, file)); //判断UUID是否相同,从自定义的uploadList获取fileData信息 }, // 真的停止加载 hideLoading(file, { status, tip, color } = {}) { this.$emit(`hideLoading`, file); if (!file) return; let fileData = this.getUploadListFileData(file); if (fileData) { switch (status) { case "error": fileData.percent = 0; break; case "success": default: fileData.percent = 100; } status && (fileData.status = status); tip && (fileData.tip = tip); color && (fileData.color = color); } }, exceed(files, fileList) { /* files:选中的文件数(文件夹或文件) fileList:上传的队列文件个数 */ this.$message.error( `本次提交了${files.length}个文件,文件数量超过了最大数${this.limit},分批次上传吧!` ); this.$emit(`exceed`, files, fileList); }, stopUpload(d) { this.$refs.uploadFolder.abort(); }, removeFileFromList(file) { this.uploadList.splice( this.uploadList.findIndex((v) => this.same_uid_lastModified(v.uid, file)), 1 ); }, //文件上传之前 beforeUpload(file) { if (file.type === "" && !file.name.includes(`.`)) return this.$message.error(`暂不支持拖拽上传文件夹`); this.uploadList.unshift({ removeFile: () => { this.$refs[this.currentRef] && this.$refs[this.currentRef].abort(file); this.removeFileFromList(file); }, isLargeFile: file.size > (this.data.largeSize || 50) * 1024 * 1024, //默认是50M以上就是大文件 uid: this.getFileID(file), //拖拽上传的时候没有uid percent: 0, //加载进度 name: file.name, size: file.size, type: file.type, webkitRelativePath: file.webkitRelativePath, status: "", //上传状态(success|error) tip: "", color: "", }); if (this.hideUploadTrayWhenDrag === "" || this.hideUploadTrayWhenDrag) { this.showUploadTray = false; //不显示右下角上传托盘 } else { this.showUploadTray = true; //显示右下角上传托盘 } // 判断是不是特定的格式________________________ let isFile = this.accept === "*" ? true : this.accept.includes(file.name.toLocaleLowerCase().split(".").pop()); isFile || this.$message.error(`上传文件只能是${this.accept}格式`); let fileSizeMB = file.size / 1024 / 1024; //转换文件大小为MB单位 const minSize = this.minSize; //限制文件最小0KB(单位MB) const isAllowMinSize = fileSizeMB >= minSize; isAllowMinSize || this.$message.error(`上传文件大小不能小于${minSize * 1000}KB`); const maxSize = this.maxSize || 500; //限制大小(单位MB) const isAllowMaxSize = fileSizeMB <= maxSize; isAllowMaxSize || this.$message.error( `传文件大小不能超过${this.$g.getSize(maxSize * 1024 * 1024)}` ); let allowUpload = isFile && isAllowMinSize && isAllowMaxSize; if (allowUpload) { this.showLoading(file); this.$nextTick(() => { this.$g.file2Base64Image(file, (d) => this.$emit(`resultBase64Image`, d)); this.$emit(`beforeUpload`, file); }); } else { this.hideLoading(file, { status: "error", tip: "上传失败", color: "red" }); } return allowUpload; //若返回false则停止上传 }, // 真实加载进度 uploadProgress(event, file, fileList) { this.showLoading(file); }, change(file, fileList) {}, remove(file, fileList) {}, // 彻底完成了所有上传队列的上传 isCompleteUploadFileList(fileList) { return !fileList.some((v) => v.percentage < 100); }, // 完成了大文件上传也要实时刷新页面(因为上传完了一个大文件就可以执行列表刷新,给用户一种马上就看到文件上传成功的感觉) isLargeFile(file) { return this.getUploadListFileData(file).isLargeFile; }, //上传成功 uploadSuccess(response, file, fileList) { if (response.data && response.data.key) { // 部分导入失败 this.hideLoading(file, { status: "error", tip: "上传失败", color: "red" }); this.$emit(`importError`, response, file, fileList); // 下载导入失败原因的描述文件 } else if (response.success) { if (this.isCompleteUploadFileList(fileList)) { this.$emit(`uploadSuccess`, response, file); } else if (this.isLargeFile(file)) { this.$emit(`uploadSuccess`, response, file); } // 上传成功了 this.hideLoading(file, { status: "success", tip: "上传成功", color: "green" }); this.hideMessageSuccessTip === "" || this.hideMessageSuccessTip || this.$message.success(`${file.name}上传成功`); } else { this.$emit(`uploadError`, response, file); // 其他失败原因 this.hideLoading(file, { status: "error", tip: "上传失败", color: "red" }); } }, //上传失败 uploadError(err, file, fileList) { file.raw && (file = file.raw); if (file.size === 0 && file.type === "") return; if (this.actionUrl === "#") return; this.$emit(`uploadError`, err, file); this.hideLoading(file, { status: "error", tip: "上传失败", color: "red" }); }, }, }; </script> <style lang="scss"> .sgUpload { width: 0; height: 0; .el-upload-dragger { z-index: 999999; //根据情况自己拿捏 position: absolute; width: 100%; height: 100%; left: 0; top: 0; display: none; background-color: #ffffff99; &::after { content: "拖拽文件到此处"; position: absolute; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; color: #409eff; font-size: 18px; font-weight: bold; line-height: 1.2; } } &[dragenter] .el-upload-dragger { display: block; border-color: #409eff; &.is-dragover { background-color: #409eff22; &::after { content: "松掉鼠标上传文件"; } } } } </style>
应用
<template> <div> <div style="width: 300px; height: 300px; position: relative"> <img :src="src" style="width: 100%; height: 100%" /> <!-- 上传组件 --> <sgUpload drag ref="sgUpload" :data="{ accept: `*`, actionUrl: `${$d.API_ROOT_URL}/customer/importCustomerData`, }" @resultBase64Image="resultBase64Image" @success="uploadSuccess" @error="uploadError" @importError="importError" hideUploadTray /> </div> <el-button type="primary" icon="el-icon-upload2" @click="(d) => $refs.sgUpload.triggerUploadFile()" >上传</el-button > </div> </template> <script> import sgUpload from "@/vue/components/admin/sgUpload"; export default { components: { sgUpload, }, data() { return { src: "", }; }, methods: { resultBase64Image(d, f) { this.src = d; }, uploadSuccess(d, f) { // 上传成功刷新相关列表数据 }, uploadError(d, f) { this.$message.error(d.msg); //上传失败提示 }, // 导入失败 importError(d, f) { let data = { key: d.data.key, sgLog: `强哥请求来源:${this.$options.name}下载导入失败原因`, }; this.$d.column_downloadImportColumnExcel({ data, r: { s: (d) => { this.$g.downloadFile(d, `${f.name}-上传失败原因`, ".xls"); this.$message.error(`${f.name}-上传失败,请查看失败原因`); // 这里也需要刷新相关列表数据 }, }, }); }, }, }; </script>
原始思想来源
升级版sgUploadFolder(可以用户上传文件夹的目录结构)