@nutui/nutui
Version:
京东风格的轻量级移动端 Vue2、Vue3 组件库(支持小程序开发)
479 lines (478 loc) • 17.3 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
import { toRef, ref, watch, h, reactive, resolveComponent, openBlock, createElementBlock, renderSlot, createBlock, resolveDynamicComponent, createCommentVNode, Fragment, renderList, normalizeClass, createElementVNode, toDisplayString, createVNode, withModifiers } from "vue";
import { c as createComponent } from "../component-DQf3CENX.js";
import { f as funInterceptor } from "../Interceptor-CBzqGJrl.js";
import { Progress as _sfc_main$1 } from "../progress/Progress.js";
import { Photograph, Failure, Loading, Del, Link } from "@nutui/icons-vue";
import { u as useLocale } from "../index-BOB4ytqZ.js";
import { u as useFormDisabled } from "../common-BH7uB7Cn.js";
import { _ as _export_sfc } from "../_plugin-vue_export-helper-1tPrXgE0.js";
class UploadOptions {
constructor() {
__publicField(this, "url", "");
__publicField(this, "name", "file");
__publicField(this, "fileType", "image");
__publicField(this, "formData");
__publicField(this, "sourceFile");
__publicField(this, "method", "post");
__publicField(this, "xhrState", 200);
__publicField(this, "timeout", 30 * 1e3);
__publicField(this, "headers", {});
__publicField(this, "withCredentials", false);
__publicField(this, "onStart");
__publicField(this, "taroFilePath");
__publicField(this, "onProgress");
__publicField(this, "onSuccess");
__publicField(this, "onFailure");
__publicField(this, "beforeXhrUpload");
}
}
class Uploader {
constructor(options) {
__publicField(this, "options");
this.options = options;
}
upload() {
var _a;
const options = this.options;
const xhr = new XMLHttpRequest();
xhr.timeout = options.timeout;
if (xhr.upload) {
xhr.upload.addEventListener(
"progress",
(e) => {
var _a2;
(_a2 = options.onProgress) == null ? void 0 : _a2.call(options, e, options);
},
false
);
xhr.onreadystatechange = () => {
var _a2, _b;
if (xhr.readyState === 4) {
if (xhr.status == options.xhrState) {
(_a2 = options.onSuccess) == null ? void 0 : _a2.call(options, xhr.responseText, options);
} else {
(_b = options.onFailure) == null ? void 0 : _b.call(options, xhr.responseText, options);
}
}
};
xhr.withCredentials = options.withCredentials;
xhr.open(options.method, options.url, true);
for (const [key, value] of Object.entries(options.headers)) {
xhr.setRequestHeader(key, value);
}
(_a = options.onStart) == null ? void 0 : _a.call(options, options);
if (options.beforeXhrUpload) {
options.beforeXhrUpload(xhr, options);
} else {
xhr.send(options.formData);
}
} else {
console.warn("浏览器不支持 XMLHttpRequest");
}
}
}
class FileItem {
constructor() {
__publicField(this, "status", "ready");
__publicField(this, "message", "");
__publicField(this, "uid", (/* @__PURE__ */ new Date()).getTime().toString());
__publicField(this, "name");
__publicField(this, "url");
__publicField(this, "type");
__publicField(this, "path");
__publicField(this, "percentage", 0);
__publicField(this, "formData", {});
}
}
const { create } = createComponent("uploader");
const cN = "NutUploader";
const _sfc_main = create({
components: {
NutProgress: _sfc_main$1,
Photograph,
Failure,
Loading,
Del,
Link
},
props: {
name: { type: String, default: "file" },
url: { type: String, default: "" },
// defaultFileList: { type: Array, default: () => new Array<FileItem>() },
timeout: { type: [Number, String], default: 1e3 * 30 },
fileList: { type: Array, default: () => [] },
isPreview: { type: Boolean, default: true },
// picture、list
listType: { type: String, default: "picture" },
isDeletable: { type: Boolean, default: true },
method: { type: String, default: "post" },
capture: { type: Boolean, default: false },
maximize: { type: [Number, String], default: Number.MAX_VALUE },
maximum: { type: [Number, String], default: 1 },
clearInput: { type: Boolean, default: true },
accept: { type: String, default: "*" },
headers: { type: Object, default: {} },
data: { type: Object, default: {} },
xhrState: { type: [Number, String], default: 200 },
withCredentials: { type: Boolean, default: false },
multiple: { type: Boolean, default: false },
disabled: { type: Boolean, default: false },
autoUpload: { type: Boolean, default: true },
beforeUpload: {
type: Function,
default: null
},
beforeXhrUpload: {
type: Function,
default: null
},
beforeDelete: {
type: Function,
default: () => {
return true;
}
},
onChange: { type: Function }
},
emits: [
"start",
"progress",
"oversize",
"success",
"failure",
"change",
"delete",
"update:fileList",
"fileItemClick"
],
setup(props, { emit }) {
const disabled = useFormDisabled(toRef(props, "disabled"));
const translate = useLocale(cN);
const fileList = ref(props.fileList);
const uploadQueue = ref([]);
watch(
() => props.fileList,
() => {
fileList.value = props.fileList;
}
);
const renderInput = () => {
let params = {
class: `nut-uploader__input`,
type: "file",
accept: props.accept,
multiple: props.multiple,
name: props.name,
disabled: disabled.value
};
if (props.capture) {
params.capture = "camera";
if (!params.accept) {
params.accept = "image/*";
}
}
return h("input", params);
};
const clearInput = (el) => {
el.value = "";
};
const fileItemClick = (fileItem) => {
emit("fileItemClick", { fileItem });
};
const executeUpload = (fileItem, index2) => {
const uploadOption = new UploadOptions();
uploadOption.url = props.url;
uploadOption.formData = fileItem.formData;
uploadOption.timeout = props.timeout * 1;
uploadOption.method = props.method;
uploadOption.xhrState = props.xhrState;
uploadOption.headers = props.headers;
uploadOption.withCredentials = props.withCredentials;
uploadOption.beforeXhrUpload = props.beforeXhrUpload;
try {
uploadOption.sourceFile = fileItem.formData.get(props.name);
} catch (error) {
console.warn("[NutUI] <Uploader> formData.get(name)", error);
}
uploadOption.onStart = (option) => {
fileItem.status = "ready";
fileItem.message = translate("readyUpload");
clearUploadQueue(index2);
emit("start", option);
};
uploadOption.onProgress = (event, option) => {
fileItem.status = "uploading";
fileItem.message = translate("uploading");
fileItem.percentage = (event.loaded / event.total * 100).toFixed(0);
emit("progress", { event, option, percentage: fileItem.percentage });
};
uploadOption.onSuccess = (responseText, option) => {
fileItem.status = "success";
fileItem.message = translate("success");
emit("success", {
responseText,
option,
fileItem
});
emit("update:fileList", fileList.value);
};
uploadOption.onFailure = (responseText, option) => {
fileItem.status = "error";
fileItem.message = translate("error");
emit("failure", {
responseText,
option,
fileItem
});
};
let task = new Uploader(uploadOption);
if (props.autoUpload) {
task.upload();
} else {
uploadQueue.value.push(
new Promise((resolve) => {
resolve(task);
})
);
}
};
const clearUploadQueue = (index2 = -1) => {
if (index2 > -1) {
uploadQueue.value.splice(index2, 1);
} else {
uploadQueue.value = [];
fileList.value = [];
emit("update:fileList", fileList.value);
}
};
const submit = () => {
Promise.all(uploadQueue.value).then((res) => {
res.forEach((i) => i.upload());
});
};
const readFile = (files) => {
files.forEach((file, index2) => {
const formData = new FormData();
for (const [key, value] of Object.entries(props.data)) {
formData.append(key, value);
}
formData.append(props.name, file);
const fileItem = reactive(new FileItem());
fileItem.name = file.name;
fileItem.status = "ready";
fileItem.type = file.type;
fileItem.formData = formData;
fileItem.message = translate("waitingUpload");
executeUpload(fileItem, index2);
if (props.isPreview && file.type.includes("image")) {
const reader = new FileReader();
reader.onload = (event) => {
fileItem.url = event.target.result;
fileList.value.push(fileItem);
};
reader.readAsDataURL(file);
} else {
fileList.value.push(fileItem);
}
});
};
const filterFiles = (files) => {
const maximum = props.maximum * 1;
const maximize = props.maximize * 1;
const oversizes = new Array();
files = files.filter((file) => {
if (file.size > maximize) {
oversizes.push(file);
return false;
} else {
return true;
}
});
if (oversizes.length) {
emit("oversize", oversizes);
}
let currentFileLength = files.length + fileList.value.length;
if (currentFileLength > maximum) {
files.splice(files.length - (currentFileLength - maximum));
}
return files;
};
const deleted = (file, index2) => {
fileList.value.splice(index2, 1);
emit("delete", {
file,
fileList: fileList.value,
index: index2
});
};
const onDelete = (file, index2) => {
if (disabled.value) return;
clearUploadQueue(index2);
funInterceptor(props.beforeDelete, {
args: [file, fileList.value],
done: () => deleted(file, index2)
});
};
const onChange = (event) => {
if (props.disabled || disabled.value) {
return;
}
const $el = event.target;
let { files } = $el;
if (props.beforeUpload) {
props.beforeUpload(files).then((f) => changeReadFile(f));
} else {
changeReadFile(files);
}
emit("change", {
fileList: fileList.value,
event
});
if (props.clearInput) {
clearInput($el);
}
};
const changeReadFile = (f) => {
const _files = filterFiles(new Array().slice.call(f));
readFile(_files);
};
return {
onChange,
onDelete,
fileList,
fileItemClick,
clearUploadQueue,
submit,
renderInput
};
}
});
const _hoisted_1 = { class: "nut-uploader" };
const _hoisted_2 = {
key: 0,
class: "nut-uploader__slot"
};
const _hoisted_3 = {
key: 0,
class: "nut-uploader__preview-img"
};
const _hoisted_4 = {
key: 0,
class: "nut-uploader__preview__progress"
};
const _hoisted_5 = { class: "nut-uploader__preview__progress__msg" };
const _hoisted_6 = ["onClick"];
const _hoisted_7 = ["src", "onClick"];
const _hoisted_8 = {
key: 3,
class: "nut-uploader__preview-img__file"
};
const _hoisted_9 = ["onClick"];
const _hoisted_10 = { class: "file__name_tips" };
const _hoisted_11 = { class: "tips" };
const _hoisted_12 = {
key: 1,
class: "nut-uploader__preview-list"
};
const _hoisted_13 = ["onClick"];
const _hoisted_14 = { class: "file__name_tips" };
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_Failure = resolveComponent("Failure");
const _component_Loading = resolveComponent("Loading");
const _component_Link = resolveComponent("Link");
const _component_Del = resolveComponent("Del");
const _component_nut_progress = resolveComponent("nut-progress");
const _component_Photograph = resolveComponent("Photograph");
return openBlock(), createElementBlock("view", _hoisted_1, [
_ctx.$slots.default ? (openBlock(), createElementBlock("view", _hoisted_2, [
renderSlot(_ctx.$slots, "default"),
Number(_ctx.maximum) - _ctx.fileList.length ? (openBlock(), createBlock(resolveDynamicComponent(_ctx.renderInput), {
key: 0,
onChange: _ctx.onChange
}, null, 40, ["onChange"])) : createCommentVNode("", true)
])) : createCommentVNode("", true),
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.fileList, (item, index2) => {
var _a;
return openBlock(), createElementBlock("view", {
key: item.uid,
class: normalizeClass(["nut-uploader__preview", [_ctx.listType]])
}, [
_ctx.listType == "picture" && !_ctx.$slots.default ? (openBlock(), createElementBlock("view", _hoisted_3, [
item.status != "success" ? (openBlock(), createElementBlock("view", _hoisted_4, [
item.status != "ready" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
item.status == "error" ? (openBlock(), createBlock(_component_Failure, {
key: 0,
color: "#fff"
})) : (openBlock(), createBlock(_component_Loading, {
key: 1,
name: "loading",
color: "#fff"
}))
], 64)) : createCommentVNode("", true),
createElementVNode("view", _hoisted_5, toDisplayString(item.message), 1)
])) : createCommentVNode("", true),
_ctx.isDeletable ? (openBlock(), createElementBlock("view", {
key: 1,
class: "close",
onClick: ($event) => _ctx.onDelete(item, index2)
}, [
renderSlot(_ctx.$slots, "delete-icon", {}, () => [
createVNode(_component_Failure)
])
], 8, _hoisted_6)) : createCommentVNode("", true),
((_a = item == null ? void 0 : item.type) == null ? void 0 : _a.includes("image")) && item.url ? (openBlock(), createElementBlock("img", {
key: 2,
class: "nut-uploader__preview-img__c",
src: item.url,
onClick: ($event) => _ctx.fileItemClick(item)
}, null, 8, _hoisted_7)) : (openBlock(), createElementBlock("view", _hoisted_8, [
createElementVNode("view", {
class: "nut-uploader__preview-img__file__name",
onClick: ($event) => _ctx.fileItemClick(item)
}, [
createElementVNode("view", _hoisted_10, toDisplayString(item.name), 1)
], 8, _hoisted_9)
])),
createElementVNode("view", _hoisted_11, toDisplayString(item.name), 1)
])) : _ctx.listType == "list" ? (openBlock(), createElementBlock("view", _hoisted_12, [
createElementVNode("view", {
class: normalizeClass(["nut-uploader__preview-img__file__name", [item.status]]),
onClick: ($event) => _ctx.fileItemClick(item)
}, [
createVNode(_component_Link, { class: "nut-uploader__preview-img__file__link" }),
createElementVNode("view", _hoisted_14, toDisplayString(item.name), 1),
_ctx.isDeletable ? (openBlock(), createBlock(_component_Del, {
key: 0,
color: "#808080",
class: "nut-uploader__preview-img__file__del",
onClick: withModifiers(($event) => _ctx.onDelete(item, index2), ["stop"])
}, null, 8, ["onClick"])) : createCommentVNode("", true)
], 10, _hoisted_13),
item.status == "uploading" ? (openBlock(), createBlock(_component_nut_progress, {
key: 0,
size: "small",
percentage: item.percentage,
"stroke-color": "linear-gradient(270deg, rgba(18,126,255,1) 0%,rgba(32,147,255,1) 32.815625%,rgba(13,242,204,1) 100%)",
"show-text": false
}, null, 8, ["percentage"])) : createCommentVNode("", true)
])) : createCommentVNode("", true)
], 2);
}), 128)),
_ctx.listType == "picture" && !_ctx.$slots.default && Number(_ctx.maximum) - _ctx.fileList.length ? (openBlock(), createElementBlock("view", {
key: 1,
class: normalizeClass(["nut-uploader__upload", [_ctx.listType]])
}, [
renderSlot(_ctx.$slots, "upload-icon", {}, () => [
createVNode(_component_Photograph, { color: "#808080" })
]),
(openBlock(), createBlock(resolveDynamicComponent(_ctx.renderInput), { onChange: _ctx.onChange }, null, 40, ["onChange"]))
], 2)) : createCommentVNode("", true)
]);
}
const index = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
export {
index as default
};