mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-26 11:26:35 +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 { defineComponent, PropType } from 'vue';
|
||||||
import { isImageFile } from './utils';
|
|
||||||
|
// Utils
|
||||||
|
import { bem, isImageFile, FileListItem } from './utils';
|
||||||
import { isDef, getSizeStyle } from '../utils';
|
import { isDef, getSizeStyle } from '../utils';
|
||||||
import { callInterceptor } from '../utils/interceptor';
|
import { callInterceptor, Interceptor } from '../utils/interceptor';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Icon from '../icon';
|
import Icon from '../icon';
|
||||||
import Image from '../image';
|
import Image, { ImageFit } from '../image';
|
||||||
import Loading from '../loading';
|
import Loading from '../loading';
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
name: String,
|
name: [String, Number],
|
||||||
item: Object,
|
|
||||||
index: Number,
|
index: Number,
|
||||||
imageFit: String,
|
imageFit: String as PropType<ImageFit>,
|
||||||
lazyLoad: Boolean,
|
lazyLoad: Boolean,
|
||||||
deletable: Boolean,
|
deletable: Boolean,
|
||||||
previewSize: [Number, String],
|
previewSize: [Number, String],
|
||||||
beforeDelete: Function,
|
beforeDelete: Function as PropType<Interceptor>,
|
||||||
|
item: {
|
||||||
|
type: Object as PropType<FileListItem>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
emits: ['delete', 'preview'],
|
emits: ['delete', 'preview'],
|
||||||
@ -45,7 +50,7 @@ export default {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDelete = (event) => {
|
const onDelete = (event: MouseEvent) => {
|
||||||
const { name, item, index, beforeDelete } = props;
|
const { name, item, index, beforeDelete } = props;
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
callInterceptor({
|
callInterceptor({
|
||||||
@ -120,4 +125,4 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
});
|
@ -1,14 +1,17 @@
|
|||||||
import { ref, reactive } from 'vue';
|
import { ref, reactive, PropType } from 'vue';
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
import { bem, createComponent } from './shared';
|
import { pick, isPromise, getSizeStyle, ComponentInstance } from '../utils';
|
||||||
import { isPromise, getSizeStyle, pick } from '../utils';
|
|
||||||
import {
|
import {
|
||||||
|
bem,
|
||||||
toArray,
|
toArray,
|
||||||
isOversize,
|
isOversize,
|
||||||
filterFiles,
|
filterFiles,
|
||||||
isImageFile,
|
isImageFile,
|
||||||
|
FileListItem,
|
||||||
readFileContent,
|
readFileContent,
|
||||||
|
createComponent,
|
||||||
|
UploaderResultType,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
// Composition
|
// Composition
|
||||||
@ -18,7 +21,29 @@ import { useLinkField } from '../composables/use-link-field';
|
|||||||
// Components
|
// Components
|
||||||
import Icon from '../icon';
|
import Icon from '../icon';
|
||||||
import PreviewItem from './PreviewItem';
|
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({
|
export default createComponent({
|
||||||
props: {
|
props: {
|
||||||
@ -27,11 +52,11 @@ export default createComponent({
|
|||||||
disabled: Boolean,
|
disabled: Boolean,
|
||||||
lazyLoad: Boolean,
|
lazyLoad: Boolean,
|
||||||
uploadText: String,
|
uploadText: String,
|
||||||
afterRead: Function,
|
afterRead: Function as PropType<UploaderAfterRead>,
|
||||||
beforeRead: Function,
|
beforeRead: Function as PropType<UploaderBeforeRead>,
|
||||||
beforeDelete: Function,
|
beforeDelete: Function as PropType<Interceptor>,
|
||||||
previewSize: [Number, String],
|
previewSize: [Number, String],
|
||||||
previewOptions: Object,
|
previewOptions: Object as PropType<ImagePreviewOptions>,
|
||||||
name: {
|
name: {
|
||||||
type: [Number, String],
|
type: [Number, String],
|
||||||
default: '',
|
default: '',
|
||||||
@ -41,7 +66,7 @@ export default createComponent({
|
|||||||
default: 'image/*',
|
default: 'image/*',
|
||||||
},
|
},
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: Array,
|
type: Array as PropType<FileListItem[]>,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
maxSize: {
|
maxSize: {
|
||||||
@ -69,11 +94,11 @@ export default createComponent({
|
|||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
imageFit: {
|
imageFit: {
|
||||||
type: String,
|
type: String as PropType<ImageFit>,
|
||||||
default: 'cover',
|
default: 'cover',
|
||||||
},
|
},
|
||||||
resultType: {
|
resultType: {
|
||||||
type: String,
|
type: String as PropType<UploaderResultType>,
|
||||||
default: 'dataUrl',
|
default: 'dataUrl',
|
||||||
},
|
},
|
||||||
uploadIcon: {
|
uploadIcon: {
|
||||||
@ -104,7 +129,7 @@ export default createComponent({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onAfterRead = (items) => {
|
const onAfterRead = (items: FileListItem | FileListItem[]) => {
|
||||||
resetInput();
|
resetInput();
|
||||||
|
|
||||||
if (isOversize(items, props.maxSize)) {
|
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;
|
const { maxCount, modelValue, resultType } = props;
|
||||||
|
|
||||||
if (Array.isArray(files)) {
|
if (Array.isArray(files)) {
|
||||||
const remainCount = maxCount - modelValue.length;
|
const remainCount = +maxCount - modelValue.length;
|
||||||
|
|
||||||
if (files.length > remainCount) {
|
if (files.length > remainCount) {
|
||||||
files = files.slice(0, remainCount);
|
files = files.slice(0, remainCount);
|
||||||
@ -142,11 +167,11 @@ export default createComponent({
|
|||||||
Promise.all(
|
Promise.all(
|
||||||
files.map((file) => readFileContent(file, resultType))
|
files.map((file) => readFileContent(file, resultType))
|
||||||
).then((contents) => {
|
).then((contents) => {
|
||||||
const fileList = files.map((file, index) => {
|
const fileList = (files as File[]).map((file, index) => {
|
||||||
const result = { file, status: '', message: '' };
|
const result: FileListItem = { file, status: '', message: '' };
|
||||||
|
|
||||||
if (contents[index]) {
|
if (contents[index]) {
|
||||||
result.content = contents[index];
|
result.content = contents[index] as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -156,7 +181,11 @@ export default createComponent({
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
readFileContent(files, resultType).then((content) => {
|
readFileContent(files, resultType).then((content) => {
|
||||||
const result = { file: files, status: '', message: '' };
|
const result: FileListItem = {
|
||||||
|
file: files as File,
|
||||||
|
status: '',
|
||||||
|
message: '',
|
||||||
|
};
|
||||||
|
|
||||||
if (content) {
|
if (content) {
|
||||||
result.content = content;
|
result.content = content;
|
||||||
@ -167,17 +196,18 @@ export default createComponent({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onChange = (event) => {
|
const onChange = (event: Event) => {
|
||||||
let { files } = event.target;
|
const { files } = event.target as HTMLInputElement;
|
||||||
|
|
||||||
if (props.disabled || !files.length) {
|
if (props.disabled || !files || !files.length) {
|
||||||
return;
|
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) {
|
if (props.beforeRead) {
|
||||||
const response = props.beforeRead(files, getDetail());
|
const response = props.beforeRead(file, getDetail());
|
||||||
|
|
||||||
if (!response) {
|
if (!response) {
|
||||||
resetInput();
|
resetInput();
|
||||||
@ -190,7 +220,7 @@ export default createComponent({
|
|||||||
if (data) {
|
if (data) {
|
||||||
readFile(data);
|
readFile(data);
|
||||||
} else {
|
} else {
|
||||||
readFile(files);
|
readFile(file);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(resetInput);
|
.catch(resetInput);
|
||||||
@ -198,19 +228,21 @@ export default createComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readFile(files);
|
readFile(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
let imagePreview;
|
let imagePreview: ComponentInstance | undefined;
|
||||||
|
|
||||||
const onClosePreview = () => {
|
const onClosePreview = () => {
|
||||||
emit('close-preview');
|
emit('close-preview');
|
||||||
};
|
};
|
||||||
|
|
||||||
const previewImage = (item) => {
|
const previewImage = (item: FileListItem) => {
|
||||||
if (props.previewFullImage) {
|
if (props.previewFullImage) {
|
||||||
const imageFiles = props.modelValue.filter(isImageFile);
|
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({
|
imagePreview = ImagePreview({
|
||||||
images,
|
images,
|
||||||
@ -227,7 +259,7 @@ export default createComponent({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteFile = (item, index) => {
|
const deleteFile = (item: FileListItem, index: number) => {
|
||||||
const fileList = props.modelValue.slice(0);
|
const fileList = props.modelValue.slice(0);
|
||||||
fileList.splice(index, 1);
|
fileList.splice(index, 1);
|
||||||
|
|
||||||
@ -235,22 +267,18 @@ export default createComponent({
|
|||||||
emit('delete', item, getDetail(index));
|
emit('delete', item, getDetail(index));
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderPreviewItem = (item, index) => {
|
const renderPreviewItem = (item: FileListItem, index: number) => {
|
||||||
const needPickData = [
|
const needPickData = [
|
||||||
'imageFit',
|
'imageFit',
|
||||||
'deletable',
|
'deletable',
|
||||||
'previewSize',
|
'previewSize',
|
||||||
'beforeDelete',
|
'beforeDelete',
|
||||||
];
|
] as const;
|
||||||
|
|
||||||
const previewData = pick(props, needPickData);
|
const previewData = {
|
||||||
const previewProp = pick(item, needPickData);
|
...pick(props, needPickData),
|
||||||
|
...pick(item, needPickData, true),
|
||||||
Object.keys(previewProp).forEach((item) => {
|
};
|
||||||
if (previewProp[item] !== undefined) {
|
|
||||||
previewData[item] = previewProp[item];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PreviewItem
|
<PreviewItem
|
||||||
@ -289,7 +317,7 @@ export default createComponent({
|
|||||||
type="file"
|
type="file"
|
||||||
class={bem('input')}
|
class={bem('input')}
|
||||||
accept={props.accept}
|
accept={props.accept}
|
||||||
capture={props.capture}
|
capture={props.capture as any}
|
||||||
multiple={props.multiple}
|
multiple={props.multiple}
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
onChange={onChange}
|
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[] {
|
export function toArray<T>(item: T | T[]): T[] {
|
||||||
if (Array.isArray(item)) {
|
if (Array.isArray(item)) {
|
||||||
@ -8,8 +29,8 @@ export function toArray<T>(item: T | T[]): T[] {
|
|||||||
return [item];
|
return [item];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readFileContent(file: File, resultType: ResultType) {
|
export function readFileContent(file: File, resultType: UploaderResultType) {
|
||||||
return new Promise<string | ArrayBuffer | null | void>((resolve) => {
|
return new Promise<string | void>((resolve) => {
|
||||||
if (resultType === 'file') {
|
if (resultType === 'file') {
|
||||||
resolve();
|
resolve();
|
||||||
return;
|
return;
|
||||||
@ -18,7 +39,7 @@ export function readFileContent(file: File, resultType: ResultType) {
|
|||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|
||||||
reader.onload = (event) => {
|
reader.onload = (event) => {
|
||||||
resolve((event.target as FileReader).result);
|
resolve((event.target as FileReader).result as string);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (resultType === 'dataUrl') {
|
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(
|
export function isOversize(
|
||||||
items: FileListItem | FileListItem[],
|
items: FileListItem | FileListItem[],
|
||||||
maxSize: number | string
|
maxSize: number | string
|
||||||
@ -81,7 +93,7 @@ export function isImageFile(item: FileListItem): boolean {
|
|||||||
return isImageUrl(item.url);
|
return isImageUrl(item.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.content) {
|
if (typeof item.content === 'string') {
|
||||||
return item.content.indexOf('data:image') === 0;
|
return item.content.indexOf('data:image') === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,9 +38,15 @@ export function get(object: any, path: string): any {
|
|||||||
return result;
|
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) => {
|
return keys.reduce((ret, key) => {
|
||||||
|
if (!ignoreUndefined || obj[key] !== undefined) {
|
||||||
ret[key] = obj[key];
|
ret[key] = obj[key];
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}, {} as Pick<T, U>);
|
}, {} 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;
|
onCancel?: EventHandler;
|
||||||
onClosed?: EventHandler;
|
onClosed?: EventHandler;
|
||||||
onChange?: EventHandler;
|
onChange?: EventHandler;
|
||||||
|
onDelete?: EventHandler;
|
||||||
onOpened?: EventHandler;
|
onOpened?: EventHandler;
|
||||||
onScroll?: EventHandler;
|
onScroll?: EventHandler;
|
||||||
onSubmit?: EventHandler;
|
onSubmit?: EventHandler;
|
||||||
onSelect?: EventHandler;
|
onSelect?: EventHandler;
|
||||||
onToggle?: EventHandler;
|
onToggle?: EventHandler;
|
||||||
onConfirm?: EventHandler;
|
onConfirm?: EventHandler;
|
||||||
|
onPreview?: EventHandler;
|
||||||
onKeypress?: EventHandler;
|
onKeypress?: EventHandler;
|
||||||
onTouchend?: EventHandler;
|
onTouchend?: EventHandler;
|
||||||
onClickStep?: EventHandler;
|
onClickStep?: EventHandler;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user