mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
types(Uploader): use tsx (#8192)
This commit is contained in:
parent
da09a6d871
commit
4c0120a183
@ -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>
|
||||
);
|
||||
},
|
||||
};
|
||||
});
|
@ -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}
|
@ -1,5 +0,0 @@
|
||||
import { createNamespace } from '../utils';
|
||||
|
||||
const [createComponent, bem] = createNamespace('uploader');
|
||||
|
||||
export { bem, createComponent };
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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>);
|
||||
}
|
||||
|
2
src/vue-tsx-shim.d.ts
vendored
2
src/vue-tsx-shim.d.ts
vendored
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user