perf: upload motion
parent
0464c84afc
commit
8a233d7c3a
|
@ -4,8 +4,8 @@ import './radio/style';
|
|||
// import './checkbox/style';
|
||||
// import './grid/style';
|
||||
// import './tag/style';
|
||||
import './rate/style';
|
||||
import './pagination/style';
|
||||
// import './rate/style';
|
||||
// import './pagination/style';
|
||||
// import './avatar/style';
|
||||
// import './badge/style';
|
||||
import './tabs/style';
|
||||
|
@ -29,14 +29,14 @@ import './auto-complete/style';
|
|||
// import './affix/style';
|
||||
import './cascader/style';
|
||||
// import './back-top/style';
|
||||
import './modal/style';
|
||||
// import './modal/style';
|
||||
// import './alert/style';
|
||||
import './time-picker/style';
|
||||
import './steps/style';
|
||||
// import './breadcrumb/style';
|
||||
import './calendar/style';
|
||||
import './date-picker/style';
|
||||
import './slider/style';
|
||||
// import './date-picker/style';
|
||||
// import './slider/style';
|
||||
import './table/style';
|
||||
// import './progress/style';
|
||||
import './timeline/style';
|
||||
|
@ -52,7 +52,7 @@ import './drawer/style';
|
|||
// import './skeleton/style';
|
||||
// import './comment/style';
|
||||
// import './config-provider/style';
|
||||
import './empty/style';
|
||||
// import './empty/style';
|
||||
// import './statistic/style';
|
||||
// import './result/style';
|
||||
// import './descriptions/style';
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
import type { UploadProps as RcUploadProps } from '../vc-upload';
|
||||
import VcUpload from '../vc-upload';
|
||||
import UploadList from './UploadList';
|
||||
import type {
|
||||
UploadType,
|
||||
UploadListType,
|
||||
UploadFile,
|
||||
UploadChangeParam,
|
||||
ShowUploadListInterface,
|
||||
FileType,
|
||||
} from './interface';
|
||||
import type { UploadFile, UploadChangeParam, ShowUploadListInterface, FileType } from './interface';
|
||||
import { uploadProps } from './interface';
|
||||
import { file2Obj, getFileItem, removeFileItem, updateFileList } from './utils';
|
||||
import { useLocaleReceiver } from '../locale-provider/LocaleReceiver';
|
||||
|
@ -25,6 +18,7 @@ import { useInjectFormItemContext } from '../form';
|
|||
|
||||
// CSSINJS
|
||||
import useStyle from './style';
|
||||
import { useInjectDisabled } from '../config-provider/DisabledContext';
|
||||
|
||||
export const LIST_IGNORE = `__LIST_IGNORE_${Date.now()}__`;
|
||||
|
||||
|
@ -33,18 +27,25 @@ export default defineComponent({
|
|||
name: 'AUpload',
|
||||
inheritAttrs: false,
|
||||
props: initDefaultProps(uploadProps(), {
|
||||
type: 'select' as UploadType,
|
||||
type: 'select',
|
||||
multiple: false,
|
||||
action: '',
|
||||
data: {},
|
||||
accept: '',
|
||||
showUploadList: true,
|
||||
listType: 'text' as UploadListType, // or picture
|
||||
listType: 'text', // or picture
|
||||
disabled: false,
|
||||
supportServerRender: true,
|
||||
}),
|
||||
setup(props, { slots, attrs, expose }) {
|
||||
const formItemContext = useInjectFormItemContext();
|
||||
const { prefixCls, direction, disabled } = useConfigInject('upload', props);
|
||||
|
||||
// style
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
const disabledContext = useInjectDisabled();
|
||||
const mergedDisabled = computed(() => disabledContext.value ?? disabled.value);
|
||||
|
||||
const [mergedFileList, setMergedFileList] = useMergedState(props.defaultFileList || [], {
|
||||
value: toRef(props, 'fileList'),
|
||||
postState: list => {
|
||||
|
@ -59,7 +60,7 @@ export default defineComponent({
|
|||
});
|
||||
const dragState = ref('drop');
|
||||
|
||||
const upload = ref();
|
||||
const upload = ref(null);
|
||||
onMounted(() => {
|
||||
devWarning(
|
||||
props.fileList !== undefined || attrs.value === undefined,
|
||||
|
@ -294,11 +295,6 @@ export default defineComponent({
|
|||
upload,
|
||||
});
|
||||
|
||||
const { prefixCls, direction } = useConfigInject('upload', props);
|
||||
|
||||
// style
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
|
||||
const [locale] = useLocaleReceiver(
|
||||
'Upload',
|
||||
defaultLocale.Upload,
|
||||
|
@ -312,7 +308,6 @@ export default defineComponent({
|
|||
previewFile,
|
||||
onPreview,
|
||||
onDownload,
|
||||
disabled,
|
||||
isImageUrl,
|
||||
progress,
|
||||
itemRender,
|
||||
|
@ -330,7 +325,7 @@ export default defineComponent({
|
|||
onPreview={onPreview}
|
||||
onDownload={onDownload}
|
||||
onRemove={handleRemove}
|
||||
showRemoveIcon={!disabled && showRemoveIcon}
|
||||
showRemoveIcon={!mergedDisabled.value && showRemoveIcon}
|
||||
showPreviewIcon={showPreviewIcon}
|
||||
showDownloadIcon={showDownloadIcon}
|
||||
removeIcon={removeIcon}
|
||||
|
@ -350,7 +345,7 @@ export default defineComponent({
|
|||
);
|
||||
};
|
||||
return () => {
|
||||
const { listType, disabled, type } = props;
|
||||
const { listType, type } = props;
|
||||
const { class: className, style: styleName, ...transAttrs } = attrs;
|
||||
const rcUploadProps = {
|
||||
onBatchStart,
|
||||
|
@ -363,6 +358,7 @@ export default defineComponent({
|
|||
prefixCls: prefixCls.value,
|
||||
beforeUpload: mergedBeforeUpload,
|
||||
onChange: undefined,
|
||||
disabled: mergedDisabled.value,
|
||||
};
|
||||
delete (rcUploadProps as any).remove;
|
||||
|
||||
|
@ -370,12 +366,12 @@ export default defineComponent({
|
|||
// !children: https://github.com/ant-design/ant-design/issues/14298
|
||||
// disabled: https://github.com/ant-design/ant-design/issues/16478
|
||||
// https://github.com/ant-design/ant-design/issues/24197
|
||||
if (!slots.default || disabled) {
|
||||
if (!slots.default || mergedDisabled.value) {
|
||||
delete rcUploadProps.id;
|
||||
}
|
||||
|
||||
const rtlCls = {
|
||||
[`${prefixCls}-rtl`]: direction.value === 'rtl',
|
||||
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
||||
};
|
||||
|
||||
if (type === 'drag') {
|
||||
|
@ -387,7 +383,7 @@ export default defineComponent({
|
|||
file => file.status === 'uploading',
|
||||
),
|
||||
[`${prefixCls.value}-drag-hover`]: dragState.value === 'dragover',
|
||||
[`${prefixCls.value}-disabled`]: disabled,
|
||||
[`${prefixCls.value}-disabled`]: mergedDisabled.value,
|
||||
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
||||
},
|
||||
attrs.class,
|
||||
|
@ -423,7 +419,7 @@ export default defineComponent({
|
|||
const uploadButtonCls = classNames(prefixCls.value, {
|
||||
[`${prefixCls.value}-select`]: true,
|
||||
[`${prefixCls.value}-select-${listType}`]: true,
|
||||
[`${prefixCls.value}-disabled`]: disabled,
|
||||
[`${prefixCls.value}-disabled`]: mergedDisabled.value,
|
||||
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
||||
});
|
||||
const children = flattenChildren(slots.default?.());
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { computed, defineComponent, onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import { computed, defineComponent, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||
import type { ExtractPropTypes, CSSProperties } from 'vue';
|
||||
import EyeOutlined from '@ant-design/icons-vue/EyeOutlined';
|
||||
import DeleteOutlined from '@ant-design/icons-vue/DeleteOutlined';
|
||||
|
@ -69,6 +69,15 @@ export default defineComponent({
|
|||
onBeforeUnmount(() => {
|
||||
clearTimeout(progressRafRef.value);
|
||||
});
|
||||
const mergedStatus = ref(props.file?.status);
|
||||
watch(
|
||||
() => props.file?.status,
|
||||
status => {
|
||||
if (status !== 'removed') {
|
||||
mergedStatus.value = status;
|
||||
}
|
||||
},
|
||||
);
|
||||
const { rootPrefixCls } = useConfigInject('upload', props);
|
||||
const transitionProps = computed(() => getTransitionProps(`${rootPrefixCls.value}-fade`));
|
||||
return () => {
|
||||
|
@ -100,10 +109,10 @@ export default defineComponent({
|
|||
const iconNode = iconRender({ file });
|
||||
let icon = <div class={`${prefixCls}-text-icon`}>{iconNode}</div>;
|
||||
if (listType === 'picture' || listType === 'picture-card') {
|
||||
if (file.status === 'uploading' || (!file.thumbUrl && !file.url)) {
|
||||
if (mergedStatus.value === 'uploading' || (!file.thumbUrl && !file.url)) {
|
||||
const uploadingClassName = {
|
||||
[`${prefixCls}-list-item-thumbnail`]: true,
|
||||
[`${prefixCls}-list-item-file`]: file.status !== 'uploading',
|
||||
[`${prefixCls}-list-item-file`]: mergedStatus.value !== 'uploading',
|
||||
};
|
||||
icon = <div class={uploadingClassName}>{iconNode}</div>;
|
||||
} else {
|
||||
|
@ -137,8 +146,7 @@ export default defineComponent({
|
|||
|
||||
const infoUploadingClass = {
|
||||
[`${prefixCls}-list-item`]: true,
|
||||
[`${prefixCls}-list-item-${file.status}`]: true,
|
||||
[`${prefixCls}-list-item-list-type-${listType}`]: true,
|
||||
[`${prefixCls}-list-item-${mergedStatus.value}`]: true,
|
||||
};
|
||||
const linkProps =
|
||||
typeof file.linkProps === 'string' ? JSON.parse(file.linkProps) : file.linkProps;
|
||||
|
@ -152,7 +160,7 @@ export default defineComponent({
|
|||
})
|
||||
: null;
|
||||
const downloadIcon =
|
||||
showDownloadIcon && file.status === 'done'
|
||||
showDownloadIcon && mergedStatus.value === 'done'
|
||||
? actionIconRender({
|
||||
customIcon: customDownloadIcon ? customDownloadIcon({ file }) : <DownloadOutlined />,
|
||||
callback: () => onDownload(file),
|
||||
|
@ -175,7 +183,7 @@ export default defineComponent({
|
|||
</span>
|
||||
);
|
||||
const listItemNameClass = `${prefixCls}-list-item-name`;
|
||||
const preview = file.url
|
||||
const fileName = file.url
|
||||
? [
|
||||
<a
|
||||
key="view"
|
||||
|
@ -219,29 +227,26 @@ export default defineComponent({
|
|||
</a>
|
||||
) : null;
|
||||
|
||||
const actions = listType === 'picture-card' && file.status !== 'uploading' && (
|
||||
<span class={`${prefixCls}-list-item-actions`}>
|
||||
{previewIcon}
|
||||
{file.status === 'done' && downloadIcon}
|
||||
{removeIcon}
|
||||
</span>
|
||||
);
|
||||
|
||||
let message;
|
||||
if (file.response && typeof file.response === 'string') {
|
||||
message = file.response;
|
||||
} else {
|
||||
message = file.error?.statusText || file.error?.message || locale.uploadError;
|
||||
}
|
||||
const pictureCardActions = listType === 'picture-card' &&
|
||||
mergedStatus.value !== 'uploading' && (
|
||||
<span class={`${prefixCls}-list-item-actions`}>
|
||||
{previewIcon}
|
||||
{mergedStatus.value === 'done' && downloadIcon}
|
||||
{removeIcon}
|
||||
</span>
|
||||
);
|
||||
|
||||
const dom = (
|
||||
<div class={infoUploadingClass}>
|
||||
{icon}
|
||||
{preview}
|
||||
{actions}
|
||||
{fileName}
|
||||
{pictureCardActions}
|
||||
{showProgress.value && (
|
||||
<Transition {...transitionProps.value}>
|
||||
<div v-show={file.status === 'uploading'} class={`${prefixCls}-list-item-progress`}>
|
||||
<div
|
||||
v-show={mergedStatus.value === 'uploading'}
|
||||
class={`${prefixCls}-list-item-progress`}
|
||||
>
|
||||
{'percent' in file ? (
|
||||
<Progress
|
||||
{...(progressProps as UploadListProgressProps)}
|
||||
|
@ -258,8 +263,12 @@ export default defineComponent({
|
|||
[`${prefixCls}-list-item-container`]: true,
|
||||
[`${className}`]: !!className,
|
||||
};
|
||||
const message =
|
||||
file.response && typeof file.response === 'string'
|
||||
? file.response
|
||||
: file.error?.statusText || file.error?.message || locale.uploadError;
|
||||
const item =
|
||||
file.status === 'error' ? (
|
||||
mergedStatus.value === 'error' ? (
|
||||
<Tooltip title={message} getPopupContainer={node => node.parentNode as HTMLElement}>
|
||||
{dom}
|
||||
</Tooltip>
|
||||
|
|
|
@ -2,7 +2,7 @@ import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined';
|
|||
import PaperClipOutlined from '@ant-design/icons-vue/PaperClipOutlined';
|
||||
import PictureTwoTone from '@ant-design/icons-vue/PictureTwoTone';
|
||||
import FileTwoTone from '@ant-design/icons-vue/FileTwoTone';
|
||||
import type { UploadListType, InternalUploadFile, UploadFile } from '../interface';
|
||||
import type { InternalUploadFile, UploadFile } from '../interface';
|
||||
import { uploadListProps } from '../interface';
|
||||
import { previewImage, isImageUrl } from '../utils';
|
||||
import type { ButtonProps } from '../../button';
|
||||
|
@ -24,7 +24,7 @@ export default defineComponent({
|
|||
compatConfig: { MODE: 3 },
|
||||
name: 'AUploadList',
|
||||
props: initDefaultProps(uploadListProps(), {
|
||||
listType: 'text' as UploadListType, // or picture
|
||||
listType: 'text', // or picture
|
||||
progress: {
|
||||
strokeWidth: 2,
|
||||
showInfo: false,
|
||||
|
@ -138,23 +138,33 @@ export default defineComponent({
|
|||
handleDownload: onInternalDownload,
|
||||
});
|
||||
|
||||
const { prefixCls, direction } = useConfigInject('upload', props);
|
||||
const { prefixCls, rootPrefixCls } = useConfigInject('upload', props);
|
||||
|
||||
const listClassNames = computed(() => ({
|
||||
[`${prefixCls.value}-list`]: true,
|
||||
[`${prefixCls.value}-list-${props.listType}`]: true,
|
||||
[`${prefixCls.value}-list-rtl`]: direction.value === 'rtl',
|
||||
}));
|
||||
const transitionGroupProps = computed(() => ({
|
||||
...collapseMotion(
|
||||
`${prefixCls.value}-${props.listType === 'picture-card' ? 'animate-inline' : 'animate'}`,
|
||||
),
|
||||
...getTransitionGroupProps(
|
||||
`${prefixCls.value}-${props.listType === 'picture-card' ? 'animate-inline' : 'animate'}`,
|
||||
),
|
||||
class: listClassNames.value,
|
||||
appear: motionAppear.value,
|
||||
}));
|
||||
const transitionGroupProps = computed(() => {
|
||||
const motion = {
|
||||
...collapseMotion(`${rootPrefixCls.value}-motion-collapse`),
|
||||
};
|
||||
delete motion.onAfterAppear;
|
||||
delete motion.onAfterEnter;
|
||||
delete motion.onAfterLeave;
|
||||
const motionConfig = {
|
||||
...getTransitionGroupProps(
|
||||
`${prefixCls.value}-${props.listType === 'picture-card' ? 'animate-inline' : 'animate'}`,
|
||||
),
|
||||
class: listClassNames.value,
|
||||
appear: motionAppear.value,
|
||||
};
|
||||
return props.listType !== 'picture-card'
|
||||
? {
|
||||
...motion,
|
||||
...motionConfig,
|
||||
}
|
||||
: motionConfig;
|
||||
});
|
||||
return () => {
|
||||
const {
|
||||
listType,
|
||||
|
|
|
@ -111,10 +111,7 @@ function uploadProps<T = any>() {
|
|||
>([Object, Function]),
|
||||
method: stringType<'POST' | 'PUT' | 'PATCH' | 'post' | 'put' | 'patch'>(),
|
||||
headers: objectType<HttpRequestHeader>(),
|
||||
showUploadList: someType<boolean | ShowUploadListInterface>(
|
||||
[Boolean, Object],
|
||||
undefined as boolean | ShowUploadListInterface,
|
||||
),
|
||||
showUploadList: someType<boolean | ShowUploadListInterface>([Boolean, Object]),
|
||||
multiple: booleanType(),
|
||||
accept: String,
|
||||
beforeUpload:
|
||||
|
@ -141,7 +138,7 @@ function uploadProps<T = any>() {
|
|||
customRequest: functionType<(options: RcCustomRequestOptions) => void>(),
|
||||
withCredentials: booleanType(),
|
||||
openFileDialogOnClick: booleanType(),
|
||||
locale: objectType<UploadLocale>(undefined as UploadLocale),
|
||||
locale: objectType<UploadLocale>(),
|
||||
id: String,
|
||||
previewFile: functionType<PreviewFileHandler>(),
|
||||
/** @deprecated Please use `beforeUpload` directly */
|
||||
|
|
|
@ -110,6 +110,15 @@ export function previewImage(file: File | Blob): Promise<string> {
|
|||
|
||||
resolve(dataURL);
|
||||
};
|
||||
img.src = window.URL.createObjectURL(file);
|
||||
img.crossOrigin = 'anonymous';
|
||||
if (file.type.startsWith('image/svg+xml')) {
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('load', () => {
|
||||
if (reader.result) img.src = reader.result as string;
|
||||
});
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
img.src = window.URL.createObjectURL(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue