types(Uploader): use tsx (#8192)

This commit is contained in:
neverland 2021-02-21 17:04:56 +08:00 committed by GitHub
parent da09a6d871
commit 4c0120a183
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 120 additions and 72 deletions

View File

@ -1,23 +1,28 @@
import { bem } from './shared';
import { isImageFile } from './utils';
import { defineComponent, PropType } from 'vue';
// Utils
import { bem, isImageFile, FileListItem } from './utils';
import { isDef, getSizeStyle } from '../utils';
import { callInterceptor } from '../utils/interceptor';
import { callInterceptor, Interceptor } from '../utils/interceptor';
// Components
import Icon from '../icon';
import Image from '../image';
import Image, { ImageFit } from '../image';
import Loading from '../loading';
export default {
export default defineComponent({
props: {
name: String,
item: Object,
name: [String, Number],
index: Number,
imageFit: String,
imageFit: String as PropType<ImageFit>,
lazyLoad: Boolean,
deletable: Boolean,
previewSize: [Number, String],
beforeDelete: Function,
beforeDelete: Function as PropType<Interceptor>,
item: {
type: Object as PropType<FileListItem>,
required: true,
},
},
emits: ['delete', 'preview'],
@ -45,7 +50,7 @@ export default {
}
};
const onDelete = (event) => {
const onDelete = (event: MouseEvent) => {
const { name, item, index, beforeDelete } = props;
event.stopPropagation();
callInterceptor({
@ -120,4 +125,4 @@ export default {
</div>
);
},
};
});

View File

@ -1,14 +1,17 @@
import { ref, reactive } from 'vue';
import { ref, reactive, PropType } from 'vue';
// Utils
import { bem, createComponent } from './shared';
import { isPromise, getSizeStyle, pick } from '../utils';
import { pick, isPromise, getSizeStyle, ComponentInstance } from '../utils';
import {
bem,
toArray,
isOversize,
filterFiles,
isImageFile,
FileListItem,
readFileContent,
createComponent,
UploaderResultType,
} from './utils';
// Composition
@ -18,7 +21,29 @@ import { useLinkField } from '../composables/use-link-field';
// Components
import Icon from '../icon';
import PreviewItem from './PreviewItem';
import ImagePreview from '../image-preview';
import ImagePreview, { ImagePreviewOptions } from '../image-preview';
// Types
import type { ImageFit } from '../image';
import type { Interceptor } from '../utils/interceptor';
type PromiseOrNot<T> = T | Promise<T>;
export type UploaderBeforeRead = (
file: File | File[],
detail: {
name: string | number;
index: number;
}
) => PromiseOrNot<File | File[] | undefined>;
export type UploaderAfterRead = (
items: FileListItem | FileListItem[],
detail: {
name: string | number;
index: number;
}
) => void;
export default createComponent({
props: {
@ -27,11 +52,11 @@ export default createComponent({
disabled: Boolean,
lazyLoad: Boolean,
uploadText: String,
afterRead: Function,
beforeRead: Function,
beforeDelete: Function,
afterRead: Function as PropType<UploaderAfterRead>,
beforeRead: Function as PropType<UploaderBeforeRead>,
beforeDelete: Function as PropType<Interceptor>,
previewSize: [Number, String],
previewOptions: Object,
previewOptions: Object as PropType<ImagePreviewOptions>,
name: {
type: [Number, String],
default: '',
@ -41,7 +66,7 @@ export default createComponent({
default: 'image/*',
},
modelValue: {
type: Array,
type: Array as PropType<FileListItem[]>,
default: () => [],
},
maxSize: {
@ -69,11 +94,11 @@ export default createComponent({
default: true,
},
imageFit: {
type: String,
type: String as PropType<ImageFit>,
default: 'cover',
},
resultType: {
type: String,
type: String as PropType<UploaderResultType>,
default: 'dataUrl',
},
uploadIcon: {
@ -104,7 +129,7 @@ export default createComponent({
}
};
const onAfterRead = (items) => {
const onAfterRead = (items: FileListItem | FileListItem[]) => {
resetInput();
if (isOversize(items, props.maxSize)) {
@ -129,11 +154,11 @@ export default createComponent({
}
};
const readFile = (files) => {
const readFile = (files: File | File[]) => {
const { maxCount, modelValue, resultType } = props;
if (Array.isArray(files)) {
const remainCount = maxCount - modelValue.length;
const remainCount = +maxCount - modelValue.length;
if (files.length > remainCount) {
files = files.slice(0, remainCount);
@ -142,11 +167,11 @@ export default createComponent({
Promise.all(
files.map((file) => readFileContent(file, resultType))
).then((contents) => {
const fileList = files.map((file, index) => {
const result = { file, status: '', message: '' };
const fileList = (files as File[]).map((file, index) => {
const result: FileListItem = { file, status: '', message: '' };
if (contents[index]) {
result.content = contents[index];
result.content = contents[index] as string;
}
return result;
@ -156,7 +181,11 @@ export default createComponent({
});
} else {
readFileContent(files, resultType).then((content) => {
const result = { file: files, status: '', message: '' };
const result: FileListItem = {
file: files as File,
status: '',
message: '',
};
if (content) {
result.content = content;
@ -167,17 +196,18 @@ export default createComponent({
}
};
const onChange = (event) => {
let { files } = event.target;
const onChange = (event: Event) => {
const { files } = event.target as HTMLInputElement;
if (props.disabled || !files.length) {
if (props.disabled || !files || !files.length) {
return;
}
files = files.length === 1 ? files[0] : [].slice.call(files);
const file =
files.length === 1 ? files[0] : ([].slice.call(files) as File[]);
if (props.beforeRead) {
const response = props.beforeRead(files, getDetail());
const response = props.beforeRead(file, getDetail());
if (!response) {
resetInput();
@ -190,7 +220,7 @@ export default createComponent({
if (data) {
readFile(data);
} else {
readFile(files);
readFile(file);
}
})
.catch(resetInput);
@ -198,19 +228,21 @@ export default createComponent({
}
}
readFile(files);
readFile(file);
};
let imagePreview;
let imagePreview: ComponentInstance | undefined;
const onClosePreview = () => {
emit('close-preview');
};
const previewImage = (item) => {
const previewImage = (item: FileListItem) => {
if (props.previewFullImage) {
const imageFiles = props.modelValue.filter(isImageFile);
const images = imageFiles.map((item) => item.content || item.url);
const images = imageFiles
.map((item) => item.content || item.url)
.filter((item) => !!item) as string[];
imagePreview = ImagePreview({
images,
@ -227,7 +259,7 @@ export default createComponent({
}
};
const deleteFile = (item, index) => {
const deleteFile = (item: FileListItem, index: number) => {
const fileList = props.modelValue.slice(0);
fileList.splice(index, 1);
@ -235,22 +267,18 @@ export default createComponent({
emit('delete', item, getDetail(index));
};
const renderPreviewItem = (item, index) => {
const renderPreviewItem = (item: FileListItem, index: number) => {
const needPickData = [
'imageFit',
'deletable',
'previewSize',
'beforeDelete',
];
] as const;
const previewData = pick(props, needPickData);
const previewProp = pick(item, needPickData);
Object.keys(previewProp).forEach((item) => {
if (previewProp[item] !== undefined) {
previewData[item] = previewProp[item];
}
});
const previewData = {
...pick(props, needPickData),
...pick(item, needPickData, true),
};
return (
<PreviewItem
@ -289,7 +317,7 @@ export default createComponent({
type="file"
class={bem('input')}
accept={props.accept}
capture={props.capture}
capture={props.capture as any}
multiple={props.multiple}
disabled={props.disabled}
onChange={onChange}

View File

@ -1,5 +0,0 @@
import { createNamespace } from '../utils';
const [createComponent, bem] = createNamespace('uploader');
export { bem, createComponent };

View File

@ -1,4 +1,25 @@
export type ResultType = 'dataUrl' | 'text' | 'file';
import { createNamespace } from '../utils';
import type { ImageFit } from '../image';
import type { Interceptor } from '../utils/interceptor';
const [createComponent, bem] = createNamespace('uploader');
export { bem, createComponent };
export type UploaderResultType = 'dataUrl' | 'text' | 'file';
export type FileListItem = {
url?: string;
file?: File;
content?: string;
isImage?: boolean;
status?: '' | 'uploading' | 'done' | 'failed';
message?: string;
imageFit?: ImageFit;
deletable?: boolean;
previewSize?: number | string;
beforeDelete?: Interceptor;
};
export function toArray<T>(item: T | T[]): T[] {
if (Array.isArray(item)) {
@ -8,8 +29,8 @@ export function toArray<T>(item: T | T[]): T[] {
return [item];
}
export function readFileContent(file: File, resultType: ResultType) {
return new Promise<string | ArrayBuffer | null | void>((resolve) => {
export function readFileContent(file: File, resultType: UploaderResultType) {
return new Promise<string | void>((resolve) => {
if (resultType === 'file') {
resolve();
return;
@ -18,7 +39,7 @@ export function readFileContent(file: File, resultType: ResultType) {
const reader = new FileReader();
reader.onload = (event) => {
resolve((event.target as FileReader).result);
resolve((event.target as FileReader).result as string);
};
if (resultType === 'dataUrl') {
@ -29,15 +50,6 @@ export function readFileContent(file: File, resultType: ResultType) {
});
}
export type FileListItem = {
url?: string;
file?: File;
content?: string; // dataUrl
isImage?: boolean;
status?: '' | 'uploading' | 'done' | 'failed';
message?: string;
};
export function isOversize(
items: FileListItem | FileListItem[],
maxSize: number | string
@ -81,7 +93,7 @@ export function isImageFile(item: FileListItem): boolean {
return isImageUrl(item.url);
}
if (item.content) {
if (typeof item.content === 'string') {
return item.content.indexOf('data:image') === 0;
}

View File

@ -38,9 +38,15 @@ export function get(object: any, path: string): any {
return result;
}
export function pick<T, U extends keyof T>(obj: T, keys: ReadonlyArray<U>) {
export function pick<T, U extends keyof T>(
obj: T,
keys: ReadonlyArray<U>,
ignoreUndefined?: boolean
) {
return keys.reduce((ret, key) => {
ret[key] = obj[key];
if (!ignoreUndefined || obj[key] !== undefined) {
ret[key] = obj[key];
}
return ret;
}, {} as Pick<T, U>);
}

View File

@ -22,12 +22,14 @@ declare module 'vue' {
onCancel?: EventHandler;
onClosed?: EventHandler;
onChange?: EventHandler;
onDelete?: EventHandler;
onOpened?: EventHandler;
onScroll?: EventHandler;
onSubmit?: EventHandler;
onSelect?: EventHandler;
onToggle?: EventHandler;
onConfirm?: EventHandler;
onPreview?: EventHandler;
onKeypress?: EventHandler;
onTouchend?: EventHandler;
onClickStep?: EventHandler;