import { ref, reactive, defineComponent, onBeforeUnmount, nextTick, mergeProps as _mergeProps, createVNode as _createVNode, vShow as _vShow, withDirectives as _withDirectives } from "vue"; import { pick, extend, toArray, isPromise, truthProp, getSizeStyle, makeArrayProp, makeStringProp, makeNumericProp } from "../utils/index.mjs"; import { bem, name, isOversize, filterFiles, isImageFile, readFileContent } from "./utils.mjs"; import { useCustomFieldValue } from "@vant/use"; import { useExpose } from "../composables/use-expose.mjs"; import { Icon } from "../icon/index.mjs"; import { showImagePreview } from "../image-preview/index.mjs"; import UploaderPreviewItem from "./UploaderPreviewItem.mjs"; const uploaderProps = { name: makeNumericProp(""), accept: makeStringProp("image/*"), capture: String, multiple: Boolean, disabled: Boolean, readonly: Boolean, lazyLoad: Boolean, maxCount: makeNumericProp(Infinity), imageFit: makeStringProp("cover"), resultType: makeStringProp("dataUrl"), uploadIcon: makeStringProp("photograph"), uploadText: String, deletable: truthProp, reupload: Boolean, afterRead: Function, showUpload: truthProp, modelValue: makeArrayProp(), beforeRead: Function, beforeDelete: Function, previewSize: [Number, String, Array], previewImage: truthProp, previewOptions: Object, previewFullImage: truthProp, maxSize: { type: [Number, String, Function], default: Infinity } }; var stdin_default = defineComponent({ name, props: uploaderProps, emits: ["delete", "oversize", "clickUpload", "closePreview", "clickPreview", "clickReupload", "update:modelValue"], setup(props, { emit, slots }) { const inputRef = ref(); const urls = []; const reuploadIndex = ref(-1); const isReuploading = ref(false); const getDetail = (index = props.modelValue.length) => ({ name: props.name, index }); const resetInput = () => { if (inputRef.value) { inputRef.value.value = ""; } }; const onAfterRead = (items) => { resetInput(); if (isOversize(items, props.maxSize)) { if (Array.isArray(items)) { const result = filterFiles(items, props.maxSize); items = result.valid; emit("oversize", result.invalid, getDetail()); if (!items.length) { return; } } else { emit("oversize", items, getDetail()); return; } } items = reactive(items); if (reuploadIndex.value > -1) { const arr = [...props.modelValue]; arr.splice(reuploadIndex.value, 1, items); emit("update:modelValue", arr); reuploadIndex.value = -1; } else { emit("update:modelValue", [...props.modelValue, ...toArray(items)]); } if (props.afterRead) { props.afterRead(items, getDetail()); } }; const readFile = (files) => { const { maxCount, modelValue, resultType } = props; if (Array.isArray(files)) { const remainCount = +maxCount - modelValue.length; if (files.length > remainCount) { files = files.slice(0, remainCount); } Promise.all(files.map((file) => readFileContent(file, resultType))).then((contents) => { const fileList = files.map((file, index) => { const result = { file, status: "", message: "", objectUrl: URL.createObjectURL(file) }; if (contents[index]) { result.content = contents[index]; } return result; }); onAfterRead(fileList); }); } else { readFileContent(files, resultType).then((content) => { const result = { file: files, status: "", message: "", objectUrl: URL.createObjectURL(files) }; if (content) { result.content = content; } onAfterRead(result); }); } }; const onChange = (event) => { const { files } = event.target; if (props.disabled || !files || !files.length) { return; } const file = files.length === 1 ? files[0] : [].slice.call(files); if (props.beforeRead) { const response = props.beforeRead(file, getDetail()); if (!response) { resetInput(); return; } if (isPromise(response)) { response.then((data) => { if (data) { readFile(data); } else { readFile(file); } }).catch(resetInput); return; } } readFile(file); }; let imagePreview; const onClosePreview = () => emit("closePreview"); const previewImage = (item) => { if (props.previewFullImage) { const imageFiles = props.modelValue.filter(isImageFile); const images = imageFiles.map((item2) => { if (item2.objectUrl && !item2.url && item2.status !== "failed") { item2.url = item2.objectUrl; urls.push(item2.url); } return item2.url; }).filter(Boolean); imagePreview = showImagePreview(extend({ images, startPosition: imageFiles.indexOf(item), onClose: onClosePreview }, props.previewOptions)); } }; const closeImagePreview = () => { if (imagePreview) { imagePreview.close(); } }; const deleteFile = (item, index) => { const fileList = props.modelValue.slice(0); fileList.splice(index, 1); emit("update:modelValue", fileList); emit("delete", item, getDetail(index)); }; const reuploadFile = (index) => { isReuploading.value = true; reuploadIndex.value = index; nextTick(() => chooseFile()); }; const onInputClick = () => { if (!isReuploading.value) { reuploadIndex.value = -1; } isReuploading.value = false; }; const renderPreviewItem = (item, index) => { const needPickData = ["imageFit", "deletable", "reupload", "previewSize", "beforeDelete"]; const previewData = extend(pick(props, needPickData), pick(item, needPickData, true)); return _createVNode(UploaderPreviewItem, _mergeProps({ "item": item, "index": index, "onClick": () => emit(props.reupload ? "clickReupload" : "clickPreview", item, getDetail(index)), "onDelete": () => deleteFile(item, index), "onPreview": () => previewImage(item), "onReupload": () => reuploadFile(index) }, pick(props, ["name", "lazyLoad"]), previewData), pick(slots, ["preview-cover", "preview-delete"])); }; const renderPreviewList = () => { if (props.previewImage) { return props.modelValue.map(renderPreviewItem); } }; const onClickUpload = (event) => emit("clickUpload", event); const renderUpload = () => { const lessThanMax = props.modelValue.length < +props.maxCount; const Input = props.readonly ? null : _createVNode("input", { "ref": inputRef, "type": "file", "class": bem("input"), "accept": props.accept, "capture": props.capture, "multiple": props.multiple && reuploadIndex.value === -1, "disabled": props.disabled, "onChange": onChange, "onClick": onInputClick }, null); if (slots.default) { return _withDirectives(_createVNode("div", { "class": bem("input-wrapper"), "onClick": onClickUpload }, [slots.default(), Input]), [[_vShow, lessThanMax]]); } return _withDirectives(_createVNode("div", { "class": bem("upload", { readonly: props.readonly }), "style": getSizeStyle(props.previewSize), "onClick": onClickUpload }, [_createVNode(Icon, { "name": props.uploadIcon, "class": bem("upload-icon") }, null), props.uploadText && _createVNode("span", { "class": bem("upload-text") }, [props.uploadText]), Input]), [[_vShow, props.showUpload && lessThanMax]]); }; const chooseFile = () => { if (inputRef.value && !props.disabled) { inputRef.value.click(); } }; onBeforeUnmount(() => { urls.forEach((url) => URL.revokeObjectURL(url)); }); useExpose({ chooseFile, reuploadFile, closeImagePreview }); useCustomFieldValue(() => props.modelValue); return () => _createVNode("div", { "class": bem() }, [_createVNode("div", { "class": bem("wrapper", { disabled: props.disabled }) }, [renderPreviewList(), renderUpload()])]); } }); export { stdin_default as default, uploaderProps };