UNPKG

vue-cloud-upload

Version:

基于vue+elementui的通用云上传组件,无缝衔接腾讯云,华为云,火山云等桶平台,开箱即用

737 lines (736 loc) 23.1 kB
var $ = Object.defineProperty, T = Object.defineProperties; var C = Object.getOwnPropertyDescriptors; var w = Object.getOwnPropertySymbols; var U = Object.prototype.hasOwnProperty, k = Object.prototype.propertyIsEnumerable; var v = (t, e, i) => e in t ? $(t, e, { enumerable: !0, configurable: !0, writable: !0, value: i }) : t[e] = i, b = (t, e) => { for (var i in e || (e = {})) U.call(e, i) && v(t, i, e[i]); if (w) for (var i of w(e)) k.call(e, i) && v(t, i, e[i]); return t; }, F = (t, e) => T(t, C(e)); var f = (t, e, i) => new Promise((s, n) => { var r = (a) => { try { l(i.next(a)); } catch (c) { n(c); } }, d = (a) => { try { l(i.throw(a)); } catch (c) { n(c); } }, l = (a) => a.done ? s(a.value) : Promise.resolve(a.value).then(r, d); l((i = i.apply(t, e)).next()); }); import p from "vue"; import { Upload as I, Image as S, Tooltip as L, Dialog as R, Loading as P } from "element-ui"; class o { /** * 获取文件后缀名(不带点) * @param {File|string} file 文件对象或文件名 * @return {string} 文件后缀 */ static getFileExtension(e) { return (typeof e == "string" ? e : e.name).split(".").pop().toLowerCase(); } /** * 获取文件大小(MB为单位) * @param {File} file 文件对象 * @return {number} 文件大小MB */ static getFileSizeMB(e) { return parseFloat((e.size / (1024 * 1024)).toFixed(2)); } /** * 获取文件大小带单位自动转换(B/KB/MB/GB) * @param {File} file 文件对象 * @return {string} 带单位的文件大小 */ static getFileSizeAuto(e) { const i = e.size; return i < 1024 ? i + " B" : i < 1048576 ? (i / 1024).toFixed(2) + " KB" : i < 1073741824 ? (i / 1048576).toFixed(2) + " MB" : (i / 1073741824).toFixed(2) + " GB"; } /** * 检查文件类型是否在允许列表中 * @param {File} file 文件对象 * @param {string[]} allowedTypes 允许的后缀名数组(如 ['jpg', 'png']) * @return {boolean} 是否允许 */ static checkFileType(e, i) { const s = o.getFileExtension(e); return i.includes(s); } /** * 生成文件预览URL(适用于图片/PDF等) * @param {File} file 文件对象 * @return {Promise<string>} 预览URL */ static generatePreviewURL(e) { return new Promise((i, s) => { const n = new FileReader(); n.onload = (r) => i(r.target.result), n.onerror = (r) => s(r), n.readAsDataURL(e); }); } /** * 通用文件下载方法 * @param {string} url - 文件下载地址 * @param {string} [filename] - 可选自定义文件名 */ static downloadFile(e, i) { let s = ""; if (i) s = i; else { const n = decodeURIComponent(e); s = n.substring(n.lastIndexOf("/") + 1).split("?")[0]; } fetch(e).then((n) => n.blob()).then((n) => { const r = document.createElement("a"); r.href = URL.createObjectURL(n), r.download = s, document.body.appendChild(r), r.click(), document.body.removeChild(r), URL.revokeObjectURL(r.href); }).catch((n) => console.error("下载失败:", n)); } /** * 获取文件分类 * @param {Object} file - 文件对象 */ static getFileType(e) { let i = ""; if (e.name && e.name != "") i = o.getFileExtension(e); else { if (!e.url) return "other"; i = o.getFileExtension(e.url); } if (o.getIfImage(e)) return "image"; let s = ""; switch (i) { case "doc": case "docx": s = "word"; break; case "pdf": s = "pdf"; break; case "ppt": case "pptx": s = "ppt"; break; case "xls": case "xlsx": case "csv": s = "excel"; break; case "rar": case "zip": case "7z": case "gzip": case "tar": s = "rar"; break; case "mp4": case "webm": case "ogg": case "mpeg": s = "video"; break; case "mp3": case "aac": case "wav": case "flac": case "opus": s = "audio"; break; case "txt": s = "txt"; break; default: s = "other"; break; } return s; } /** * 判断文件是否为图片 * @param {Object} file - 文件对象 */ static getIfImage(e) { let i = ""; if (e.name && e.name != "") i = o.getFileExtension(e); else { if (!e.url) return !1; i = o.getFileExtension(e.url); } return ["png", "jpg", "jpeg", "bmp", "gif", "webp", "svg"].some((n) => n === i); } /** * 获取文件名 * @param {Object} file - 文件对象 */ static getFileName(e) { if (e.name) return e.name; if (e.url && e.url != "") { const i = decodeURIComponent(e.url); return i.substring(i.lastIndexOf("/") + 1).split("?")[0]; } else return ""; } } function _(t, e, i, s, n, r, d, l) { var a = typeof t == "function" ? t.options : t; return e && (a.render = e, a.staticRenderFns = i, a._compiled = !0), r && (a._scopeId = "data-v-" + r), { exports: t, options: a }; } const z = { props: { visible: { type: Boolean }, file: { type: Object, required: !0 } }, data() { return { currentVisible: this.visible, fileType: "", fileName: "", fileRaw: null, pdfUrl: "", fileContent: "", loading: !1, fullscreen: !1 }; }, computed: { formattedText() { return this.fileContent.replace(/\n/g, "<br>"); }, dialogWidth() { return this.fileType == "audio" ? "30%" : "75%"; } }, methods: { handleOpen() { }, handleClose() { this.$emit("update:visible", !1); }, initTxtContent() { if (!this.fileRaw) return; const t = new FileReader(); t.onload = (e) => { this.fileContent = e.target.result; }, t.readAsText(this.fileRaw); }, initPdfContent() { this.pdfUrl = URL.createObjectURL(this.fileRaw); } }, watch: { visible(t) { this.currentVisible = t; }, file: function(t) { return f(this, null, function* () { if (this.loading = !0, this.fileName = o.getFileName(t), this.fileType = o.getFileType(t), t.raw && t.raw instanceof File) this.fileRaw = t.raw; else if (this.fileType == "txt" || this.fileType == "pdf") try { const i = yield (yield fetch(t.url)).blob(); this.fileRaw = i; } catch (e) { console.error("文件下载失败:", e); return; } switch (this.fileType) { case "txt": this.initTxtContent(); break; case "pdf": this.initPdfContent(); break; } this.loading = !1; }); } } }; var E = function() { var e = this, i = e._self._c; return i("el-dialog", { attrs: { visible: e.currentVisible, title: e.fileName, "custom-class": "file-preview-dialog", "append-to-body": "", fullscreen: e.fullscreen, width: e.dialogWidth }, on: { "update:visible": function(s) { e.currentVisible = s; }, open: e.handleOpen, close: e.handleClose }, scopedSlots: e._u([{ key: "title", fn: function() { return [i("div", { staticClass: "dialog-header" }, [i("span", [e._v(e._s(e.fileName))]), i("i", { class: [ "preview-header-icon", e.fullscreen ? "el-icon-copy-document" : "el-icon-full-screen" ], on: { click: function(s) { e.fullscreen = !e.fullscreen; } } })])]; }, proxy: !0 }]) }, [i("div", { directives: [{ name: "loading", rawName: "v-loading", value: e.loading, expression: "loading" }], class: [ "file-preview-content", e.fileType == "audio" ? "preview-audio" : "" ] }, [e.fileType == "txt" ? i("div", { domProps: { innerHTML: e._s(e.formattedText) } }) : e._e(), e.fileType == "pdf" ? i("iframe", { staticClass: "pdf-container", attrs: { src: e.pdfUrl, frameborder: "0" } }) : e._e(), e.fileType == "video" && e.currentVisible ? i("video", { ref: "cloud-upload-video", attrs: { controls: "", src: e.file.url, autoplay: "", preload: "auto", crossorigin: "" } }) : e._e(), e.fileType == "audio" && e.currentVisible ? i("audio", { attrs: { controls: "", src: e.file.url, autoplay: "", preload: "auto", crossorigin: "" } }) : e._e()])]); }, O = [], j = /* @__PURE__ */ _( z, E, O, !1, null, "ab02c2c8" ); const N = j.exports, V = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict"; let x = (t = 21) => { let e = "", i = crypto.getRandomValues(new Uint8Array(t |= 0)); for (; t--; ) e += V[i[t] & 63]; return e; }, h = null, y = null; p.component("el-upload", I); p.component("el-image", S); p.component("el-tooltip", L); p.component("el-dialog", R); p.use(P.directive); const B = { name: "CloudUpload", inheritAttrs: !1, props: { /** * 是否支持多选文件 */ multiple: { type: Boolean, default: !1 }, /** * 是否显示已上传文件列表 */ showFileList: { type: Boolean, default: !0 }, /** * 是否启用拖拽上传 */ drag: { type: Boolean, default: !1 }, /** * 接受上传的文件类型(thumbnail-mode 模式下此参数无效) */ accept: { type: String }, /** * 文件列表的类型 text/picture/picture-card */ listType: { type: String, default: "picture-card", validator: (t) => { const e = ["text", "picture", "picture-card"]; return e.includes(t) ? !0 : (console.error( `listType参数必须是以下值之一: ${e.join(", ")} 当前值: "${t}"将回退到默认值"picture-card"` ), !1); } }, /** * 是否禁用 */ disabled: { type: Boolean, default: !1 }, /** * 最大允许上传个数 */ limit: { type: Number }, /** * 单个附件大小限制mb */ maxSize: { type: Number }, /** * 默认ui组件大小 medium / small / mini */ size: { type: String, default: "small", validator: (t) => { const e = ["medium", "small", "mini"]; return e.includes(t) ? !0 : (console.error( `listType参数必须是以下值之一: ${e.join(", ")} 当前值: "${t}"将回退到默认值"small"` ), !1); } }, /** * 触发分块上传的阈值 默认10Mb */ sliceSize: { type: Number, default: 1024 * 1024 * 10 }, /** * 分块大小,默认5MB,非必须 */ chunkSize: { type: Number, default: 1024 * 1024 * 5 }, /** * 对象存储桶中文件的key配置 */ fileKey: { type: String, default: "uuid+name", validator: (t) => { const e = ["uuid", "name", "uuid+name"]; return e.includes(t) ? !0 : (console.error( `listType参数必须是以下值之一: ${e.join(", ")} 当前值: "${t}"将回退到默认值"uuid+name"` ), !1); } }, /** * 使用的云平台类型 tencent腾讯云桶 */ cloudType: { type: String, default: "tencent", validator: (t) => { const e = ["tencent", "huawei"]; return e.includes(t) ? !0 : (console.error( `listType参数必须是以下值之一: ${e.join(", ")} 当前值: "${t}"将回退到默认值"tencent"` ), !1); } }, /** * 云平台配置参数,包含桶名,地域,上传目录,凭证获取等 */ cloudConfig: { type: Object, required: !0, default: () => ({ bucket: "", region: "", path: "", getTempCredential: () => ({ TmpSecretId: "", TmpSecretKey: "", SecurityToken: "", StartTime: "", ExpiredTime: "" }) }) }, /** * 附件预览/在线查看配置 */ previewConfig: { type: Object, required: !1 }, /** * 自定义v-model */ value: { type: Array, default: () => { } }, /** * 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。 */ beforeUpload: { type: Function, required: !1 }, /** * 文件超出个数限制时的钩子 */ onExceed: { type: Function, required: !1 } }, data() { return { fileList: this.value, previewUrl: "", previewVisible: !1, previewFile: {} }; }, computed: { getPreviewList() { let t = []; return this.fileList.forEach((e) => { this.getIfImage(e) && t.push(e.url); }), t; }, getPreviewConfig() { return Object.assign( { image: !0, //图片附件默认开启预览 video: !0, //视频附件默认不开启预览 audio: !0, //音频附件默认不开启预览 word: !1, //word excel: !1, //excel ppt: !1, //ppt txt: !0, //txt pdf: !0, //pdf rar: !1 //压缩包 }, this.previewConfig ); } }, created() { this.checkAndInit(this.cloudConfig); }, beforeDestroy() { h.destroyInstance(); }, methods: { /** * 检查关键props传入是否规范并初始化上传实例 */ checkAndInit(t) { return f(this, null, function* () { const e = ["tencent", "huawei"]; switch (this.cloudType ? e.includes(this.cloudType) || console.warn(`云平台类型cloudType设置错误,应为${e.join("/")}`) : console.warn("未设置云平台类型cloudType!"), this.cloudType) { case "tencent": h = (yield import("./tencent-Cj6fQqIJ.js")).default, h.getInstance(t); break; case "huawei": y = (yield import("./huawei-Doo1fffo.js")).default, y.getInstance(t); } }); }, getImgRef(t) { return `previewImg${this.getPreviewList.findIndex((e) => e == t.url)}`; }, getFileName(t) { return o.getFileName(t); }, getIfImage(t) { return o.getIfImage(t); }, getFileType(t) { return o.getFileType(t); }, getFileIcon(t) { const e = this.getFileType(t); return { image: "", video: "icon-video", audio: "icon-audio", rar: "icon-yasuobao", word: "icon-WORD", excel: "icon-EXCEL", ppt: "icon-ppt", txt: "icon-txt", pdf: "icon-Pdf", other: "icon-fujian1" }[e]; }, getFileLoading(t) { var i; if (!((i = this.$refs.innerUpload) != null && i.uploadFiles)) return !1; const e = this.$refs.innerUpload.uploadFiles.find( (s) => s.uid == t.uid || s.url == t.url ); return e ? !!(e.percentage && e.percentage < 1) : !1; }, getFilePercent(t) { var i; if (!((i = this.$refs.innerUpload) != null && i.uploadFiles)) return ""; const e = this.$refs.innerUpload.uploadFiles.find( (s) => s.uid == t.uid || s.url == t.url ); return e ? e.percentage && e.percentage < 1 ? `上传中${Math.round(e.percentage * 1e3) / 10}%` : "上传完成" : ""; }, // 自定义上传方法 customUpload(t) { return f(this, null, function* () { const { file: e, onProgress: i, onSuccess: s, onError: n } = t; let r = this.generateKey(e.name); const d = F(b({ file: e, key: r, chunkSize: this.chunkSize, sliceSize: this.sliceSize }, this.cloudConfig), { onProgress: (l) => { console.log("当前进度:", l), i({ percent: l }), this.$emit("progress", l, e); } }); try { let l; switch (this.cloudType) { case "tencent": if (l = yield h.getInstance().uploadFile(d), l.statusCode == 200) { const a = this.$refs.innerUpload.uploadFiles.findIndex( (g) => g.uid == e.uid ); let c = this.$refs.innerUpload.uploadFiles[a]; const m = Object.assign(c, { url: l.url, result: l }); this.$refs.innerUpload.uploadFiles.splice(a, 1, m), this.fileList = this.$refs.innerUpload.uploadFiles; } break; case "huawei": if (l = yield y.getInstance().uploadFile(d), l.CommonMsg.Status == 200) { const a = this.$refs.innerUpload.uploadFiles.findIndex( (g) => g.uid == e.uid ); let c = this.$refs.innerUpload.uploadFiles[a]; const m = Object.assign(c, { url: l.url, result: l }); this.$refs.innerUpload.uploadFiles.splice(a, 1, m), this.fileList = this.$refs.innerUpload.uploadFiles; } break; default: throw new Error(`Unsupported cloudType: ${this.cloudType}`); } s(l, e), this.$emit("success", l, e), this.$emit("input", this.fileList); } catch (l) { n(l, e), this.$emit("error", l, e); } }); }, generateKey(t) { let e = ""; switch (this.fileKey) { case "name": e = `${this.cloudConfig.path}${t}`; break; case "uuid": const i = o.getFileExtension(t); e = `${this.cloudConfig.path}${x()}.${i}`; break; case "uuid+name": e = `${this.cloudConfig.path}${x()}/${t}`; break; } return e; }, onbeforeUpload(t) { if (this.beforeUpload && typeof this.beforeUpload == "function") return this.beforeUpload(); { let e = !0; if (this.accept) { const s = this.accept.split(",").map((n) => n.replace(".", "")); e = o.checkFileType(t, s); } const i = this.maxSize ? o.getFileSizeMB(t) < this.maxSize : !0; return e ? i ? !0 : (this.$message.error(`文件大小不能超过 ${this.maxSize}MB!`), !1) : (this.$message.error(`文件类型必须是 ${this.accept} 中的一种!`), !1); } }, handleRemove(t, e) { this.fileList = this.fileList.filter( (i) => i.uid != t.uid && i.url != t.url ), this.$emit("input", this.fileList); }, handlePreview(t) { if (this.onPreview && typeof this.onPreview == "function") this.onPreview(t); else if (this.getFileType(t) == "image") { const i = this.getImgRef(t); this.$refs[i].clickHandler(); } else this.previewFile = t, this.previewVisible = !0; }, handleDown(t) { this.$message.success("文件开始下载,请稍等!"), o.downloadFile(t.url, t.name); }, handleExceed(t, e) { this.onExceed && typeof this.onExceed == "function" ? this.onExceed(t, e) : this.$message.warning(`当前限制最多选择${this.limit}个文件!`); } }, watch: { value(t) { this.fileList = t; }, cloudType(t) { this.checkAndInit(this.cloudConfig); }, cloudConfig: { deep: !0, //immediate: true, handler(t) { this.checkAndInit(t); } } }, components: { FilePreview: N } }; var A = function() { var e = this, i = e._self._c; return i("div", { class: ["cloud-upload", "cloud-upload-" + e.size] }, [i("el-upload", e._g(e._b({ ref: "innerUpload", attrs: { action: "#", "file-list": e.fileList, multiple: e.multiple, accept: e.accept, limit: e.limit, drag: e.drag, "show-file-list": e.showFileList, "on-exceed": e.handleExceed, "on-remove": e.handleRemove, "on-preview": e.handlePreview, "before-upload": e.onbeforeUpload, "http-request": e.customUpload, "list-type": e.listType }, scopedSlots: e._u([{ key: "file", fn: function({ file: s }) { return !e.$scopedSlots.file && e.listType == "picture-card" ? [e.getIfImage(s) ? i("el-image", { directives: [{ name: "loading", rawName: "v-loading", value: e.getFileLoading(s), expression: "getFileLoading(file)" }], ref: e.getImgRef(s), staticClass: "el-upload-list__item-thumbnail", attrs: { "preview-src-list": e.getPreviewList, fit: "contain", "element-loading-text": e.getFilePercent(s), src: s.url } }) : i("div", { directives: [{ name: "loading", rawName: "v-loading", value: e.getFileLoading(s), expression: "getFileLoading(file)" }], staticClass: "el-upload-list__item-thumbnail previewIcon", attrs: { "element-loading-text": e.getFilePercent(s) } }, [i("i", { class: ["cloud-upload-icon", e.getFileIcon(s)] })]), i("span", { staticClass: "el-upload-list__item-actions" }, [e.getPreviewConfig[e.getFileType(s)] && s.status == "success" ? i("span", { staticClass: "el-upload-list__item-preview" }, [i("i", { staticClass: "el-icon-view", on: { click: () => e.handlePreview(s) } })]) : e._e(), i("span", { staticClass: "el-upload-list__item-delete" }, [i("i", { staticClass: "el-icon-download", on: { click: () => e.handleDown(s) } })]), e.disabled ? e._e() : i("span", { staticClass: "el-upload-list__item-delete", on: { click: () => e.handleRemove(s) } }, [i("i", { staticClass: "el-icon-delete" })])]), i("el-tooltip", { staticClass: "item", attrs: { effect: "light", content: e.getFileName(s), placement: "top" } }, [i("span", { staticClass: "file-name", on: { click: () => e.handleDown(s) } }, [e._v(e._s(e.getFileName(s)))])])] : void 0; } }], null, !0) }, "el-upload", e.$attrs, !1), e.$listeners), [e.$scopedSlots.default ? e._e() : [e.listType == "picture-card" ? i("div", { staticClass: "default-content" }, [i("i", { staticClass: "el-icon-upload", attrs: { slot: "default" }, slot: "default" }), i("span", { directives: [{ name: "show", rawName: "v-show", value: !e.disabled, expression: "!disabled" }] }, [e._v("点击上传")])]) : i("el-button", { attrs: { size: e.size, type: "primary" } }, [e._v("点击上传")])], e._l(e.$scopedSlots, function(s, n) { return [e._t(n, null, null, e.scoped)]; })], 2), i("FilePreview", { attrs: { visible: e.previewVisible, file: e.previewFile }, on: { "update:visible": function(s) { e.previewVisible = s; } } })], 1); }, M = [], D = /* @__PURE__ */ _( B, A, M, !1, null, "09ed794e" ); const u = D.exports; function G(t) { import("./tencent-Cj6fQqIJ.js").then((e) => { e.default.setExternalCOS(t); }); } function X(t) { import("./huawei-Doo1fffo.js").then((e) => { e.default.setExternalOBS(t); }); } u.install = function(t) { t.component(u.name, u); }; typeof window != "undefined" && window.Vue && window.Vue.component(u.name, u); export { u as default, G as setExternalCOS, X as setExternalOBS };