UNPKG

element-plus

Version:

A Component Library for Vue3.0

920 lines (903 loc) 25.6 kB
import { defineComponent, ref, resolveComponent, openBlock, createBlock, TransitionGroup, withCtx, Fragment, renderList, withKeys, renderSlot, createCommentVNode, createVNode, createTextVNode, toDisplayString, inject, withModifiers, watch, computed, provide, getCurrentInstance, onBeforeUnmount, h } from 'vue'; import { elFormKey } from '../el-form'; import { t } from '../locale'; import ElProgress from '../el-progress'; import cloneDeep from 'lodash/cloneDeep'; /** * Make a map and return a function for checking if a key * is in that map. * IMPORTANT: all calls of this function must be prefixed with * \/\*#\_\_PURE\_\_\*\/ * So that rollup can tree-shake them if necessary. */ const EMPTY_OBJ = (process.env.NODE_ENV !== 'production') ? Object.freeze({}) : {}; const EMPTY_ARR = (process.env.NODE_ENV !== 'production') ? Object.freeze([]) : []; const NOOP = () => { }; const hasOwnProperty = Object.prototype.hasOwnProperty; const hasOwn = (val, key) => hasOwnProperty.call(val, key); function getError(action, option, xhr) { let msg; if (xhr.response) { msg = `${xhr.response.error || xhr.response}`; } else if (xhr.responseText) { msg = `${xhr.responseText}`; } else { msg = `fail to post ${action} ${xhr.status}`; } const err = new Error(msg); err.status = xhr.status; err.method = "post"; err.url = action; return err; } function getBody(xhr) { const text = xhr.responseText || xhr.response; if (!text) { return text; } try { return JSON.parse(text); } catch (e) { return text; } } function upload(option) { if (typeof XMLHttpRequest === "undefined") { return; } const xhr = new XMLHttpRequest(); const action = option.action; if (xhr.upload) { xhr.upload.onprogress = function progress(e) { if (e.total > 0) { e.percent = e.loaded / e.total * 100; } option.onProgress(e); }; } const formData = new FormData(); if (option.data) { Object.keys(option.data).forEach((key) => { formData.append(key, option.data[key]); }); } formData.append(option.filename, option.file, option.file.name); xhr.onerror = function error() { option.onError(getError(action, option, xhr)); }; xhr.onload = function onload() { if (xhr.status < 200 || xhr.status >= 300) { return option.onError(getError(action, option, xhr)); } option.onSuccess(getBody(xhr)); }; xhr.open("post", action, true); if (option.withCredentials && "withCredentials" in xhr) { xhr.withCredentials = true; } const headers = option.headers || {}; for (const item in headers) { if (hasOwn(headers, item) && headers[item] !== null) { xhr.setRequestHeader(item, headers[item]); } } xhr.send(formData); return xhr; } var script = defineComponent({ name: "ElUploadList", components: { ElProgress }, props: { files: { type: Array, default: () => [] }, disabled: { type: Boolean, default: false }, handlePreview: { type: Function, default: () => NOOP }, listType: { type: String, default: "text" } }, emits: ["remove"], setup(props, { emit }) { const parsePercentage = (val) => { return parseInt(val, 10); }; const handleClick = (file) => { props.handlePreview(file); }; const onFileClicked = (e) => { e.target.focus(); }; const handleRemove = (e, file) => { emit("remove", file); }; return { focusing: ref(false), parsePercentage, handleClick, handleRemove, onFileClicked, t }; } }); const _hoisted_1 = /* @__PURE__ */ createVNode("i", { class: "el-icon-document" }, null, -1); const _hoisted_2 = { class: "el-upload-list__item-status-label" }; const _hoisted_3 = { key: 2, class: "el-icon-close-tip" }; const _hoisted_4 = { key: 4, class: "el-upload-list__item-actions" }; const _hoisted_5 = /* @__PURE__ */ createVNode("i", { class: "el-icon-zoom-in" }, null, -1); const _hoisted_6 = /* @__PURE__ */ createVNode("i", { class: "el-icon-delete" }, null, -1); function render(_ctx, _cache, $props, $setup, $data, $options) { const _component_el_progress = resolveComponent("el-progress"); return openBlock(), createBlock(TransitionGroup, { tag: "ul", class: [ "el-upload-list", "el-upload-list--" + _ctx.listType, { "is-disabled": _ctx.disabled } ], name: "el-list" }, { default: withCtx(() => [ (openBlock(true), createBlock(Fragment, null, renderList(_ctx.files, (file) => { return openBlock(), createBlock("li", { key: file.uid || file, class: ["el-upload-list__item", "is-" + file.status, _ctx.focusing ? "focusing" : ""], tabindex: "0", onKeydown: withKeys(($event) => !_ctx.disabled && _ctx.handleRemove($event, file), ["delete"]), onFocus: _cache[1] || (_cache[1] = ($event) => _ctx.focusing = true), onBlur: _cache[2] || (_cache[2] = ($event) => _ctx.focusing = false), onClick: _cache[3] || (_cache[3] = (...args) => _ctx.onFileClicked && _ctx.onFileClicked(...args)) }, [ renderSlot(_ctx.$slots, "default", { file }, () => [ file.status !== "uploading" && ["picture-card", "picture"].includes(_ctx.listType) ? (openBlock(), createBlock("img", { key: 0, class: "el-upload-list__item-thumbnail", src: file.url, alt: "" }, null, 8, ["src"])) : createCommentVNode("v-if", true), createVNode("a", { class: "el-upload-list__item-name", onClick: ($event) => _ctx.handleClick(file) }, [ _hoisted_1, createTextVNode(toDisplayString(file.name), 1) ], 8, ["onClick"]), createVNode("label", _hoisted_2, [ createVNode("i", { class: { "el-icon-upload-success": true, "el-icon-circle-check": _ctx.listType === "text", "el-icon-check": ["picture-card", "picture"].includes(_ctx.listType) } }, null, 2) ]), !_ctx.disabled ? (openBlock(), createBlock("i", { key: 1, class: "el-icon-close", onClick: ($event) => _ctx.handleRemove($event, file) }, null, 8, ["onClick"])) : createCommentVNode("v-if", true), createCommentVNode(" Due to close btn only appears when li gets focused disappears after li gets blurred, thus keyboard navigation can never reach close btn"), createCommentVNode(" This is a bug which needs to be fixed "), createCommentVNode(" TODO: Fix the incorrect navigation interaction "), !_ctx.disabled ? (openBlock(), createBlock("i", _hoisted_3, toDisplayString(_ctx.t("el.upload.deleteTip")), 1)) : createCommentVNode("v-if", true), file.status === "uploading" ? (openBlock(), createBlock(_component_el_progress, { key: 3, type: _ctx.listType === "picture-card" ? "circle" : "line", "stroke-width": _ctx.listType === "picture-card" ? 6 : 2, percentage: _ctx.parsePercentage(file.percentage) }, null, 8, ["type", "stroke-width", "percentage"])) : createCommentVNode("v-if", true), _ctx.listType === "picture-card" ? (openBlock(), createBlock("span", _hoisted_4, [ createVNode("span", { class: "el-upload-list__item-preview", onClick: ($event) => _ctx.handlePreview(file) }, [ _hoisted_5 ], 8, ["onClick"]), !_ctx.disabled ? (openBlock(), createBlock("span", { key: 0, class: "el-upload-list__item-delete", onClick: ($event) => _ctx.handleRemove($event, file) }, [ _hoisted_6 ], 8, ["onClick"])) : createCommentVNode("v-if", true) ])) : createCommentVNode("v-if", true) ]) ], 42, ["onKeydown"]); }), 128)) ]), _: 3 }, 8, ["class"]); } script.render = render; script.__file = "packages/upload/src/upload-list.vue"; var script$1 = defineComponent({ name: "ElUploadDrag", props: { disabled: { type: Boolean, default: false } }, emits: ["file"], setup(props, { emit }) { const uploader = inject("uploader", {}); const dragover = ref(false); function onDrop(e) { if (props.disabled || !uploader) return; const accept = uploader.accept; dragover.value = false; if (!accept) { emit("file", e.dataTransfer.files); return; } emit("file", Array.from(e.dataTransfer.files).filter((file) => { const { type, name } = file; const extension = name.indexOf(".") > -1 ? `.${name.split(".").pop()}` : ""; const baseType = type.replace(/\/.*$/, ""); return accept.split(",").map((type2) => type2.trim()).filter((type2) => type2).some((acceptedType) => { if (acceptedType.startsWith(".")) { return extension === acceptedType; } if (/\/\*$/.test(acceptedType)) { return baseType === acceptedType.replace(/\/\*$/, ""); } if (/^[^\/]+\/[^\/]+$/.test(acceptedType)) { return type === acceptedType; } return false; }); })); } function onDragover() { if (!props.disabled) dragover.value = true; } return { dragover, onDrop, onDragover }; } }); function render$1(_ctx, _cache, $props, $setup, $data, $options) { return openBlock(), createBlock("div", { class: { "el-upload-dragger": true, "is-dragover": _ctx.dragover }, onDrop: _cache[1] || (_cache[1] = withModifiers((...args) => _ctx.onDrop && _ctx.onDrop(...args), ["prevent"])), onDragover: _cache[2] || (_cache[2] = withModifiers((...args) => _ctx.onDragover && _ctx.onDragover(...args), ["prevent"])), onDragleave: _cache[3] || (_cache[3] = withModifiers(($event) => _ctx.dragover = false, ["prevent"])) }, [ renderSlot(_ctx.$slots, "default") ], 34); } script$1.render = render$1; script$1.__file = "packages/upload/src/upload-dragger.vue"; var script$2 = defineComponent({ components: { UploadDragger: script$1 }, props: { type: { type: String, default: "" }, action: { type: String, required: true }, name: { type: String, default: "file" }, data: { type: Object, default: () => null }, headers: { type: Object, default: () => null }, withCredentials: { type: Boolean, default: false }, multiple: { type: Boolean, default: null }, accept: { type: String, default: "" }, onStart: { type: Function, default: NOOP }, onProgress: { type: Function, default: NOOP }, onSuccess: { type: Function, default: NOOP }, onError: { type: Function, default: NOOP }, beforeUpload: { type: Function, default: NOOP }, drag: { type: Boolean, default: false }, onPreview: { type: Function, default: NOOP }, onRemove: { type: Function, default: NOOP }, fileList: { type: Array, default: () => [] }, autoUpload: { type: Boolean, default: true }, listType: { type: String, default: "text" }, httpRequest: { type: Function, default: () => upload }, disabled: Boolean, limit: { type: Number, default: null }, onExceed: { type: Function, default: NOOP } }, setup(props) { const reqs = ref({}); const mouseover = ref(false); const inputRef = ref(null); function uploadFiles(files) { if (props.limit && props.fileList.length + files.length > props.limit) { props.onExceed(files, props.fileList); return; } let postFiles = Array.from(files); if (!props.multiple) { postFiles = postFiles.slice(0, 1); } if (postFiles.length === 0) { return; } postFiles.forEach((rawFile) => { props.onStart(rawFile); if (props.autoUpload) upload(rawFile); }); } function upload(rawFile) { inputRef.value.value = null; if (!props.beforeUpload) { return post(rawFile); } const before = props.beforeUpload(rawFile); if (before instanceof Promise) { before.then((processedFile) => { const fileType = Object.prototype.toString.call(processedFile); if (fileType === "[object File]" || fileType === "[object Blob]") { if (fileType === "[object Blob]") { processedFile = new File([processedFile], rawFile.name, { type: rawFile.type }); } for (const p in rawFile) { if (hasOwn(rawFile, p)) { processedFile[p] = rawFile[p]; } } post(processedFile); } else { post(rawFile); } }).catch(() => { props.onRemove(null, rawFile); }); } else if (before !== false) { post(rawFile); } else { props.onRemove(null, rawFile); } } function abort(file) { const _reqs = reqs.value; if (file) { let uid = file; if (file.uid) uid = file.uid; if (_reqs[uid]) { _reqs[uid].abort(); } } else { Object.keys(_reqs).forEach((uid) => { if (_reqs[uid]) _reqs[uid].abort(); delete _reqs[uid]; }); } } function post(rawFile) { const { uid } = rawFile; const options = { headers: props.headers, withCredentials: props.withCredentials, file: rawFile, data: props.data, filename: props.name, action: props.action, onProgress: (e) => { props.onProgress(e, rawFile); }, onSuccess: (res) => { props.onSuccess(res, rawFile); delete reqs.value[uid]; }, onError: (err) => { props.onError(err, rawFile); delete reqs.value[uid]; } }; const req = props.httpRequest(options); reqs.value[uid] = req; if (req instanceof Promise) { req.then(options.onSuccess, options.onError); } } function handleChange(e) { const files = e.target.files; if (!files) return; uploadFiles(files); } function handleClick() { if (!props.disabled) { inputRef.value.value = null; inputRef.value.click(); } } function handleKeydown() { handleClick(); } return { reqs, mouseover, inputRef, abort, post, handleChange, handleClick, handleKeydown, upload, uploadFiles }; } }); function render$2(_ctx, _cache, $props, $setup, $data, $options) { const _component_upload_dragger = resolveComponent("upload-dragger"); return openBlock(), createBlock("div", { class: ["el-upload", `el-upload--${_ctx.listType}`], tabindex: "0", onClick: _cache[2] || (_cache[2] = (...args) => _ctx.handleClick && _ctx.handleClick(...args)), onKeydown: _cache[3] || (_cache[3] = withKeys(withModifiers((...args) => _ctx.handleKeydown && _ctx.handleKeydown(...args), ["self"]), ["enter", "space"])) }, [ _ctx.drag ? (openBlock(), createBlock(_component_upload_dragger, { key: 0, disabled: _ctx.disabled, onFile: _ctx.uploadFiles }, { default: withCtx(() => [ renderSlot(_ctx.$slots, "default") ]), _: 3 }, 8, ["disabled", "onFile"])) : renderSlot(_ctx.$slots, "default", { key: 1 }), createVNode("input", { ref: "inputRef", class: "el-upload__input", type: "file", name: _ctx.name, multiple: _ctx.multiple, accept: _ctx.accept, onChange: _cache[1] || (_cache[1] = (...args) => _ctx.handleChange && _ctx.handleChange(...args)) }, null, 40, ["name", "multiple", "accept"]) ], 34); } script$2.render = render$2; script$2.__file = "packages/upload/src/upload.vue"; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); function getFile(rawFile, uploadFiles) { return uploadFiles.find((file) => file.uid === rawFile.uid); } function genUid(seed) { return Date.now() + seed; } var useHandlers = (props) => { const uploadFiles = ref([]); const uploadRef = ref(null); let tempIndex = 1; function abort(file) { uploadRef.value.abort(file); } function clearFiles() { uploadFiles.value = []; } function handleError(err, rawFile) { const file = getFile(rawFile, uploadFiles.value); file.status = "fail"; uploadFiles.value.splice(uploadFiles.value.indexOf(file), 1); props.onError(err, file, uploadFiles.value); props.onChange(file, uploadFiles.value); } function handleProgress(ev, rawFile) { const file = getFile(rawFile, uploadFiles.value); props.onProgress(ev, file, uploadFiles.value); file.status = "uploading"; file.percentage = ev.percent || 0; } function handleSuccess(res, rawFile) { const file = getFile(rawFile, uploadFiles.value); if (file) { file.status = "success"; file.response = res; props.onSuccess(res, file, uploadFiles.value); props.onChange(file, uploadFiles.value); } } function handleStart(rawFile) { const uid = genUid(tempIndex++); rawFile.uid = uid; const file = { name: rawFile.name, percentage: 0, status: "ready", size: rawFile.size, raw: rawFile, uid }; if (props.listType === "picture-card" || props.listType === "picture") { try { file.url = URL.createObjectURL(rawFile); } catch (err) { console.error("[Element Error][Upload]", err); props.onError(err, file, uploadFiles.value); } } uploadFiles.value.push(file); props.onChange(file, uploadFiles.value); } function handleRemove(file, raw) { if (raw) { file = getFile(raw, uploadFiles.value); } const doRemove = () => { abort(file); const fileList = uploadFiles.value; fileList.splice(fileList.indexOf(file), 1); props.onRemove(file, fileList); }; if (!props.beforeRemove) { doRemove(); } else if (typeof props.beforeRemove === "function") { const before = props.beforeRemove(file, uploadFiles.value); if (before instanceof Promise) { before.then(() => { doRemove(); }).catch(NOOP); } else if (before !== false) { doRemove(); } } } function submit() { uploadFiles.value.filter((file) => file.status === "ready").forEach((file) => { uploadRef.value.upload(file.raw); }); } watch(() => props.listType, (val) => { if (val === "picture-card" || val === "picture") { uploadFiles.value = uploadFiles.value.map((file) => { if (!file.url && file.raw) { try { file.url = URL.createObjectURL(file.raw); } catch (err) { props.onError(err, file, uploadFiles.value); } } return file; }); } }); watch(() => props.fileList, (fileList) => { uploadFiles.value = fileList.map((file) => { const cloneFile = cloneDeep(file); return __spreadProps(__spreadValues({}, cloneFile), { uid: file.uid || genUid(tempIndex++), status: file.status || "success" }); }); }, { immediate: true, deep: true }); return { abort, clearFiles, handleError, handleProgress, handleStart, handleSuccess, handleRemove, submit, uploadFiles, uploadRef }; }; var script$3 = defineComponent({ name: "ElUpload", components: { Upload: script$2, UploadList: script }, props: { action: { type: String, required: true }, headers: { type: Object, default: () => ({}) }, data: { type: Object, default: () => ({}) }, multiple: { type: Boolean, default: false }, name: { type: String, default: "file" }, drag: { type: Boolean, default: false }, withCredentials: Boolean, showFileList: { type: Boolean, default: true }, accept: { type: String, default: "" }, type: { type: String, default: "select" }, beforeUpload: { type: Function, default: NOOP }, beforeRemove: { type: Function, default: NOOP }, onRemove: { type: Function, default: NOOP }, onChange: { type: Function, default: NOOP }, onPreview: { type: Function, default: NOOP }, onSuccess: { type: Function, default: NOOP }, onProgress: { type: Function, default: NOOP }, onError: { type: Function, default: NOOP }, fileList: { type: Array, default: () => { return []; } }, autoUpload: { type: Boolean, default: true }, listType: { type: String, default: "text" }, httpRequest: { type: Function, default: upload }, disabled: Boolean, limit: { type: Number, default: null }, onExceed: { type: Function, default: () => NOOP } }, setup(props) { const elForm = inject(elFormKey, {}); const uploadDisabled = computed(() => { return props.disabled || elForm.disabled; }); const { abort, clearFiles, handleError, handleProgress, handleStart, handleSuccess, handleRemove, submit, uploadRef, uploadFiles } = useHandlers(props); provide("uploader", getCurrentInstance()); onBeforeUnmount(() => { uploadFiles.value.forEach((file) => { if (file.url && file.url.indexOf("blob:") === 0) { URL.revokeObjectURL(file.url); } }); }); return { abort, dragOver: ref(false), draging: ref(false), handleError, handleProgress, handleRemove, handleStart, handleSuccess, uploadDisabled, uploadFiles, uploadRef, submit, clearFiles }; }, render() { var _a, _b; let uploadList; if (this.showFileList) { uploadList = h(script, { disabled: this.uploadDisabled, listType: this.listType, files: this.uploadFiles, onRemove: this.handleRemove, handlePreview: this.onPreview }, this.$slots.file ? { default: (props) => { return this.$slots.file({ file: props.file }); } } : null); } else { uploadList = null; } const uploadData = { type: this.type, drag: this.drag, action: this.action, multiple: this.multiple, "before-upload": this.beforeUpload, "with-credentials": this.withCredentials, headers: this.headers, name: this.name, data: this.data, accept: this.accept, fileList: this.uploadFiles, autoUpload: this.autoUpload, listType: this.listType, disabled: this.uploadDisabled, limit: this.limit, "on-exceed": this.onExceed, "on-start": this.handleStart, "on-progress": this.handleProgress, "on-success": this.handleSuccess, "on-error": this.handleError, "on-preview": this.onPreview, "on-remove": this.handleRemove, "http-request": this.httpRequest, ref: "uploadRef" }; const trigger = this.$slots.trigger || this.$slots.default; const uploadComponent = h(script$2, uploadData, { default: () => trigger == null ? void 0 : trigger() }); return h("div", [ this.listType === "picture-card" ? uploadList : null, this.$slots.trigger ? [uploadComponent, this.$slots.default()] : uploadComponent, (_b = (_a = this.$slots).tip) == null ? void 0 : _b.call(_a), this.listType !== "picture-card" ? uploadList : null ]); } }); script$3.__file = "packages/upload/src/index.vue"; script$3.install = (app) => { app.component(script$3.name, script$3); }; const _Upload = script$3; export default _Upload;