ckgl/node_modules/vant/es/uploader/Uploader.mjs
2024-12-21 13:52:42 +08:00

273 lines
8.7 KiB
JavaScript

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
};