diff --git a/src/uploader/PreviewItem.js b/src/uploader/PreviewItem.js
new file mode 100644
index 000000000..d8e0f155d
--- /dev/null
+++ b/src/uploader/PreviewItem.js
@@ -0,0 +1,123 @@
+import { bem } from './shared';
+import { isImageFile } from './utils';
+import { isDef, getSizeStyle } from '../utils';
+import { callInterceptor } from '../utils/interceptor';
+
+// Components
+import Icon from '../icon';
+import Image from '../image';
+import Loading from '../loading';
+
+export default {
+ props: {
+ name: String,
+ item: Object,
+ imageFit: String,
+ lazyLoad: Boolean,
+ deletable: Boolean,
+ previewSize: [Number, String],
+ beforeDelete: Function,
+ },
+
+ emits: ['delete', 'preview'],
+
+ setup(props, { emit, slots }) {
+ const renderPreviewMask = () => {
+ const { status, message } = props.item;
+
+ if (status === 'uploading' || status === 'failed') {
+ const MaskIcon =
+ status === 'failed' ? (
+
+ ) : (
+
+ );
+
+ const showMessage = isDef(message) && message !== '';
+
+ return (
+
+ {MaskIcon}
+ {showMessage &&
{message}
}
+
+ );
+ }
+ };
+
+ const onDelete = (event) => {
+ const { name, item, index, beforeDelete } = props;
+
+ event.stopPropagation();
+ callInterceptor({
+ interceptor: beforeDelete,
+ args: [item, { name, index }],
+ done() {
+ emit('delete');
+ },
+ });
+ };
+
+ const onPreview = () => {
+ emit('preview');
+ };
+
+ const renderDeleteIcon = () => {
+ if (props.deletable && props.item.status !== 'uploading') {
+ return (
+
+
+
+ );
+ }
+ };
+
+ const renderPreviewCover = () => {
+ if (slots['preview-cover']) {
+ const { index, item } = props;
+ return (
+
+ {slots['preview-cover']({ index, ...item })}
+
+ );
+ }
+ };
+
+ const renderPreview = () => {
+ const { item } = props;
+
+ if (isImageFile(item)) {
+ return (
+
+ {renderPreviewCover()}
+
+ );
+ }
+
+ return (
+
+
+
+ {item.file ? item.file.name : item.url}
+
+ {renderPreviewCover()}
+
+ );
+ };
+
+ return () => (
+
+ {renderPreview()}
+ {renderPreviewMask()}
+ {renderDeleteIcon()}
+
+ );
+ },
+};
diff --git a/src/uploader/index.js b/src/uploader/index.js
index 888da59ce..cb4bfe2c6 100644
--- a/src/uploader/index.js
+++ b/src/uploader/index.js
@@ -1,8 +1,8 @@
import { ref } from 'vue';
// Utils
-import { callInterceptor } from '../utils/interceptor';
-import { isDef, isPromise, getSizeStyle, createNamespace } from '../utils';
+import { bem, createComponent } from './shared';
+import { isPromise, getSizeStyle, pick } from '../utils';
import { toArray, isOversize, isImageFile, readFileContent } from './utils';
// Composition
@@ -11,12 +11,9 @@ import { useParentField } from '../composition/use-parent-field';
// Components
import Icon from '../icon';
-import Image from '../image';
-import Loading from '../loading';
+import PreviewItem from './PreviewItem';
import ImagePreview from '../image-preview';
-const [createComponent, bem] = createNamespace('uploader');
-
export default createComponent({
props: {
capture: String,
@@ -101,34 +98,42 @@ export default createComponent({
}
};
- const onAfterRead = (files, oversize) => {
+ const findOversizeFiles = (files) => {
+ const valid = [];
+ const oversize = [];
+
+ files.forEach((item) => {
+ if (item.file && item.file.size > props.maxSize) {
+ oversize.push(item);
+ } else {
+ valid.push(item);
+ }
+ });
+
+ return { valid, oversize };
+ };
+
+ const onAfterRead = (items) => {
resetInput();
- let validFiles = files;
+ let validFiles = items;
- if (oversize) {
- let oversizeFiles = files;
- if (Array.isArray(files)) {
- oversizeFiles = [];
- validFiles = [];
- files.forEach((item) => {
- if (item.file) {
- if (item.file.size > props.maxSize) {
- oversizeFiles.push(item);
- } else {
- validFiles.push(item);
- }
- }
- });
+ const hasOversize = isOversize(items, props.maxSize);
+
+ if (hasOversize) {
+ if (Array.isArray(items)) {
+ const result = findOversizeFiles(items);
+ validFiles = result.valid;
+ emit('oversize', result.oversizeFiles, getDetail());
} else {
- validFiles = null;
+ emit('oversize', items, getDetail());
+ return;
}
- emit('oversize', oversizeFiles, getDetail());
}
- const isValidFiles = Array.isArray(validFiles)
- ? Boolean(validFiles.length)
- : Boolean(validFiles);
+ const isValidFiles = Boolean(
+ Array.isArray(validFiles) ? validFiles.length : validFiles
+ );
if (isValidFiles) {
emit('update:modelValue', [
@@ -143,8 +148,7 @@ export default createComponent({
};
const readFile = (files) => {
- const { maxSize, maxCount, modelValue, resultType } = props;
- const oversize = isOversize(files, maxSize);
+ const { maxCount, modelValue, resultType } = props;
if (Array.isArray(files)) {
const remainCount = maxCount - modelValue.length;
@@ -166,7 +170,7 @@ export default createComponent({
return result;
});
- onAfterRead(fileList, oversize);
+ onAfterRead(fileList);
});
} else {
readFileContent(files, resultType).then((content) => {
@@ -176,29 +180,11 @@ export default createComponent({
result.content = content;
}
- onAfterRead(result, oversize);
+ onAfterRead(result);
});
}
};
- const deleteFile = (file, index) => {
- const fileList = props.modelValue.slice(0);
- fileList.splice(index, 1);
-
- emit('update:modelValue', fileList);
- emit('delete', file, getDetail(index));
- };
-
- const onDelete = (file, index) => {
- callInterceptor({
- interceptor: props.beforeDelete,
- args: [file, getDetail(index)],
- done() {
- deleteFile(file, index);
- },
- });
- };
-
const onChange = (event) => {
let { files } = event.target;
@@ -234,19 +220,19 @@ export default createComponent({
let imagePreview;
- const onPreviewImage = (item) => {
+ const onClosePreview = () => {
+ emit('close-preview');
+ };
+
+ const previewImage = (item) => {
if (props.previewFullImage) {
- const imageFiles = props.modelValue.filter((item) => isImageFile(item));
- const imageContents = imageFiles.map(
- (item) => item.content || item.url
- );
+ const imageFiles = props.modelValue.filter(isImageFile);
+ const images = imageFiles.map((item) => item.content || item.url);
imagePreview = ImagePreview({
- images: imageContents,
+ images,
startPosition: imageFiles.indexOf(item),
- onClose: () => {
- emit('close-preview');
- },
+ onClose: onClosePreview,
...props.previewOptions,
});
}
@@ -258,95 +244,37 @@ export default createComponent({
}
};
- const chooseFile = () => {
- if (inputRef.value && !props.disabled) {
- inputRef.value.click();
- }
+ const deleteFile = (item, index) => {
+ const fileList = props.modelValue.slice(0);
+ fileList.splice(index, 1);
+
+ emit('update:modelValue', fileList);
+ emit('delete', item, getDetail(index));
};
- const renderPreviewMask = (item) => {
- const { status, message } = item;
-
- if (status === 'uploading' || status === 'failed') {
- const MaskIcon =
- status === 'failed' ? (
-
- ) : (
-
- );
-
- const showMessage = isDef(message) && message !== '';
-
- return (
-
- {MaskIcon}
- {showMessage &&
{message}
}
-
- );
- }
- };
-
- const renderPreviewItem = (item, index) => {
- const showDelete = item.status !== 'uploading' && props.deletable;
-
- const DeleteIcon = showDelete && (
- {
- event.stopPropagation();
- onDelete(item, index);
- }}
- >
-
-
- );
-
- const PreviewCover = slots['preview-cover'] && (
-
- {slots['preview-cover']({
- index,
- ...item,
- })}
-
- );
-
- const Preview = isImageFile(item) ? (
- {
- onPreviewImage(item);
- }}
- >
- {PreviewCover}
-
- ) : (
-
-
-
- {item.file ? item.file.name : item.url}
-
- {PreviewCover}
-
- );
-
- return (
- {
- emit('click-preview', item, getDetail(index));
- }}
- >
- {Preview}
- {renderPreviewMask(item)}
- {DeleteIcon}
-
- );
- };
+ const renderPreviewItem = (item, index) => (
+ {
+ emit('click-preview', item, getDetail(index));
+ }}
+ onDelete={() => {
+ deleteFile(item, index);
+ }}
+ onPreview={() => {
+ previewImage(item);
+ }}
+ {...pick(props, [
+ 'name',
+ 'lazyLoad',
+ 'imageFit',
+ 'deletable',
+ 'previewSize',
+ 'beforeDelete',
+ ])}
+ />
+ );
const renderPreviewList = () => {
if (props.previewImage) {
@@ -392,6 +320,12 @@ export default createComponent({
);
};
+ const chooseFile = () => {
+ if (inputRef.value && !props.disabled) {
+ inputRef.value.click();
+ }
+ };
+
usePublicApi({
chooseFile,
closeImagePreview,
diff --git a/src/uploader/shared.js b/src/uploader/shared.js
new file mode 100644
index 000000000..25387ce5c
--- /dev/null
+++ b/src/uploader/shared.js
@@ -0,0 +1,5 @@
+import { createNamespace } from '../utils';
+
+const [createComponent, bem] = createNamespace('uploader');
+
+export { bem, createComponent };
diff --git a/src/uploader/utils.ts b/src/uploader/utils.ts
index 7533734b9..cee47b459 100644
--- a/src/uploader/utils.ts
+++ b/src/uploader/utils.ts
@@ -30,10 +30,10 @@ export function readFileContent(file: File, resultType: ResultType) {
}
export function isOversize(
- files: File | File[],
+ items: FileListItem | FileListItem[],
maxSize: number | string
): boolean {
- return toArray(files).some((file) => file.size > maxSize);
+ return toArray(items).some((item) => item.file && item.file.size > maxSize);
}
export type FileListItem = {