vue-cloud-upload
Version:
基于vue+elementui的通用云上传组件,无缝衔接腾讯云,华为云,火山云等桶平台,开箱即用
737 lines (736 loc) • 23.1 kB
JavaScript
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
};