refactor: vc-upload
parent
b0c3677d50
commit
67f5226cdc
|
@ -965,6 +965,10 @@
|
|||
@typography-title-margin-top: 1.2em;
|
||||
@typography-title-margin-bottom: 0.5em;
|
||||
|
||||
// Upload
|
||||
// ---
|
||||
@upload-actions-color: @text-color-secondary;
|
||||
|
||||
// Image
|
||||
// ---
|
||||
@image-size-base: 48px;
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import { defineComponent } from 'vue';
|
||||
import Upload from './Upload';
|
||||
import { uploadProps } from './interface';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AUploadDragger',
|
||||
inheritAttrs: false,
|
||||
props: uploadProps(),
|
||||
setup(props, { slots, attrs }) {
|
||||
return () => {
|
||||
const { height, ...restProps } = props;
|
||||
const { style, ...restAttrs } = attrs;
|
||||
const draggerProps = {
|
||||
...restProps,
|
||||
...restAttrs,
|
||||
type: 'drag',
|
||||
style: { ...(style as any), height: typeof height === 'number' ? `${height}px` : height },
|
||||
} as any;
|
||||
return <Upload {...draggerProps} v-slots={slots}></Upload>;
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,342 @@
|
|||
import classNames from '../_util/classNames';
|
||||
import uniqBy from 'lodash-es/uniqBy';
|
||||
import findIndex from 'lodash-es/findIndex';
|
||||
import VcUpload from '../vc-upload';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import { getOptionProps, hasProp, getSlot } from '../_util/props-util';
|
||||
import initDefaultProps from '../_util/props-util/initDefaultProps';
|
||||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
import defaultLocale from '../locale-provider/default';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
import Dragger from './Dragger';
|
||||
import UploadList from './UploadList';
|
||||
import type { UploadFile } from './interface';
|
||||
import { uploadProps } from './interface';
|
||||
import { T, fileToObject, genPercentAdd, getFileItem, removeFileItem } from './utils';
|
||||
import { defineComponent, inject } from 'vue';
|
||||
import { getDataAndAriaProps } from '../_util/util';
|
||||
import { useInjectFormItemContext } from '../form/FormItemContext';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AUpload',
|
||||
mixins: [BaseMixin],
|
||||
inheritAttrs: false,
|
||||
Dragger,
|
||||
props: initDefaultProps(uploadProps, {
|
||||
type: 'select',
|
||||
multiple: false,
|
||||
action: '',
|
||||
data: {},
|
||||
accept: '',
|
||||
beforeUpload: T,
|
||||
showUploadList: true,
|
||||
listType: 'text', // or pictrue
|
||||
disabled: false,
|
||||
supportServerRender: true,
|
||||
}),
|
||||
setup() {
|
||||
const formItemContext = useInjectFormItemContext();
|
||||
return {
|
||||
upload: null,
|
||||
progressTimer: null,
|
||||
configProvider: inject('configProvider', defaultConfigProvider),
|
||||
formItemContext,
|
||||
};
|
||||
},
|
||||
// recentUploadStatus: boolean | PromiseLike<any>;
|
||||
data() {
|
||||
return {
|
||||
sFileList: this.fileList || this.defaultFileList || [],
|
||||
dragState: 'drop',
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
fileList(val) {
|
||||
this.sFileList = val || [];
|
||||
},
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.clearProgressTimer();
|
||||
},
|
||||
methods: {
|
||||
onStart(file) {
|
||||
const targetItem = fileToObject(file);
|
||||
targetItem.status = 'uploading';
|
||||
const nextFileList = this.sFileList.concat();
|
||||
const fileIndex = findIndex(nextFileList, ({ uid }) => uid === targetItem.uid);
|
||||
if (fileIndex === -1) {
|
||||
nextFileList.push(targetItem);
|
||||
} else {
|
||||
nextFileList[fileIndex] = targetItem;
|
||||
}
|
||||
this.handleChange({
|
||||
file: targetItem,
|
||||
fileList: nextFileList,
|
||||
});
|
||||
// fix ie progress
|
||||
if (!window.File || (typeof process === 'object' && process.env.TEST_IE)) {
|
||||
this.autoUpdateProgress(0, targetItem);
|
||||
}
|
||||
},
|
||||
|
||||
onSuccess(response, file, xhr) {
|
||||
this.clearProgressTimer();
|
||||
try {
|
||||
if (typeof response === 'string') {
|
||||
response = JSON.parse(response);
|
||||
}
|
||||
} catch (e) {
|
||||
/* do nothing */
|
||||
}
|
||||
const fileList = this.sFileList;
|
||||
const targetItem = getFileItem(file, fileList);
|
||||
// removed
|
||||
if (!targetItem) {
|
||||
return;
|
||||
}
|
||||
targetItem.status = 'done';
|
||||
targetItem.response = response;
|
||||
targetItem.xhr = xhr;
|
||||
this.handleChange({
|
||||
file: { ...targetItem },
|
||||
fileList,
|
||||
});
|
||||
},
|
||||
onProgress(e, file) {
|
||||
const fileList = this.sFileList;
|
||||
const targetItem = getFileItem(file, fileList);
|
||||
// removed
|
||||
if (!targetItem) {
|
||||
return;
|
||||
}
|
||||
targetItem.percent = e.percent;
|
||||
this.handleChange({
|
||||
event: e,
|
||||
file: { ...targetItem },
|
||||
fileList: this.sFileList,
|
||||
});
|
||||
},
|
||||
onError(error, response, file) {
|
||||
this.clearProgressTimer();
|
||||
const fileList = this.sFileList;
|
||||
const targetItem = getFileItem(file, fileList);
|
||||
// removed
|
||||
if (!targetItem) {
|
||||
return;
|
||||
}
|
||||
targetItem.error = error;
|
||||
targetItem.response = response;
|
||||
targetItem.status = 'error';
|
||||
this.handleChange({
|
||||
file: { ...targetItem },
|
||||
fileList,
|
||||
});
|
||||
},
|
||||
onReject(fileList) {
|
||||
this.$emit('reject', fileList);
|
||||
},
|
||||
handleRemove(file) {
|
||||
const { remove: onRemove } = this;
|
||||
const { sFileList: fileList } = this.$data;
|
||||
|
||||
Promise.resolve(typeof onRemove === 'function' ? onRemove(file) : onRemove).then(ret => {
|
||||
// Prevent removing file
|
||||
if (ret === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
const removedFileList = removeFileItem(file, fileList);
|
||||
|
||||
if (removedFileList) {
|
||||
file.status = 'removed'; // eslint-disable-line
|
||||
|
||||
if (this.upload) {
|
||||
this.upload.abort(file);
|
||||
}
|
||||
|
||||
this.handleChange({
|
||||
file,
|
||||
fileList: removedFileList,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
handleManualRemove(file) {
|
||||
if (this.$refs.uploadRef) {
|
||||
(this.$refs.uploadRef as any).abort(file);
|
||||
}
|
||||
this.handleRemove(file);
|
||||
},
|
||||
handleChange(info) {
|
||||
if (!hasProp(this, 'fileList')) {
|
||||
this.setState({ sFileList: info.fileList });
|
||||
}
|
||||
this.$emit('update:fileList', info.fileList);
|
||||
this.$emit('change', info);
|
||||
this.formItemContext.onFieldChange();
|
||||
},
|
||||
onFileDrop(e) {
|
||||
this.setState({
|
||||
dragState: e.type,
|
||||
});
|
||||
},
|
||||
reBeforeUpload(file, fileList) {
|
||||
const { beforeUpload } = this.$props;
|
||||
const { sFileList: stateFileList } = this.$data;
|
||||
if (!beforeUpload) {
|
||||
return true;
|
||||
}
|
||||
const result = beforeUpload(file, fileList);
|
||||
if (result === false) {
|
||||
this.handleChange({
|
||||
file,
|
||||
fileList: uniqBy(
|
||||
stateFileList.concat(fileList.map(fileToObject)),
|
||||
(item: UploadFile) => item.uid,
|
||||
),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if (result && result.then) {
|
||||
return result;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
clearProgressTimer() {
|
||||
clearInterval(this.progressTimer);
|
||||
},
|
||||
autoUpdateProgress(_, file) {
|
||||
const getPercent = genPercentAdd();
|
||||
let curPercent = 0;
|
||||
this.clearProgressTimer();
|
||||
this.progressTimer = setInterval(() => {
|
||||
curPercent = getPercent(curPercent);
|
||||
this.onProgress(
|
||||
{
|
||||
percent: curPercent * 100,
|
||||
},
|
||||
file,
|
||||
);
|
||||
}, 200);
|
||||
},
|
||||
renderUploadList(locale) {
|
||||
const {
|
||||
showUploadList = {},
|
||||
listType,
|
||||
previewFile,
|
||||
disabled,
|
||||
locale: propLocale,
|
||||
} = getOptionProps(this);
|
||||
const { showRemoveIcon, showPreviewIcon, showDownloadIcon } = showUploadList;
|
||||
const { sFileList: fileList } = this.$data;
|
||||
const { onDownload, onPreview } = this.$props;
|
||||
const uploadListProps = {
|
||||
listType,
|
||||
items: fileList,
|
||||
previewFile,
|
||||
showRemoveIcon: !disabled && showRemoveIcon,
|
||||
showPreviewIcon,
|
||||
showDownloadIcon,
|
||||
locale: { ...locale, ...propLocale },
|
||||
onRemove: this.handleManualRemove,
|
||||
onDownload,
|
||||
onPreview,
|
||||
};
|
||||
return <UploadList {...uploadListProps} />;
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
showUploadList,
|
||||
listType,
|
||||
type,
|
||||
disabled,
|
||||
} = getOptionProps(this);
|
||||
const { sFileList: fileList, dragState } = this.$data;
|
||||
const { class: className, style } = this.$attrs;
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('upload', customizePrefixCls);
|
||||
|
||||
const vcUploadProps = {
|
||||
...this.$props,
|
||||
id: this.$props.id ?? this.formItemContext.id.value,
|
||||
prefixCls,
|
||||
beforeUpload: this.reBeforeUpload,
|
||||
onStart: this.onStart,
|
||||
onError: this.onError,
|
||||
onProgress: this.onProgress,
|
||||
onSuccess: this.onSuccess,
|
||||
onReject: this.onReject,
|
||||
ref: 'uploadRef',
|
||||
};
|
||||
|
||||
const uploadList = showUploadList ? (
|
||||
<LocaleReceiver
|
||||
componentName="Upload"
|
||||
defaultLocale={defaultLocale.Upload}
|
||||
children={this.renderUploadList}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
const children = getSlot(this);
|
||||
|
||||
if (type === 'drag') {
|
||||
const dragCls = classNames(prefixCls, {
|
||||
[`${prefixCls}-drag`]: true,
|
||||
[`${prefixCls}-drag-uploading`]: fileList.some((file: any) => file.status === 'uploading'),
|
||||
[`${prefixCls}-drag-hover`]: dragState === 'dragover',
|
||||
[`${prefixCls}-disabled`]: disabled,
|
||||
});
|
||||
return (
|
||||
<span class={className} {...getDataAndAriaProps(this.$attrs)}>
|
||||
<div
|
||||
class={dragCls}
|
||||
onDrop={this.onFileDrop}
|
||||
onDragover={this.onFileDrop}
|
||||
onDragleave={this.onFileDrop}
|
||||
style={style}
|
||||
>
|
||||
<VcUpload {...vcUploadProps} class={`${prefixCls}-btn`}>
|
||||
<div class={`${prefixCls}-drag-container`}>{children}</div>
|
||||
</VcUpload>
|
||||
</div>
|
||||
{uploadList}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
const uploadButtonCls = classNames(prefixCls, {
|
||||
[`${prefixCls}-select`]: true,
|
||||
[`${prefixCls}-select-${listType}`]: true,
|
||||
[`${prefixCls}-disabled`]: disabled,
|
||||
});
|
||||
|
||||
// Remove id to avoid open by label when trigger is hidden
|
||||
// https://github.com/ant-design/ant-design/issues/14298
|
||||
if (!children.length || disabled) {
|
||||
delete vcUploadProps.id;
|
||||
}
|
||||
|
||||
const uploadButton = (
|
||||
<div class={uploadButtonCls} style={children.length ? undefined : { display: 'none' }}>
|
||||
<VcUpload {...vcUploadProps}>{children}</VcUpload>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (listType === 'picture-card') {
|
||||
return (
|
||||
<span class={classNames(`${prefixCls}-picture-card-wrapper`, className)}>
|
||||
{uploadList}
|
||||
{uploadButton}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span class={className}>
|
||||
{uploadButton}
|
||||
{uploadList}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
});
|
|
@ -0,0 +1,287 @@
|
|||
import { computed, defineComponent, onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import type { ExtractPropTypes, PropType, CSSProperties } from 'vue';
|
||||
import EyeOutlined from '@ant-design/icons-vue/EyeOutlined';
|
||||
import DeleteOutlined from '@ant-design/icons-vue/DeleteOutlined';
|
||||
import DownloadOutlined from '@ant-design/icons-vue/DownloadOutlined';
|
||||
import Tooltip from '../../tooltip';
|
||||
import Progress from '../../progress';
|
||||
|
||||
import type {
|
||||
ItemRender,
|
||||
UploadFile,
|
||||
UploadListProgressProps,
|
||||
UploadListType,
|
||||
UploadLocale,
|
||||
} from '../interface';
|
||||
import type { VueNode } from '../../_util/type';
|
||||
import useConfigInject from '../../_util/hooks/useConfigInject';
|
||||
import Transition, { getTransitionProps } from '../../_util/transition';
|
||||
export const listItemProps = () => {
|
||||
return {
|
||||
prefixCls: String,
|
||||
locale: { type: Object as PropType<UploadLocale>, default: undefined as UploadLocale },
|
||||
file: Object as PropType<UploadFile>,
|
||||
items: Array as PropType<UploadFile[]>,
|
||||
listType: String as PropType<UploadListType>,
|
||||
isImgUrl: Function as PropType<(file: UploadFile) => boolean>,
|
||||
|
||||
showRemoveIcon: Boolean,
|
||||
showDownloadIcon: Boolean,
|
||||
showPreviewIcon: Boolean,
|
||||
removeIcon: Function as PropType<(opt: { file: UploadFile }) => VueNode>,
|
||||
downloadIcon: Function as PropType<(opt: { file: UploadFile }) => VueNode>,
|
||||
previewIcon: Function as PropType<(opt: { file: UploadFile }) => VueNode>,
|
||||
|
||||
iconRender: Function as PropType<(opt: { file: UploadFile }) => VueNode>,
|
||||
actionIconRender: Function as PropType<
|
||||
(opt: {
|
||||
customIcon: VueNode;
|
||||
callback: () => void;
|
||||
prefixCls: string;
|
||||
title?: string | undefined;
|
||||
}) => VueNode
|
||||
>,
|
||||
itemRender: Function as PropType<ItemRender>,
|
||||
onPreview: Function as PropType<(file: UploadFile, e: Event) => void>,
|
||||
onClose: Function as PropType<(file: UploadFile) => void>,
|
||||
onDownload: Function as PropType<(file: UploadFile) => void>,
|
||||
progress: Object as PropType<UploadListProgressProps>,
|
||||
};
|
||||
};
|
||||
|
||||
export type ListItemProps = Partial<ExtractPropTypes<ReturnType<typeof listItemProps>>>;
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ListItem',
|
||||
inheritAttrs: false,
|
||||
props: listItemProps(),
|
||||
setup(props, { slots, attrs }) {
|
||||
const showProgress = ref(false);
|
||||
const progressRafRef = ref();
|
||||
onMounted(() => {
|
||||
progressRafRef.value = setTimeout(() => {
|
||||
showProgress.value = true;
|
||||
}, 300);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
clearTimeout(progressRafRef.value);
|
||||
});
|
||||
const { rootPrefixCls } = useConfigInject('upload', props);
|
||||
const transitionProps = computed(() => getTransitionProps(`${rootPrefixCls.value}-fade`));
|
||||
return () => {
|
||||
const {
|
||||
prefixCls,
|
||||
locale,
|
||||
listType,
|
||||
file,
|
||||
items,
|
||||
progress: progressProps,
|
||||
iconRender,
|
||||
actionIconRender,
|
||||
itemRender = slots.itemRender,
|
||||
isImgUrl,
|
||||
showPreviewIcon,
|
||||
showRemoveIcon,
|
||||
showDownloadIcon,
|
||||
previewIcon: customPreviewIcon = slots.previewIcon,
|
||||
removeIcon: customRemoveIcon = slots.removeIcon,
|
||||
downloadIcon: customDownloadIcon = slots.downloadIcon,
|
||||
onPreview,
|
||||
onDownload,
|
||||
onClose,
|
||||
} = props;
|
||||
const { class: className, style } = attrs;
|
||||
// This is used for legacy span make scrollHeight the wrong value.
|
||||
// We will force these to be `display: block` with non `picture-card`
|
||||
const spanClassName = `${prefixCls}-span`;
|
||||
|
||||
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)) {
|
||||
const uploadingClassName = {
|
||||
[`${prefixCls}-list-item-thumbnail`]: true,
|
||||
[`${prefixCls}-list-item-file`]: file.status !== 'uploading',
|
||||
};
|
||||
icon = <div class={uploadingClassName}>{iconNode}</div>;
|
||||
} else {
|
||||
const thumbnail = isImgUrl?.(file) ? (
|
||||
<img
|
||||
src={file.thumbUrl || file.url}
|
||||
alt={file.name}
|
||||
class={`${prefixCls}-list-item-image`}
|
||||
/>
|
||||
) : (
|
||||
iconNode
|
||||
);
|
||||
const aClassName = {
|
||||
[`${prefixCls}-list-item-thumbnail`]: true,
|
||||
[`${prefixCls}-list-item-file`]: isImgUrl && !isImgUrl(file),
|
||||
};
|
||||
icon = (
|
||||
<a
|
||||
class={aClassName}
|
||||
onClick={e => onPreview(file, e)}
|
||||
href={file.url || file.thumbUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{thumbnail}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const infoUploadingClass = {
|
||||
[`${prefixCls}-list-item`]: true,
|
||||
[`${prefixCls}-list-item-${file.status}`]: true,
|
||||
[`${prefixCls}-list-item-list-type-${listType}`]: true,
|
||||
};
|
||||
const linkProps =
|
||||
typeof file.linkProps === 'string' ? JSON.parse(file.linkProps) : file.linkProps;
|
||||
|
||||
const removeIcon = showRemoveIcon
|
||||
? actionIconRender({
|
||||
customIcon: customRemoveIcon ? customRemoveIcon({ file }) : <DeleteOutlined />,
|
||||
callback: () => onClose(file),
|
||||
prefixCls,
|
||||
title: locale.removeFile,
|
||||
})
|
||||
: null;
|
||||
|
||||
const downloadIcon =
|
||||
showDownloadIcon && file.status === 'done'
|
||||
? actionIconRender({
|
||||
customIcon: customDownloadIcon ? customDownloadIcon({ file }) : <DownloadOutlined />,
|
||||
callback: () => onDownload(file),
|
||||
prefixCls,
|
||||
title: locale.downloadFile,
|
||||
})
|
||||
: null;
|
||||
const downloadOrDelete = listType !== 'picture-card' && (
|
||||
<span
|
||||
key="download-delete"
|
||||
class={[
|
||||
`${prefixCls}-list-item-card-actions`,
|
||||
{
|
||||
picture: listType === 'picture',
|
||||
},
|
||||
]}
|
||||
>
|
||||
{downloadIcon}
|
||||
{removeIcon}
|
||||
</span>
|
||||
);
|
||||
const listItemNameClass = `${prefixCls}-list-item-name`;
|
||||
const preview = file.url
|
||||
? [
|
||||
<a
|
||||
key="view"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class={listItemNameClass}
|
||||
title={file.name}
|
||||
{...linkProps}
|
||||
href={file.url}
|
||||
onClick={e => onPreview(file, e)}
|
||||
>
|
||||
{file.name}
|
||||
</a>,
|
||||
downloadOrDelete,
|
||||
]
|
||||
: [
|
||||
<span
|
||||
key="view"
|
||||
class={listItemNameClass}
|
||||
onClick={e => onPreview(file, e)}
|
||||
title={file.name}
|
||||
>
|
||||
{file.name}
|
||||
</span>,
|
||||
downloadOrDelete,
|
||||
];
|
||||
const previewStyle: CSSProperties = {
|
||||
pointerEvents: 'none',
|
||||
opacity: 0.5,
|
||||
};
|
||||
const previewIcon = showPreviewIcon ? (
|
||||
<a
|
||||
href={file.url || file.thumbUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={file.url || file.thumbUrl ? undefined : previewStyle}
|
||||
onClick={e => onPreview(file, e)}
|
||||
title={locale.previewFile}
|
||||
>
|
||||
{customPreviewIcon ? customPreviewIcon({ file }) : <EyeOutlined />}
|
||||
</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 iconAndPreview = (
|
||||
<span class={spanClassName}>
|
||||
{icon}
|
||||
{preview}
|
||||
</span>
|
||||
);
|
||||
|
||||
const dom = (
|
||||
<div class={infoUploadingClass}>
|
||||
<div class={`${prefixCls}-list-item-info`}>{iconAndPreview}</div>
|
||||
{actions}
|
||||
{showProgress.value && (
|
||||
<Transition {...transitionProps.value}>
|
||||
<div v-show={file.status === 'uploading'} class={`${prefixCls}-list-item-progress`}>
|
||||
{'percent' in file ? (
|
||||
<Progress {...progressProps} type="line" percent={file.percent} />
|
||||
) : null}
|
||||
</div>
|
||||
</Transition>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
const listContainerNameClass = {
|
||||
[`${prefixCls}-list-${listType}-container`]: true,
|
||||
[`${className}`]: !!className,
|
||||
};
|
||||
const item =
|
||||
file.status === 'error' ? (
|
||||
<Tooltip title={message} getPopupContainer={node => node.parentNode as HTMLElement}>
|
||||
{dom}
|
||||
</Tooltip>
|
||||
) : (
|
||||
dom
|
||||
);
|
||||
|
||||
return (
|
||||
<div class={listContainerNameClass} style={style} ref={ref}>
|
||||
{itemRender
|
||||
? itemRender({
|
||||
originNode: item,
|
||||
file,
|
||||
fileList: items,
|
||||
actions: {
|
||||
download: onDownload.bind(null, file),
|
||||
preview: onPreview.bind(null, file),
|
||||
remove: onClose.bind(null, file),
|
||||
},
|
||||
})
|
||||
: item}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,261 @@
|
|||
import * as React from 'react';
|
||||
import CSSMotion, { CSSMotionList, CSSMotionListProps } from 'rc-motion';
|
||||
import classNames from 'classnames';
|
||||
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
||||
import PaperClipOutlined from '@ant-design/icons/PaperClipOutlined';
|
||||
import PictureTwoTone from '@ant-design/icons/PictureTwoTone';
|
||||
import FileTwoTone from '@ant-design/icons/FileTwoTone';
|
||||
import { cloneElement, isValidElement } from '../../_util/reactNode';
|
||||
import { UploadListProps, UploadFile, UploadListType, InternalUploadFile } from '../interface';
|
||||
import { previewImage, isImageUrl } from '../utils';
|
||||
import collapseMotion from '../../_util/motion';
|
||||
import { ConfigContext } from '../../config-provider';
|
||||
import Button, { ButtonProps } from '../../button';
|
||||
import useForceUpdate from '../../_util/hooks/useForceUpdate';
|
||||
import ListItem from './ListItem';
|
||||
|
||||
const listItemMotion: Partial<CSSMotionListProps> = {
|
||||
...collapseMotion,
|
||||
};
|
||||
|
||||
delete listItemMotion.onAppearEnd;
|
||||
delete listItemMotion.onEnterEnd;
|
||||
delete listItemMotion.onLeaveEnd;
|
||||
|
||||
const InternalUploadList: React.ForwardRefRenderFunction<unknown, UploadListProps> = (
|
||||
{
|
||||
listType,
|
||||
previewFile,
|
||||
onPreview,
|
||||
onDownload,
|
||||
onRemove,
|
||||
locale,
|
||||
iconRender,
|
||||
isImageUrl: isImgUrl,
|
||||
prefixCls: customizePrefixCls,
|
||||
items = [],
|
||||
showPreviewIcon,
|
||||
showRemoveIcon,
|
||||
showDownloadIcon,
|
||||
removeIcon,
|
||||
previewIcon,
|
||||
downloadIcon,
|
||||
progress,
|
||||
appendAction,
|
||||
itemRender,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const forceUpdate = useForceUpdate();
|
||||
const [motionAppear, setMotionAppear] = React.useState(false);
|
||||
|
||||
// ============================= Effect =============================
|
||||
React.useEffect(() => {
|
||||
if (listType !== 'picture' && listType !== 'picture-card') {
|
||||
return;
|
||||
}
|
||||
(items || []).forEach((file: InternalUploadFile) => {
|
||||
if (
|
||||
typeof document === 'undefined' ||
|
||||
typeof window === 'undefined' ||
|
||||
!(window as any).FileReader ||
|
||||
!(window as any).File ||
|
||||
!(file.originFileObj instanceof File || (file.originFileObj as Blob) instanceof Blob) ||
|
||||
file.thumbUrl !== undefined
|
||||
) {
|
||||
return;
|
||||
}
|
||||
file.thumbUrl = '';
|
||||
if (previewFile) {
|
||||
previewFile(file.originFileObj as File).then((previewDataUrl: string) => {
|
||||
// Need append '' to avoid dead loop
|
||||
file.thumbUrl = previewDataUrl || '';
|
||||
forceUpdate();
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [listType, items, previewFile]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setMotionAppear(true);
|
||||
}, []);
|
||||
|
||||
// ============================= Events =============================
|
||||
const onInternalPreview = (file: UploadFile, e?: React.SyntheticEvent<HTMLElement>) => {
|
||||
if (!onPreview) {
|
||||
return;
|
||||
}
|
||||
e?.preventDefault();
|
||||
return onPreview(file);
|
||||
};
|
||||
|
||||
const onInternalDownload = (file: UploadFile) => {
|
||||
if (typeof onDownload === 'function') {
|
||||
onDownload(file);
|
||||
} else if (file.url) {
|
||||
window.open(file.url);
|
||||
}
|
||||
};
|
||||
|
||||
const onInternalClose = (file: UploadFile) => {
|
||||
onRemove?.(file);
|
||||
};
|
||||
|
||||
const internalIconRender = (file: UploadFile) => {
|
||||
if (iconRender) {
|
||||
return iconRender(file, listType);
|
||||
}
|
||||
const isLoading = file.status === 'uploading';
|
||||
const fileIcon = isImgUrl && isImgUrl(file) ? <PictureTwoTone /> : <FileTwoTone />;
|
||||
let icon: React.ReactNode = isLoading ? <LoadingOutlined /> : <PaperClipOutlined />;
|
||||
if (listType === 'picture') {
|
||||
icon = isLoading ? <LoadingOutlined /> : fileIcon;
|
||||
} else if (listType === 'picture-card') {
|
||||
icon = isLoading ? locale.uploading : fileIcon;
|
||||
}
|
||||
return icon;
|
||||
};
|
||||
|
||||
const actionIconRender = (
|
||||
customIcon: React.ReactNode,
|
||||
callback: () => void,
|
||||
prefixCls: string,
|
||||
title?: string,
|
||||
) => {
|
||||
const btnProps: ButtonProps = {
|
||||
type: 'text',
|
||||
size: 'small',
|
||||
title,
|
||||
onClick: (e: React.MouseEvent<HTMLElement>) => {
|
||||
callback();
|
||||
if (isValidElement(customIcon) && customIcon.props.onClick) {
|
||||
customIcon.props.onClick(e);
|
||||
}
|
||||
},
|
||||
className: `${prefixCls}-list-item-card-actions-btn`,
|
||||
};
|
||||
if (isValidElement(customIcon)) {
|
||||
const btnIcon = cloneElement(customIcon, {
|
||||
...customIcon.props,
|
||||
onClick: () => {},
|
||||
});
|
||||
|
||||
return <Button {...btnProps} icon={btnIcon} />;
|
||||
}
|
||||
return (
|
||||
<Button {...btnProps}>
|
||||
<span>{customIcon}</span>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
// ============================== Ref ===============================
|
||||
// Test needs
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
handlePreview: onInternalPreview,
|
||||
handleDownload: onInternalDownload,
|
||||
}));
|
||||
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
|
||||
// ============================= Render =============================
|
||||
const prefixCls = getPrefixCls('upload', customizePrefixCls);
|
||||
|
||||
const listClassNames = classNames({
|
||||
[`${prefixCls}-list`]: true,
|
||||
[`${prefixCls}-list-${listType}`]: true,
|
||||
[`${prefixCls}-list-rtl`]: direction === 'rtl',
|
||||
});
|
||||
|
||||
// >>> Motion config
|
||||
const motionKeyList = [
|
||||
...items.map(file => ({
|
||||
key: file.uid,
|
||||
file,
|
||||
})),
|
||||
];
|
||||
|
||||
const animationDirection = listType === 'picture-card' ? 'animate-inline' : 'animate';
|
||||
// const transitionName = list.length === 0 ? '' : `${prefixCls}-${animationDirection}`;
|
||||
|
||||
let motionConfig: Omit<CSSMotionListProps, 'onVisibleChanged'> = {
|
||||
motionDeadline: 2000,
|
||||
motionName: `${prefixCls}-${animationDirection}`,
|
||||
keys: motionKeyList,
|
||||
motionAppear,
|
||||
};
|
||||
|
||||
if (listType !== 'picture-card') {
|
||||
motionConfig = {
|
||||
...listItemMotion,
|
||||
...motionConfig,
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={listClassNames}>
|
||||
<CSSMotionList {...motionConfig} component={false}>
|
||||
{({ key, file, className: motionClassName, style: motionStyle }) => (
|
||||
<ListItem
|
||||
key={key}
|
||||
locale={locale}
|
||||
prefixCls={prefixCls}
|
||||
className={motionClassName}
|
||||
style={motionStyle}
|
||||
file={file}
|
||||
items={items}
|
||||
progress={progress}
|
||||
listType={listType}
|
||||
isImgUrl={isImgUrl}
|
||||
showPreviewIcon={showPreviewIcon}
|
||||
showRemoveIcon={showRemoveIcon}
|
||||
showDownloadIcon={showDownloadIcon}
|
||||
removeIcon={removeIcon}
|
||||
previewIcon={previewIcon}
|
||||
downloadIcon={downloadIcon}
|
||||
iconRender={internalIconRender}
|
||||
actionIconRender={actionIconRender}
|
||||
itemRender={itemRender}
|
||||
onPreview={onInternalPreview}
|
||||
onDownload={onInternalDownload}
|
||||
onClose={onInternalClose}
|
||||
/>
|
||||
)}
|
||||
</CSSMotionList>
|
||||
|
||||
{/* Append action */}
|
||||
{appendAction && (
|
||||
<CSSMotion {...motionConfig}>
|
||||
{({ className: motionClassName, style: motionStyle }) =>
|
||||
cloneElement(appendAction, oriProps => ({
|
||||
className: classNames(oriProps.className, motionClassName),
|
||||
style: {
|
||||
...motionStyle,
|
||||
...oriProps.style,
|
||||
},
|
||||
}))
|
||||
}
|
||||
</CSSMotion>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const UploadList = React.forwardRef<unknown, UploadListProps>(InternalUploadList);
|
||||
|
||||
UploadList.displayName = 'UploadList';
|
||||
|
||||
UploadList.defaultProps = {
|
||||
listType: 'text' as UploadListType, // or picture
|
||||
progress: {
|
||||
strokeWidth: 2,
|
||||
showInfo: false,
|
||||
},
|
||||
showRemoveIcon: true,
|
||||
showDownloadIcon: false,
|
||||
showPreviewIcon: true,
|
||||
previewFile: previewImage,
|
||||
isImageUrl,
|
||||
};
|
||||
|
||||
export default UploadList;
|
|
@ -0,0 +1,126 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders ./components/upload/demo/avatar.vue correctly 1`] = `
|
||||
<span class="ant-upload-picture-card-wrapper avatar-uploader"><!----><div class="ant-upload ant-upload-select ant-upload-select-picture-card"><span role="button" tabindex="0" class="ant-upload"><input type="file" style="display: none;" accept=""><div><span role="img" aria-label="plus" class="anticon anticon-plus"><svg focusable="false" class="" data-icon="plus" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><defs><style></style></defs><path d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"></path><path d="M176 474h672q8 0 8 8v60q0 8-8 8H176q-8 0-8-8v-60q0-8 8-8z"></path></svg></span>
|
||||
<div class="ant-upload-text">Upload</div>
|
||||
</div></span></div></span>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/upload/demo/basic.vue correctly 1`] = `
|
||||
<span><div class="ant-upload ant-upload-select ant-upload-select-text"><span role="button" tabindex="0" class="ant-upload"><input type="file" style="display: none;" accept="" multiple=""><button class="ant-btn" type="button"><!----><span role="img" aria-label="upload" class="anticon anticon-upload"><svg focusable="false" class="" data-icon="upload" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 00-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"></path></svg></span><span>Click to Upload</span></button></span></div>
|
||||
<div class="ant-upload-list ant-upload-list-text"></div></span>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/upload/demo/defaultFileList.vue correctly 1`] = `
|
||||
<span><div class="ant-upload ant-upload-select ant-upload-select-text"><span role="button" tabindex="0" class="ant-upload"><input type="file" style="display: none;" accept=""><button class="ant-btn" type="button"><!----><span role="img" aria-label="upload" class="anticon anticon-upload"><svg focusable="false" class="" data-icon="upload" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 00-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"></path></svg></span><span>Upload</span></button></span></div>
|
||||
<div class="ant-upload-list ant-upload-list-text">
|
||||
<div class=""><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-text"><div class="ant-upload-list-item-info"><span><span role="img" aria-label="paper-clip" class="anticon anticon-paper-clip"><svg focusable="false" class="" data-icon="paper-clip" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M779.3 196.6c-94.2-94.2-247.6-94.2-341.7 0l-261 260.8c-1.7 1.7-2.6 4-2.6 6.4s.9 4.7 2.6 6.4l36.9 36.9a9 9 0 0012.7 0l261-260.8c32.4-32.4 75.5-50.2 121.3-50.2s88.9 17.8 121.2 50.2c32.4 32.4 50.2 75.5 50.2 121.2 0 45.8-17.8 88.8-50.2 121.2l-266 265.9-43.1 43.1c-40.3 40.3-105.8 40.3-146.1 0-19.5-19.5-30.2-45.4-30.2-73s10.7-53.5 30.2-73l263.9-263.8c6.7-6.6 15.5-10.3 24.9-10.3h.1c9.4 0 18.1 3.7 24.7 10.3 6.7 6.7 10.3 15.5 10.3 24.9 0 9.3-3.7 18.1-10.3 24.7L372.4 653c-1.7 1.7-2.6 4-2.6 6.4s.9 4.7 2.6 6.4l36.9 36.9a9 9 0 0012.7 0l215.6-215.6c19.9-19.9 30.8-46.3 30.8-74.4s-11-54.6-30.8-74.4c-41.1-41.1-107.9-41-149 0L463 364 224.8 602.1A172.22 172.22 0 00174 724.8c0 46.3 18.1 89.8 50.8 122.5 33.9 33.8 78.3 50.7 122.7 50.7 44.4 0 88.8-16.9 122.6-50.7l309.2-309C824.8 492.7 850 432 850 367.5c.1-64.6-25.1-125.3-70.7-170.9z"></path></svg></span><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="xxx.png" href="http://www.baidu.com/xxx.png">xxx.png</a><span class="ant-upload-list-item-card-actions "><!----><a title="Remove file"><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div></span></div>
|
||||
<div class=""><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-text"><div class="ant-upload-list-item-info"><span><span role="img" aria-label="paper-clip" class="anticon anticon-paper-clip"><svg focusable="false" class="" data-icon="paper-clip" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M779.3 196.6c-94.2-94.2-247.6-94.2-341.7 0l-261 260.8c-1.7 1.7-2.6 4-2.6 6.4s.9 4.7 2.6 6.4l36.9 36.9a9 9 0 0012.7 0l261-260.8c32.4-32.4 75.5-50.2 121.3-50.2s88.9 17.8 121.2 50.2c32.4 32.4 50.2 75.5 50.2 121.2 0 45.8-17.8 88.8-50.2 121.2l-266 265.9-43.1 43.1c-40.3 40.3-105.8 40.3-146.1 0-19.5-19.5-30.2-45.4-30.2-73s10.7-53.5 30.2-73l263.9-263.8c6.7-6.6 15.5-10.3 24.9-10.3h.1c9.4 0 18.1 3.7 24.7 10.3 6.7 6.7 10.3 15.5 10.3 24.9 0 9.3-3.7 18.1-10.3 24.7L372.4 653c-1.7 1.7-2.6 4-2.6 6.4s.9 4.7 2.6 6.4l36.9 36.9a9 9 0 0012.7 0l215.6-215.6c19.9-19.9 30.8-46.3 30.8-74.4s-11-54.6-30.8-74.4c-41.1-41.1-107.9-41-149 0L463 364 224.8 602.1A172.22 172.22 0 00174 724.8c0 46.3 18.1 89.8 50.8 122.5 33.9 33.8 78.3 50.7 122.7 50.7 44.4 0 88.8-16.9 122.6-50.7l309.2-309C824.8 492.7 850 432 850 367.5c.1-64.6-25.1-125.3-70.7-170.9z"></path></svg></span><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="yyy.png" href="http://www.baidu.com/yyy.png">yyy.png</a><span class="ant-upload-list-item-card-actions "><!----><a title="Remove file"><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div></span></div>
|
||||
<div class="">
|
||||
<!---->
|
||||
<div class="ant-upload-list-item ant-upload-list-item-error ant-upload-list-item-list-type-text">
|
||||
<div class="ant-upload-list-item-info"><span><span role="img" aria-label="paper-clip" class="anticon anticon-paper-clip"><svg focusable="false" class="" data-icon="paper-clip" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M779.3 196.6c-94.2-94.2-247.6-94.2-341.7 0l-261 260.8c-1.7 1.7-2.6 4-2.6 6.4s.9 4.7 2.6 6.4l36.9 36.9a9 9 0 0012.7 0l261-260.8c32.4-32.4 75.5-50.2 121.3-50.2s88.9 17.8 121.2 50.2c32.4 32.4 50.2 75.5 50.2 121.2 0 45.8-17.8 88.8-50.2 121.2l-266 265.9-43.1 43.1c-40.3 40.3-105.8 40.3-146.1 0-19.5-19.5-30.2-45.4-30.2-73s10.7-53.5 30.2-73l263.9-263.8c6.7-6.6 15.5-10.3 24.9-10.3h.1c9.4 0 18.1 3.7 24.7 10.3 6.7 6.7 10.3 15.5 10.3 24.9 0 9.3-3.7 18.1-10.3 24.7L372.4 653c-1.7 1.7-2.6 4-2.6 6.4s.9 4.7 2.6 6.4l36.9 36.9a9 9 0 0012.7 0l215.6-215.6c19.9-19.9 30.8-46.3 30.8-74.4s-11-54.6-30.8-74.4c-41.1-41.1-107.9-41-149 0L463 364 224.8 602.1A172.22 172.22 0 00174 724.8c0 46.3 18.1 89.8 50.8 122.5 33.9 33.8 78.3 50.7 122.7 50.7 44.4 0 88.8-16.9 122.6-50.7l309.2-309C824.8 492.7 850 432 850 367.5c.1-64.6-25.1-125.3-70.7-170.9z"></path></svg></span><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="zzz.png" href="http://www.baidu.com/zzz.png">zzz.png</a><span class="ant-upload-list-item-card-actions "><!----><a title="Remove file"><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div></span>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/upload/demo/directory.vue correctly 1`] = `
|
||||
<span><div class="ant-upload ant-upload-select ant-upload-select-text"><span role="button" tabindex="0" class="ant-upload"><input type="file" style="display: none;" accept="" directory="directory" webkitdirectory="webkitdirectory"><button class="ant-btn" type="button"><!----><span role="img" aria-label="upload" class="anticon anticon-upload"><svg focusable="false" class="" data-icon="upload" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 00-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"></path></svg></span><span>Upload Directory</span></button></span></div>
|
||||
<div class="ant-upload-list ant-upload-list-text"></div></span>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/upload/demo/drag.vue correctly 1`] = `
|
||||
<span><div class="ant-upload ant-upload-drag"><span role="button" tabindex="0" class="ant-upload ant-upload-btn"><input type="file" style="display: none;" accept="" multiple=""><div class="ant-upload-drag-container"><p class="ant-upload-drag-icon"><span role="img" aria-label="inbox" class="anticon anticon-inbox"><svg focusable="false" class="" data-icon="inbox" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M885.2 446.3l-.2-.8-112.2-285.1c-5-16.1-19.9-27.2-36.8-27.2H281.2c-17 0-32.1 11.3-36.9 27.6L139.4 443l-.3.7-.2.8c-1.3 4.9-1.7 9.9-1 14.8-.1 1.6-.2 3.2-.2 4.8V830a60.9 60.9 0 0060.8 60.8h627.2c33.5 0 60.8-27.3 60.9-60.8V464.1c0-1.3 0-2.6-.1-3.7.4-4.9 0-9.6-1.3-14.1zm-295.8-43l-.3 15.7c-.8 44.9-31.8 75.1-77.1 75.1-22.1 0-41.1-7.1-54.8-20.6S436 441.2 435.6 419l-.3-15.7H229.5L309 210h399.2l81.7 193.3H589.4zm-375 76.8h157.3c24.3 57.1 76 90.8 140.4 90.8 33.7 0 65-9.4 90.3-27.2 22.2-15.6 39.5-37.4 50.7-63.6h156.5V814H214.4V480.1z"></path></svg></span></p>
|
||||
<p class="ant-upload-text">Click or drag file to this area to upload</p>
|
||||
<p class="ant-upload-hint"> Support for a single or bulk upload. Strictly prohibit from uploading company data or other band files </p>
|
||||
</div></span></div>
|
||||
<div class="ant-upload-list ant-upload-list-text"></div></span>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/upload/demo/fileList.vue correctly 1`] = `
|
||||
<span><div class="ant-upload ant-upload-select ant-upload-select-text"><span role="button" tabindex="0" class="ant-upload"><input type="file" style="display: none;" accept="" multiple=""><button class="ant-btn" type="button"><!----><span role="img" aria-label="upload" class="anticon anticon-upload"><svg focusable="false" class="" data-icon="upload" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 00-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"></path></svg></span><span>Upload</span></button></span></div>
|
||||
<div class="ant-upload-list ant-upload-list-text">
|
||||
<div class=""><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-text"><div class="ant-upload-list-item-info"><span><span role="img" aria-label="paper-clip" class="anticon anticon-paper-clip"><svg focusable="false" class="" data-icon="paper-clip" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M779.3 196.6c-94.2-94.2-247.6-94.2-341.7 0l-261 260.8c-1.7 1.7-2.6 4-2.6 6.4s.9 4.7 2.6 6.4l36.9 36.9a9 9 0 0012.7 0l261-260.8c32.4-32.4 75.5-50.2 121.3-50.2s88.9 17.8 121.2 50.2c32.4 32.4 50.2 75.5 50.2 121.2 0 45.8-17.8 88.8-50.2 121.2l-266 265.9-43.1 43.1c-40.3 40.3-105.8 40.3-146.1 0-19.5-19.5-30.2-45.4-30.2-73s10.7-53.5 30.2-73l263.9-263.8c6.7-6.6 15.5-10.3 24.9-10.3h.1c9.4 0 18.1 3.7 24.7 10.3 6.7 6.7 10.3 15.5 10.3 24.9 0 9.3-3.7 18.1-10.3 24.7L372.4 653c-1.7 1.7-2.6 4-2.6 6.4s.9 4.7 2.6 6.4l36.9 36.9a9 9 0 0012.7 0l215.6-215.6c19.9-19.9 30.8-46.3 30.8-74.4s-11-54.6-30.8-74.4c-41.1-41.1-107.9-41-149 0L463 364 224.8 602.1A172.22 172.22 0 00174 724.8c0 46.3 18.1 89.8 50.8 122.5 33.9 33.8 78.3 50.7 122.7 50.7 44.4 0 88.8-16.9 122.6-50.7l309.2-309C824.8 492.7 850 432 850 367.5c.1-64.6-25.1-125.3-70.7-170.9z"></path></svg></span><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="xxx.png" href="http://www.baidu.com/xxx.png">xxx.png</a><span class="ant-upload-list-item-card-actions "><!----><a title="Remove file"><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div></span></div>
|
||||
</div></span>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/upload/demo/picture-card.vue correctly 1`] = `
|
||||
<div class="clearfix"><span class="ant-upload-picture-card-wrapper"><div class="ant-upload-list ant-upload-list-picture-card"><div class="ant-upload-list-picture-card-container"><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture-card"><div class="ant-upload-list-item-info"><span><a class="ant-upload-list-item-thumbnail" href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" target="_blank" rel="noopener noreferrer"><img src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" alt="image.png" class="ant-upload-list-item-image"></a><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="image.png" href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png">image.png</a><!----></span></div><span class="ant-upload-list-item-actions"><a href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" target="_blank" rel="noopener noreferrer" title="Preview file"><span role="img" aria-label="eye" class="anticon anticon-eye"><svg focusable="false" class="" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path></svg></span></a>
|
||||
<!----><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></span>
|
||||
<!---->
|
||||
</div></span></div>
|
||||
<div class="ant-upload-list-picture-card-container"><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture-card"><div class="ant-upload-list-item-info"><span><a class="ant-upload-list-item-thumbnail" href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" target="_blank" rel="noopener noreferrer"><img src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" alt="image.png" class="ant-upload-list-item-image"></a><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="image.png" href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png">image.png</a><!----></span></div><span class="ant-upload-list-item-actions"><a href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" target="_blank" rel="noopener noreferrer" title="Preview file"><span role="img" aria-label="eye" class="anticon anticon-eye"><svg focusable="false" class="" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path></svg></span></a>
|
||||
<!----><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></span>
|
||||
<!---->
|
||||
</div></span></div>
|
||||
<div class="ant-upload-list-picture-card-container"><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture-card"><div class="ant-upload-list-item-info"><span><a class="ant-upload-list-item-thumbnail" href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" target="_blank" rel="noopener noreferrer"><img src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" alt="image.png" class="ant-upload-list-item-image"></a><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="image.png" href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png">image.png</a><!----></span></div><span class="ant-upload-list-item-actions"><a href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" target="_blank" rel="noopener noreferrer" title="Preview file"><span role="img" aria-label="eye" class="anticon anticon-eye"><svg focusable="false" class="" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path></svg></span></a>
|
||||
<!----><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></span>
|
||||
<!---->
|
||||
</div></span></div>
|
||||
<div class="ant-upload-list-picture-card-container"><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture-card"><div class="ant-upload-list-item-info"><span><a class="ant-upload-list-item-thumbnail" href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" target="_blank" rel="noopener noreferrer"><img src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" alt="image.png" class="ant-upload-list-item-image"></a><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="image.png" href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png">image.png</a><!----></span></div><span class="ant-upload-list-item-actions"><a href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" target="_blank" rel="noopener noreferrer" title="Preview file"><span role="img" aria-label="eye" class="anticon anticon-eye"><svg focusable="false" class="" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path></svg></span></a>
|
||||
<!----><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></span>
|
||||
<!---->
|
||||
</div></span></div>
|
||||
<div class="ant-upload-list-picture-card-container">
|
||||
<!---->
|
||||
<div class="ant-upload-list-item ant-upload-list-item-error ant-upload-list-item-list-type-picture-card">
|
||||
<div class="ant-upload-list-item-info"><span><span role="img" aria-label="picture" class="anticon anticon-picture ant-upload-list-item-thumbnail"><svg focusable="false" class="" data-icon="picture" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M928 160H96c-17.7 0-32 14.3-32 32v640c0 17.7 14.3 32 32 32h832c17.7 0 32-14.3 32-32V192c0-17.7-14.3-32-32-32zm-40 632H136v-39.9l138.5-164.3 150.1 178L658.1 489 888 761.6V792zm0-129.8L664.2 396.8c-3.2-3.8-9-3.8-12.2 0L424.6 666.4l-144-170.7c-3.2-3.8-9-3.8-12.2 0L136 652.7V232h752v430.2z" fill="#1890ff"></path><path d="M424.6 765.8l-150.1-178L136 752.1V792h752v-30.4L658.1 489z" fill="#e6f7ff"></path><path d="M136 652.7l132.4-157c3.2-3.8 9-3.8 12.2 0l144 170.7L652 396.8c3.2-3.8 9-3.8 12.2 0L888 662.2V232H136v420.7zM304 280a88 88 0 110 176 88 88 0 010-176z" fill="#e6f7ff"></path><path d="M276 368a28 28 0 1056 0 28 28 0 10-56 0z" fill="#e6f7ff"></path><path d="M304 456a88 88 0 100-176 88 88 0 000 176zm0-116c15.5 0 28 12.5 28 28s-12.5 28-28 28-28-12.5-28-28 12.5-28 28-28z" fill="#1890ff"></path></svg></span><span class="ant-upload-list-item-name" title="image.png">image.png</span>
|
||||
<!----></span>
|
||||
</div><span class="ant-upload-list-item-actions"><a target="_blank" rel="noopener noreferrer" style="pointer-events: none; opacity: 0.5;" title="Preview file"><span role="img" aria-label="eye" class="anticon anticon-eye"><svg focusable="false" class="" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path></svg></span></a>
|
||||
<!----><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></span>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ant-upload ant-upload-select ant-upload-select-picture-card"><span role="button" tabindex="0" class="ant-upload"><input type="file" style="display: none;" accept=""><div><span role="img" aria-label="plus" class="anticon anticon-plus"><svg focusable="false" class="" data-icon="plus" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><defs><style></style></defs><path d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"></path><path d="M176 474h672q8 0 8 8v60q0 8-8 8H176q-8 0-8-8v-60q0-8 8-8z"></path></svg></span>
|
||||
<div class="ant-upload-text">Upload</div>
|
||||
</div></span></div></span>
|
||||
<!---->
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/upload/demo/picture-style.vue correctly 1`] = `
|
||||
<div><span><div class="ant-upload ant-upload-select ant-upload-select-picture"><span role="button" tabindex="0" class="ant-upload"><input type="file" style="display: none;" accept=""><button class="ant-btn" type="button"><!----><span role="img" aria-label="upload" class="anticon anticon-upload"><svg focusable="false" class="" data-icon="upload" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 00-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"></path></svg></span><span>upload</span></button></span></div>
|
||||
<div class="ant-upload-list ant-upload-list-picture">
|
||||
<div class=""><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture"><div class="ant-upload-list-item-info"><span><a class="ant-upload-list-item-thumbnail" href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" target="_blank" rel="noopener noreferrer"><img src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" alt="xxx.png" class="ant-upload-list-item-image"></a><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="xxx.png" href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png">xxx.png</a><span class="ant-upload-list-item-card-actions picture"><!----><a title="Remove file"><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div></span></div>
|
||||
<div class=""><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture"><div class="ant-upload-list-item-info"><span><a class="ant-upload-list-item-thumbnail" href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" target="_blank" rel="noopener noreferrer"><img src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" alt="yyy.png" class="ant-upload-list-item-image"></a><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="yyy.png" href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png">yyy.png</a><span class="ant-upload-list-item-card-actions picture"><!----><a title="Remove file"><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div></span></div>
|
||||
</div></span><br><br><span class="upload-list-inline"><div class="ant-upload ant-upload-select ant-upload-select-picture"><span role="button" tabindex="0" class="ant-upload"><input type="file" style="display: none;" accept=""><button class="ant-btn" type="button"><!----><span role="img" aria-label="upload" class="anticon anticon-upload"><svg focusable="false" class="" data-icon="upload" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 00-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"></path></svg></span><span>upload</span></button></span></div>
|
||||
<div class="ant-upload-list ant-upload-list-picture">
|
||||
<div class=""><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture"><div class="ant-upload-list-item-info"><span><a class="ant-upload-list-item-thumbnail" href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" target="_blank" rel="noopener noreferrer"><img src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" alt="xxx.png" class="ant-upload-list-item-image"></a><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="xxx.png" href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png">xxx.png</a><span class="ant-upload-list-item-card-actions picture"><!----><a title="Remove file"><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div></span></div>
|
||||
<div class=""><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture"><div class="ant-upload-list-item-info"><span><a class="ant-upload-list-item-thumbnail" href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" target="_blank" rel="noopener noreferrer"><img src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" alt="yyy.png" class="ant-upload-list-item-image"></a><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="yyy.png" href="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png">yyy.png</a><span class="ant-upload-list-item-card-actions picture"><!----><a title="Remove file"><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div></span></div>
|
||||
</div></span></div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/upload/demo/preview-file.vue correctly 1`] = `
|
||||
<div><span><div class="ant-upload ant-upload-select ant-upload-select-picture"><span role="button" tabindex="0" class="ant-upload"><input type="file" style="display: none;" accept=""><button class="ant-btn" type="button"><!----><span role="img" aria-label="upload" class="anticon anticon-upload"><svg focusable="false" class="" data-icon="upload" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 00-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"></path></svg></span><span>Upload</span></button></span></div>
|
||||
<div class="ant-upload-list ant-upload-list-picture"></div></span></div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/upload/demo/transform-file.vue correctly 1`] = `
|
||||
<div><span><div class="ant-upload ant-upload-select ant-upload-select-text"><span role="button" tabindex="0" class="ant-upload"><input type="file" style="display: none;" accept=""><button class="ant-btn" type="button"><!----><span role="img" aria-label="upload" class="anticon anticon-upload"><svg focusable="false" class="" data-icon="upload" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 00-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"></path></svg></span><span>Upload</span></button></span></div>
|
||||
<div class="ant-upload-list ant-upload-list-text"></div></span></div>
|
||||
`;
|
|
@ -0,0 +1,75 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Upload List handle error 1`] = `
|
||||
<span><div class="ant-upload ant-upload-select ant-upload-select-text"><span role="button" tabindex="0" class="ant-upload"><input type="file" accept="" style="display: none;"><button>upload</button></span></div><span tag="div" class="ant-upload-list ant-upload-list-text"><div class=""><span><div class="ant-upload-list-item ant-upload-list-item-uploading ant-upload-list-item-list-type-text"><div class="ant-upload-list-item-info"><span><span role="img" aria-label="loading" class="anticon anticon-loading"><svg viewBox="0 0 1024 1024" focusable="false" data-icon="loading" width="1em" height="1em" fill="currentColor" aria-hidden="true" class="anticon-spin"><path d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 00-94.3-139.9 437.71 437.71 0 00-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"></path></svg></span><span title="foo.png" class="ant-upload-list-item-name">foo.png</span><span class="ant-upload-list-item-card-actions "><a title="Remove file"><span role="img" aria-label="delete" tabindex="-1" title="Remove file" class="anticon anticon-delete"><svg viewBox="64 64 896 896" focusable="false" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<div class="ant-upload-list-item-progress">
|
||||
<div class="ant-progress ant-progress-line ant-progress-status-normal ant-progress-default">
|
||||
<div>
|
||||
<div class="ant-progress-outer">
|
||||
<div class="ant-progress-inner">
|
||||
<div class="ant-progress-bg" style="width: 0%; height: 2px; border-radius: 100px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div></span></div></span></span>
|
||||
`;
|
||||
|
||||
exports[`Upload List handle error 2`] = `
|
||||
<span><div class="ant-upload ant-upload-select ant-upload-select-text"><span role="button" tabindex="0" class="ant-upload"><input type="file" accept="" style="display: none;"><button>upload</button></span></div><span tag="div" class="ant-upload-list ant-upload-list-text"><div class=""><span><div class="ant-upload-list-item ant-upload-list-item-uploading ant-upload-list-item-list-type-text"><div class="ant-upload-list-item-info"><span><span role="img" aria-label="loading" class="anticon anticon-loading"><svg viewBox="0 0 1024 1024" focusable="false" data-icon="loading" width="1em" height="1em" fill="currentColor" aria-hidden="true" class="anticon-spin"><path d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 00-94.3-139.9 437.71 437.71 0 00-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"></path></svg></span><span title="foo.png" class="ant-upload-list-item-name">foo.png</span><span class="ant-upload-list-item-card-actions "><a title="Remove file"><span role="img" aria-label="delete" tabindex="-1" title="Remove file" class="anticon anticon-delete"><svg viewBox="64 64 896 896" focusable="false" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<div class="ant-upload-list-item-progress">
|
||||
<div class="ant-progress ant-progress-line ant-progress-status-normal ant-progress-default">
|
||||
<div>
|
||||
<div class="ant-progress-outer">
|
||||
<div class="ant-progress-inner">
|
||||
<div class="ant-progress-bg" style="width: 0%; height: 2px; border-radius: 100px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div></span></div></span></span>
|
||||
`;
|
||||
|
||||
exports[`Upload List should be uploading when upload a file 1`] = `<span><div class="ant-upload ant-upload-select ant-upload-select-text"><span role="button" tabindex="0" class="ant-upload"><input type="file" accept="" style="display: none;"><button>upload</button></span></div><span tag="div" class="ant-upload-list ant-upload-list-text"></span></span>`;
|
||||
|
||||
exports[`Upload List should non-image format file preview 1`] = `
|
||||
<span><div class="ant-upload ant-upload-select ant-upload-select-picture"><!----></div><div class="ant-upload-list ant-upload-list-picture"><div class=""><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture"><div class="ant-upload-list-item-info"><span><a class="ant-upload-list-item-thumbnail" href="https://cdn.xxx.com/aaa.zip" target="_blank" rel="noopener noreferrer"><span role="img" aria-label="file" class="anticon anticon-file ant-upload-list-item-icon"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></a><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="not-image" href="https://cdn.xxx.com/aaa.zip">not-image</a><span class="ant-upload-list-item-card-actions picture"><!----><a title="Remove file"><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div></span></div>
|
||||
<div class=""><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture"><div class="ant-upload-list-item-info"><span><a class="ant-upload-list-item-thumbnail" href="https://cdn.xxx.com/aaa" target="_blank" rel="noopener noreferrer"><img src="https://cdn.xxx.com/aaa" alt="image" class="ant-upload-list-item-image"></a><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="image" href="https://cdn.xxx.com/aaa">image</a><span class="ant-upload-list-item-card-actions picture"><!----><a title="Remove file"><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div></span></div>
|
||||
<div class=""><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture"><div class="ant-upload-list-item-info"><span><a class="ant-upload-list-item-thumbnail" href="https://cdn.xxx.com/aaa.xx" target="_blank" rel="noopener noreferrer"><span role="img" aria-label="file" class="anticon anticon-file ant-upload-list-item-icon"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></a><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="not-image" href="https://cdn.xxx.com/aaa.xx">not-image</a><span class="ant-upload-list-item-card-actions picture"><!----><a title="Remove file"><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div></span></div>
|
||||
<div class=""><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture"><div class="ant-upload-list-item-info"><span><a class="ant-upload-list-item-thumbnail" href="https://cdn.xxx.com/aaa.png/xx.xx" target="_blank" rel="noopener noreferrer"><span role="img" aria-label="file" class="anticon anticon-file ant-upload-list-item-icon"><svg focusable="false" class="" data-icon="file" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494z"></path></svg></span></a><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="not-image" href="https://cdn.xxx.com/aaa.png/xx.xx">not-image</a><span class="ant-upload-list-item-card-actions picture"><!----><a title="Remove file"><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div></span></div>
|
||||
<div class=""><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture"><div class="ant-upload-list-item-info"><span><a class="ant-upload-list-item-thumbnail" href="https://cdn.xxx.com/xx.xx/aaa.png" target="_blank" rel="noopener noreferrer"><img src="https://cdn.xxx.com/xx.xx/aaa.png" alt="image" class="ant-upload-list-item-image"></a><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="image" href="https://cdn.xxx.com/xx.xx/aaa.png">image</a><span class="ant-upload-list-item-card-actions picture"><!----><a title="Remove file"><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div></span></div>
|
||||
<div class=""><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture"><div class="ant-upload-list-item-info"><span><a class="ant-upload-list-item-thumbnail" href="https://cdn.xxx.com/xx.xx/aaa.png" target="_blank" rel="noopener noreferrer"><img src="data:image/png;base64,UEsDBAoAAAAAADYZYkwAAAAAAAAAAAAAAAAdAAk" alt="image" class="ant-upload-list-item-image"></a><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="image" href="https://cdn.xxx.com/xx.xx/aaa.png">image</a><span class="ant-upload-list-item-card-actions picture"><!----><a title="Remove file"><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div></span></div>
|
||||
<div class=""><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture"><div class="ant-upload-list-item-info"><span><a class="ant-upload-list-item-thumbnail" href="https://cdn.xxx.com/xx.xx/aaa.png?query=123" target="_blank" rel="noopener noreferrer"><img src="https://cdn.xxx.com/xx.xx/aaa.png?query=123" alt="image" class="ant-upload-list-item-image"></a><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="image" href="https://cdn.xxx.com/xx.xx/aaa.png?query=123">image</a><span class="ant-upload-list-item-card-actions picture"><!----><a title="Remove file"><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div></span></div>
|
||||
<div class=""><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture"><div class="ant-upload-list-item-info"><span><a class="ant-upload-list-item-thumbnail" href="https://cdn.xxx.com/xx.xx/aaa.png#anchor" target="_blank" rel="noopener noreferrer"><img src="https://cdn.xxx.com/xx.xx/aaa.png#anchor" alt="image" class="ant-upload-list-item-image"></a><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="image" href="https://cdn.xxx.com/xx.xx/aaa.png#anchor">image</a><span class="ant-upload-list-item-card-actions picture"><!----><a title="Remove file"><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div></span></div>
|
||||
<div class=""><span><div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture"><div class="ant-upload-list-item-info"><span><a class="ant-upload-list-item-thumbnail" href="https://cdn.xxx.com/xx.xx/aaa.png?query=some.query.with.dot" target="_blank" rel="noopener noreferrer"><img src="https://cdn.xxx.com/xx.xx/aaa.png?query=some.query.with.dot" alt="image" class="ant-upload-list-item-image"></a><a target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name ant-upload-list-item-name-icon-count-1" title="image" href="https://cdn.xxx.com/xx.xx/aaa.png?query=some.query.with.dot">image</a><span class="ant-upload-list-item-card-actions picture"><!----><a title="Remove file"><span title="Remove file" tabindex="-1" role="img" aria-label="delete" class="anticon anticon-delete"><svg focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg></span></a></span></span></div>
|
||||
<!---->
|
||||
<!---->
|
||||
</div></span></div>
|
||||
</div></span>
|
||||
`;
|
|
@ -0,0 +1,3 @@
|
|||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('upload', { skip: ['upload-manually'] });
|
|
@ -0,0 +1,14 @@
|
|||
import mock from 'xhr-mock';
|
||||
|
||||
export function setup() {
|
||||
mock.setup();
|
||||
mock.post('http://upload.com/', (req, res) => {
|
||||
req.headers({
|
||||
'content-length': 100,
|
||||
});
|
||||
req.body('thisisbody');
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
export const teardown = mock.teardown.bind(mock);
|
|
@ -0,0 +1,11 @@
|
|||
export const successRequest = ({ onSuccess, file }) => {
|
||||
setTimeout(() => {
|
||||
onSuccess(null, file);
|
||||
});
|
||||
};
|
||||
|
||||
export const errorRequest = ({ onError }) => {
|
||||
setTimeout(() => {
|
||||
onError();
|
||||
});
|
||||
};
|
|
@ -0,0 +1,331 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import Upload from '..';
|
||||
import { T, fileToObject, genPercentAdd, getFileItem, removeFileItem } from '../utils';
|
||||
import PropsTypes from '../../_util/vue-types';
|
||||
import { uploadListProps } from '../interface';
|
||||
import { setup, teardown } from './mock';
|
||||
|
||||
uploadListProps.items = PropsTypes.any;
|
||||
|
||||
describe('Upload', () => {
|
||||
beforeEach(() => setup());
|
||||
afterEach(() => teardown());
|
||||
it('should get refs inside Upload in componentDidMount', () => {
|
||||
let ref = null;
|
||||
const APP = {
|
||||
mounted() {
|
||||
ref = this.$refs.input;
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<Upload supportServerRender={false} action="">
|
||||
<input ref="input" />
|
||||
</Upload>
|
||||
);
|
||||
},
|
||||
};
|
||||
mount(APP);
|
||||
expect(ref).toBeDefined();
|
||||
});
|
||||
|
||||
xit('return promise in beforeUpload', done => {
|
||||
const data = jest.fn();
|
||||
const props = {
|
||||
props: {
|
||||
action: 'http://upload.com',
|
||||
beforeUpload: () => new Promise(resolve => setTimeout(() => resolve('success'), 100)),
|
||||
data,
|
||||
},
|
||||
listeners: {
|
||||
change: ({ file }) => {
|
||||
if (file.status !== 'uploading') {
|
||||
expect(data).toBeCalled();
|
||||
done();
|
||||
}
|
||||
},
|
||||
},
|
||||
slots: {
|
||||
default: () => <button>upload</button>,
|
||||
},
|
||||
sync: false,
|
||||
};
|
||||
const wrapper = mount(Upload, props);
|
||||
setTimeout(() => {
|
||||
wrapper.findComponent('ajaxUploader').vm.onChange({
|
||||
target: {
|
||||
files: [{ file: 'foo.png' }],
|
||||
},
|
||||
});
|
||||
}, 0);
|
||||
});
|
||||
|
||||
xit('upload promise return file in beforeUpload', done => {
|
||||
const data = jest.fn();
|
||||
const props = {
|
||||
action: 'http://upload.com',
|
||||
beforeUpload: file =>
|
||||
new Promise(resolve =>
|
||||
setTimeout(() => {
|
||||
const result = file;
|
||||
result.name = 'test.png';
|
||||
resolve(result);
|
||||
}, 100),
|
||||
),
|
||||
data,
|
||||
onChange: ({ file }) => {
|
||||
if (file.status !== 'uploading') {
|
||||
expect(data).toBeCalled();
|
||||
expect(file.name).toEqual('test.png');
|
||||
done();
|
||||
}
|
||||
},
|
||||
slots: {
|
||||
default: () => <button>upload</button>,
|
||||
},
|
||||
sync: false,
|
||||
};
|
||||
|
||||
const wrapper = mount(Upload, props);
|
||||
|
||||
setTimeout(() => {
|
||||
wrapper.find({ name: 'ajaxUploader' }).vm.onChange({
|
||||
target: {
|
||||
files: [{ file: 'foo.png' }],
|
||||
},
|
||||
});
|
||||
}, 0);
|
||||
});
|
||||
|
||||
xit('should not stop upload when return value of beforeUpload is false', done => {
|
||||
const data = jest.fn();
|
||||
const props = {
|
||||
action: 'http://upload.com',
|
||||
beforeUpload: () => false,
|
||||
data,
|
||||
onChange: ({ file }) => {
|
||||
expect(file instanceof File).toBe(true);
|
||||
expect(data).not.toBeCalled();
|
||||
done();
|
||||
},
|
||||
slots: {
|
||||
default: () => <button>upload</button>,
|
||||
},
|
||||
sync: false,
|
||||
};
|
||||
const wrapper = mount(Upload, props);
|
||||
setTimeout(() => {
|
||||
const mockFile = new File(['foo'], 'foo.png', {
|
||||
type: 'image/png',
|
||||
});
|
||||
wrapper.find({ name: 'ajaxUploader' }).vm.onChange({
|
||||
target: {
|
||||
files: [mockFile],
|
||||
},
|
||||
});
|
||||
}, 0);
|
||||
});
|
||||
|
||||
xit('should increase percent automaticly when call autoUpdateProgress in IE', done => {
|
||||
let uploadInstance;
|
||||
let lastPercent = -1;
|
||||
const props = {
|
||||
props: {
|
||||
action: 'http://upload.com',
|
||||
},
|
||||
listeners: {
|
||||
change: ({ file }) => {
|
||||
if (file.percent === 0 && file.status === 'uploading') {
|
||||
// manually call it
|
||||
uploadInstance.autoUpdateProgress(0, file);
|
||||
}
|
||||
if (file.status === 'uploading') {
|
||||
expect(file.percent).toBeGreaterThan(lastPercent);
|
||||
lastPercent = file.percent;
|
||||
}
|
||||
if (file.status === 'done' || file.status === 'error') {
|
||||
done();
|
||||
}
|
||||
},
|
||||
},
|
||||
slots: {
|
||||
default: '<button>upload</button>',
|
||||
},
|
||||
sync: false,
|
||||
};
|
||||
const wrapper = mount(Upload, props);
|
||||
setTimeout(() => {
|
||||
const mockFile = new File(['foo'], 'foo.png', {
|
||||
type: 'image/png',
|
||||
});
|
||||
wrapper.find({ name: 'ajaxUploader' }).vm.onChange({
|
||||
target: {
|
||||
files: [mockFile],
|
||||
},
|
||||
});
|
||||
uploadInstance = wrapper.vm;
|
||||
}, 0);
|
||||
});
|
||||
xit('should not stop upload when return value of beforeUpload is not false', done => {
|
||||
const data = jest.fn();
|
||||
const props = {
|
||||
props: {
|
||||
action: 'http://upload.com',
|
||||
beforeUpload() {},
|
||||
data,
|
||||
},
|
||||
listeners: {
|
||||
change: () => {
|
||||
expect(data).toBeCalled();
|
||||
done();
|
||||
},
|
||||
},
|
||||
slots: {
|
||||
default: '<button>upload</button>',
|
||||
},
|
||||
sync: false,
|
||||
};
|
||||
|
||||
const wrapper = mount(Upload, props);
|
||||
setTimeout(() => {
|
||||
const mockFile = new File(['foo'], 'foo.png', {
|
||||
type: 'image/png',
|
||||
});
|
||||
wrapper.find({ name: 'ajaxUploader' }).vm.onChange({
|
||||
target: {
|
||||
files: [mockFile],
|
||||
},
|
||||
});
|
||||
}, 0);
|
||||
});
|
||||
|
||||
describe('util', () => {
|
||||
// https://github.com/react-component/upload/issues/36
|
||||
it('should T() return true', () => {
|
||||
const res = T();
|
||||
expect(res).toBe(true);
|
||||
});
|
||||
it('should be able to copy file instance', () => {
|
||||
const file = new File([], 'aaa.zip');
|
||||
const copiedFile = fileToObject(file);
|
||||
['uid', 'lastModified', 'lastModifiedDate', 'name', 'size', 'type'].forEach(key => {
|
||||
expect(key in copiedFile).toBe(true);
|
||||
});
|
||||
});
|
||||
it('should be able to progress from 0.1 ', () => {
|
||||
// 0.1 -> 0.98
|
||||
const getPercent = genPercentAdd();
|
||||
let curPercent = 0;
|
||||
curPercent = getPercent(curPercent);
|
||||
expect(curPercent).toBe(0.1);
|
||||
});
|
||||
|
||||
it('should be able to progress to 0.98 ', () => {
|
||||
// 0.1 -> 0.98
|
||||
const getPercent = genPercentAdd();
|
||||
let curPercent = 0;
|
||||
for (let i = 0; i < 500; i += 1) {
|
||||
curPercent = getPercent(curPercent);
|
||||
}
|
||||
expect(parseFloat(curPercent.toFixed(2))).toBe(0.98);
|
||||
});
|
||||
|
||||
it('should be able to get fileItem', () => {
|
||||
const file = { uid: '-1', name: 'item.jpg' };
|
||||
const fileList = [
|
||||
{
|
||||
uid: '-1',
|
||||
name: 'item.jpg',
|
||||
},
|
||||
];
|
||||
const targetItem = getFileItem(file, fileList);
|
||||
expect(targetItem).toBe(fileList[0]);
|
||||
});
|
||||
|
||||
it('should be able to remove fileItem', () => {
|
||||
const file = { uid: '-1', name: 'item.jpg' };
|
||||
const fileList = [
|
||||
{
|
||||
uid: '-1',
|
||||
name: 'item.jpg',
|
||||
},
|
||||
{
|
||||
uid: '-2',
|
||||
name: 'item2.jpg',
|
||||
},
|
||||
];
|
||||
const targetItem = removeFileItem(file, fileList);
|
||||
expect(targetItem).toEqual(fileList.slice(1));
|
||||
});
|
||||
|
||||
it('should not be able to remove fileItem', () => {
|
||||
const file = { uid: '-3', name: 'item.jpg' };
|
||||
const fileList = [
|
||||
{
|
||||
uid: '-1',
|
||||
name: 'item.jpg',
|
||||
},
|
||||
{
|
||||
uid: '-2',
|
||||
name: 'item2.jpg',
|
||||
},
|
||||
];
|
||||
const targetItem = removeFileItem(file, fileList);
|
||||
expect(targetItem).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
it('should support linkProps as object', () => {
|
||||
const fileList = [
|
||||
{
|
||||
uid: '-1',
|
||||
name: 'foo.png',
|
||||
status: 'done',
|
||||
url: 'http://www.baidu.com/xxx.png',
|
||||
linkProps: {
|
||||
download: 'image',
|
||||
rel: 'noopener',
|
||||
},
|
||||
},
|
||||
];
|
||||
const props = {
|
||||
props: {
|
||||
fileList,
|
||||
},
|
||||
sync: false,
|
||||
};
|
||||
const wrapper = mount(Upload, props);
|
||||
setTimeout(() => {
|
||||
const linkNode = wrapper.find('a.ant-upload-list-item-name');
|
||||
expect(linkNode.props().download).toBe('image');
|
||||
expect(linkNode.props().rel).toBe('noopener');
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('should support linkProps as json stringify', () => {
|
||||
const linkPropsString = JSON.stringify({
|
||||
download: 'image',
|
||||
rel: 'noopener',
|
||||
});
|
||||
const fileList = [
|
||||
{
|
||||
uid: '-1',
|
||||
name: 'foo.png',
|
||||
status: 'done',
|
||||
url: 'http://www.baidu.com/xxx.png',
|
||||
linkProps: linkPropsString,
|
||||
},
|
||||
];
|
||||
const props = {
|
||||
props: {
|
||||
fileList,
|
||||
},
|
||||
sync: false,
|
||||
};
|
||||
const wrapper = mount(Upload, props);
|
||||
setTimeout(() => {
|
||||
const linkNode = wrapper.find('a.ant-upload-list-item-name');
|
||||
expect(linkNode.props().download).toBe('image');
|
||||
expect(linkNode.props().rel).toBe('noopener');
|
||||
}, 0);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,433 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import * as Vue from 'vue';
|
||||
import Upload from '..';
|
||||
import { errorRequest, successRequest } from './requests';
|
||||
import PropsTypes from '../../_util/vue-types';
|
||||
import { uploadListProps } from '../interface';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
import { h } from 'vue';
|
||||
|
||||
uploadListProps.items = PropsTypes.any;
|
||||
|
||||
const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout));
|
||||
const fileList = [
|
||||
{
|
||||
uid: -1,
|
||||
name: 'xxx.png',
|
||||
status: 'done',
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/IQKRngzUuFzJzGzRJXUs.png',
|
||||
},
|
||||
{
|
||||
uid: -2,
|
||||
name: 'yyy.png',
|
||||
status: 'done',
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/IQKRngzUuFzJzGzRJXUs.png',
|
||||
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
},
|
||||
];
|
||||
|
||||
describe('Upload List', () => {
|
||||
// jsdom not support `createObjectURL` yet. Let's handle this.
|
||||
const originCreateObjectURL = window.URL.createObjectURL;
|
||||
window.URL.createObjectURL = jest.fn(() => '');
|
||||
const originHTMLCanvasElementGetContext = window.HTMLCanvasElement.prototype.getContext;
|
||||
window.HTMLCanvasElement.prototype.getContext = jest.fn(() => '');
|
||||
// https://github.com/ant-design/ant-design/issues/4653
|
||||
afterAll(() => {
|
||||
window.URL.createObjectURL = originCreateObjectURL;
|
||||
window.HTMLCanvasElement.prototype.getContext = originHTMLCanvasElementGetContext;
|
||||
});
|
||||
it('should use file.thumbUrl for <img /> in priority', done => {
|
||||
const props = {
|
||||
props: {
|
||||
defaultFileList: fileList,
|
||||
listType: 'picture',
|
||||
action: '',
|
||||
},
|
||||
slots: {
|
||||
default: () => h('button', 'upload'),
|
||||
},
|
||||
sync: false,
|
||||
};
|
||||
const wrapper = mount(Upload, props);
|
||||
Vue.nextTick(() => {
|
||||
fileList.forEach((file, i) => {
|
||||
const linkNode = wrapper.findAll('.ant-upload-list-item-thumbnail')[i];
|
||||
const imgNode = wrapper.findAll('.ant-upload-list-item-thumbnail img')[i];
|
||||
expect(linkNode.attributes().href).toBe(file.url);
|
||||
expect(imgNode.attributes().src).toBe(file.thumbUrl);
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/7269
|
||||
it('should remove correct item when uid is 0', done => {
|
||||
const list = [
|
||||
{
|
||||
uid: 0,
|
||||
name: 'xxx.png',
|
||||
status: 'done',
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/IQKRngzUuFzJzGzRJXUs.png',
|
||||
},
|
||||
{
|
||||
uid: 1,
|
||||
name: 'xxx.png',
|
||||
status: 'done',
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/IQKRngzUuFzJzGzRJXUs.png',
|
||||
},
|
||||
];
|
||||
const props = {
|
||||
props: {
|
||||
defaultFileList: list,
|
||||
action: '',
|
||||
},
|
||||
slots: {
|
||||
default: () => h('button', 'upload'),
|
||||
},
|
||||
sync: false,
|
||||
};
|
||||
const wrapper = mount(Upload, props);
|
||||
setTimeout(async () => {
|
||||
expect(wrapper.findAll('.ant-upload-list-item').length).toBe(2);
|
||||
wrapper.findAll('.ant-upload-list-item')[0].find('.anticon-delete').trigger('click');
|
||||
await delay(400);
|
||||
// wrapper.update();
|
||||
expect(wrapper.findAll('.ant-upload-list-item').length).toBe(1);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
xit('should be uploading when upload a file', done => {
|
||||
const props = {
|
||||
props: {
|
||||
action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
|
||||
customRequest: successRequest,
|
||||
onChange: ({ file }) => {
|
||||
if (file.status === 'uploading') {
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
done();
|
||||
}
|
||||
if (file.status === 'done') {
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
done();
|
||||
}
|
||||
},
|
||||
},
|
||||
slots: {
|
||||
default: () => h('button', 'upload'),
|
||||
},
|
||||
sync: false,
|
||||
};
|
||||
const wrapper = mount(Upload, props);
|
||||
setTimeout(() => {
|
||||
const mockFile = new File(['foo'], 'foo.png', {
|
||||
type: 'image/png',
|
||||
});
|
||||
wrapper.findComponent({ name: 'ajaxUploader' }).vm.onChange({
|
||||
target: {
|
||||
files: [mockFile],
|
||||
},
|
||||
});
|
||||
}, 0);
|
||||
});
|
||||
|
||||
xit('handle error', done => {
|
||||
const props = {
|
||||
props: {
|
||||
action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
|
||||
customRequest: errorRequest,
|
||||
},
|
||||
listeners: {
|
||||
change: ({ file }) => {
|
||||
if (file.status !== 'uploading') {
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
done();
|
||||
}
|
||||
},
|
||||
},
|
||||
slots: {
|
||||
default: () => h('button', 'upload'),
|
||||
},
|
||||
sync: false,
|
||||
};
|
||||
const wrapper = mount(Upload, props);
|
||||
setTimeout(() => {
|
||||
const mockFile = new File(['foo'], 'foo.png', {
|
||||
type: 'image/png',
|
||||
});
|
||||
wrapper.findComponent({ name: 'ajaxUploader' }).vm.onChange({
|
||||
target: {
|
||||
files: [mockFile],
|
||||
},
|
||||
});
|
||||
}, 0);
|
||||
});
|
||||
|
||||
xit('does concat filelist when beforeUpload returns false', done => {
|
||||
const handleChange = jest.fn();
|
||||
const props = {
|
||||
props: {
|
||||
action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
|
||||
listType: 'picture',
|
||||
defaultFileList: fileList,
|
||||
beforeUpload: () => false,
|
||||
onChange: handleChange,
|
||||
},
|
||||
slots: {
|
||||
default: () => h('button', 'upload'),
|
||||
},
|
||||
sync: false,
|
||||
};
|
||||
const wrapper = mount(Upload, props);
|
||||
|
||||
setTimeout(() => {
|
||||
const mockFile = new File(['foo'], 'foo.png', {
|
||||
type: 'image/png',
|
||||
});
|
||||
wrapper.findComponent({ name: 'ajaxUploader' }).vm.onChange({
|
||||
target: {
|
||||
files: [mockFile],
|
||||
},
|
||||
});
|
||||
Vue.nextTick(() => {
|
||||
expect(wrapper.vm.sFileList.length).toBe(fileList.length + 1);
|
||||
expect(handleChange.mock.calls[0][0].fileList).toHaveLength(3);
|
||||
done();
|
||||
});
|
||||
}, 0);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/7762
|
||||
// it('work with form validation', (done) => {
|
||||
// let errors
|
||||
// const TestForm = {
|
||||
// methods: {
|
||||
// handleSubmit () {
|
||||
// const { validateFields } = this.form
|
||||
// validateFields((err) => {
|
||||
// errors = err
|
||||
// })
|
||||
// },
|
||||
// },
|
||||
// render () {
|
||||
// const { getFieldDecorator } = this.form
|
||||
|
||||
// return (
|
||||
// <Form onSubmit={this.handleSubmit}>
|
||||
// <Form.Item>
|
||||
// {getFieldDecorator('file', {
|
||||
// valuePropname: 'fileList',
|
||||
// getValueFromEvent: e => e.fileList,
|
||||
// rules: [
|
||||
// {
|
||||
// required: true,
|
||||
// validator: (rule, value, callback) => {
|
||||
// if (!value || value.length === 0) {
|
||||
// callback('file required')
|
||||
// } else {
|
||||
// callback()
|
||||
// }
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// })(
|
||||
// <Upload
|
||||
// beforeUpload={() => false}
|
||||
// >
|
||||
// <button>upload</button>
|
||||
// </Upload>
|
||||
// )}
|
||||
// </Form.Item>
|
||||
// </Form>
|
||||
// )
|
||||
// },
|
||||
// }
|
||||
|
||||
// const App = Form.create()(TestForm)
|
||||
// console.dir(App)
|
||||
// const wrapper = mount(() => {
|
||||
// return <App />
|
||||
// })
|
||||
// setTimeout(async () => {
|
||||
// wrapper.find(Form).trigger('submit')
|
||||
// expect(errors.file.errors).toEqual([{ message: 'file required', field: 'file' }])
|
||||
|
||||
// const mockFile = new File(['foo'], 'foo.png', {
|
||||
// type: 'image/png',
|
||||
// })
|
||||
// wrapper.findComponent({ name: 'ajaxUploader' }).vm.onChange({
|
||||
// target: {
|
||||
// files: [mockFile],
|
||||
// },
|
||||
// })
|
||||
// wrapper.find(Form).trigger('submit')
|
||||
// expect(errors).toBeNull()
|
||||
// done()
|
||||
// }, 0)
|
||||
// })
|
||||
|
||||
it('should support onPreview', async () => {
|
||||
const handlePreview = jest.fn();
|
||||
const props = {
|
||||
props: {
|
||||
defaultFileList: fileList,
|
||||
listType: 'picture-card',
|
||||
action: '',
|
||||
onPreview: handlePreview,
|
||||
},
|
||||
slots: {
|
||||
default: () => h('button', 'upload'),
|
||||
},
|
||||
sync: false,
|
||||
};
|
||||
const wrapper = mount(Upload, props);
|
||||
await sleep(500);
|
||||
wrapper.findAll('.anticon-eye')[0].trigger('click');
|
||||
expect(handlePreview).toBeCalledWith(fileList[0]);
|
||||
wrapper.findAll('.anticon-eye')[1].trigger('click');
|
||||
expect(handlePreview).toBeCalledWith(fileList[1]);
|
||||
});
|
||||
|
||||
it('should support onRemove', done => {
|
||||
const handleRemove = jest.fn();
|
||||
const handleChange = jest.fn();
|
||||
const props = {
|
||||
props: {
|
||||
defaultFileList: fileList,
|
||||
listType: 'picture-card',
|
||||
action: '',
|
||||
remove: handleRemove,
|
||||
onChange: handleChange,
|
||||
},
|
||||
|
||||
slots: {
|
||||
default: () => h('button', 'upload'),
|
||||
},
|
||||
sync: false,
|
||||
};
|
||||
const wrapper = mount(Upload, props);
|
||||
jest.setTimeout(300000);
|
||||
setTimeout(async () => {
|
||||
wrapper.findAll('.anticon-delete')[0].trigger('click');
|
||||
expect(handleRemove).toBeCalledWith(fileList[0]);
|
||||
wrapper.findAll('.anticon-delete')[1].trigger('click');
|
||||
expect(handleRemove).toBeCalledWith(fileList[1]);
|
||||
await delay(0);
|
||||
expect(handleChange.mock.calls.length).toBe(2);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
xit('should generate thumbUrl from file', done => {
|
||||
const handlePreview = jest.fn();
|
||||
const newFileList = [...fileList];
|
||||
const newFile = { ...fileList[0], uid: -3, originFileObj: new File([], 'xxx.png') };
|
||||
delete newFile.thumbUrl;
|
||||
newFileList.push(newFile);
|
||||
const props = {
|
||||
props: {
|
||||
defaultFileList: newFileList,
|
||||
listType: 'picture-card',
|
||||
action: '',
|
||||
onPreview: handlePreview,
|
||||
},
|
||||
slots: {
|
||||
default: () => h('button', 'upload'),
|
||||
},
|
||||
sync: false,
|
||||
};
|
||||
const wrapper = mount(Upload, props);
|
||||
setTimeout(async () => {
|
||||
const newFile = { ...fileList[2], uid: -4, originFileObj: new File([], 'xxx.png') };
|
||||
newFileList.push(newFile);
|
||||
wrapper.setProps({
|
||||
defaultFileList: [...newFileList],
|
||||
});
|
||||
await delay(200);
|
||||
expect(wrapper.vm.sFileList[2].thumbUrl).not.toBe(undefined);
|
||||
done();
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
it('should non-image format file preview', done => {
|
||||
const list = [
|
||||
{
|
||||
name: 'not-image',
|
||||
status: 'done',
|
||||
uid: -3,
|
||||
url: 'https://cdn.xxx.com/aaa.zip',
|
||||
thumbUrl: 'data:application/zip;base64,UEsDBAoAAAAAADYZYkwAAAAAAAAAAAAAAAAdAAk',
|
||||
originFileObj: new File([], 'aaa.zip'),
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
status: 'done',
|
||||
uid: -4,
|
||||
url: 'https://cdn.xxx.com/aaa',
|
||||
},
|
||||
{
|
||||
name: 'not-image',
|
||||
status: 'done',
|
||||
uid: -5,
|
||||
url: 'https://cdn.xxx.com/aaa.xx',
|
||||
},
|
||||
{
|
||||
name: 'not-image',
|
||||
status: 'done',
|
||||
uid: -6,
|
||||
url: 'https://cdn.xxx.com/aaa.png/xx.xx',
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
status: 'done',
|
||||
uid: -7,
|
||||
url: 'https://cdn.xxx.com/xx.xx/aaa.png',
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
status: 'done',
|
||||
uid: -8,
|
||||
url: 'https://cdn.xxx.com/xx.xx/aaa.png',
|
||||
thumbUrl: 'data:image/png;base64,UEsDBAoAAAAAADYZYkwAAAAAAAAAAAAAAAAdAAk',
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
status: 'done',
|
||||
uid: -9,
|
||||
url: 'https://cdn.xxx.com/xx.xx/aaa.png?query=123',
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
status: 'done',
|
||||
uid: -10,
|
||||
url: 'https://cdn.xxx.com/xx.xx/aaa.png#anchor',
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
status: 'done',
|
||||
uid: -11,
|
||||
url: 'https://cdn.xxx.com/xx.xx/aaa.png?query=some.query.with.dot',
|
||||
},
|
||||
];
|
||||
const props = {
|
||||
props: {
|
||||
defaultFileList: list,
|
||||
listType: 'picture',
|
||||
action: '',
|
||||
},
|
||||
slots: {
|
||||
default: () => h('button', 'upload'),
|
||||
},
|
||||
sync: false,
|
||||
};
|
||||
const wrapper = mount(Upload, props);
|
||||
Vue.nextTick(() => {
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,116 @@
|
|||
<docs>
|
||||
---
|
||||
order: 1
|
||||
title:
|
||||
zh-CN: 用户头像
|
||||
en-US: Avatar
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
点击上传用户头像,并使用 `beforeUpload` 限制用户上传的图片格式和大小。
|
||||
|
||||
> `beforeUpload` 的返回值可以是一个 Promise 以支持异步处理,如服务端校验等:[示例](http://react-component.github.io/upload/examples/beforeUpload.html)。
|
||||
|
||||
## en-US
|
||||
|
||||
Click to upload user's avatar, and validate size and format of picture with `beforeUpload`.
|
||||
|
||||
> The return value of function `beforeUpload` can be a Promise to check asynchronously. [demo](http://react-component.github.io/upload/examples/beforeUpload.html)
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<a-upload
|
||||
v-model:file-list="fileList"
|
||||
name="avatar"
|
||||
list-type="picture-card"
|
||||
class="avatar-uploader"
|
||||
:show-upload-list="false"
|
||||
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
|
||||
:before-upload="beforeUpload"
|
||||
@change="handleChange"
|
||||
>
|
||||
<img v-if="imageUrl" :src="imageUrl" alt="avatar" />
|
||||
<div v-else>
|
||||
<loading-outlined v-if="loading"></loading-outlined>
|
||||
<plus-outlined v-else></plus-outlined>
|
||||
<div class="ant-upload-text">Upload</div>
|
||||
</div>
|
||||
</a-upload>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import type { UploadChangeParam, UploadProps } from 'ant-design-vue';
|
||||
|
||||
function getBase64(img: Blob, callback: (base64Url: string) => void) {
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('load', () => callback(reader.result as string));
|
||||
reader.readAsDataURL(img);
|
||||
}
|
||||
export default defineComponent({
|
||||
components: {
|
||||
LoadingOutlined,
|
||||
PlusOutlined,
|
||||
},
|
||||
setup() {
|
||||
const fileList = ref([]);
|
||||
const loading = ref<boolean>(false);
|
||||
const imageUrl = ref<string>('');
|
||||
|
||||
const handleChange = (info: UploadChangeParam) => {
|
||||
if (info.file.status === 'uploading') {
|
||||
loading.value = true;
|
||||
return;
|
||||
}
|
||||
if (info.file.status === 'done') {
|
||||
// Get this url from response in real world.
|
||||
getBase64(info.file.originFileObj, (base64Url: string) => {
|
||||
imageUrl.value = base64Url;
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
if (info.file.status === 'error') {
|
||||
loading.value = false;
|
||||
message.error('upload error');
|
||||
}
|
||||
};
|
||||
|
||||
const beforeUpload = (file: UploadProps['fileList'][number]) => {
|
||||
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
|
||||
if (!isJpgOrPng) {
|
||||
message.error('You can only upload JPG file!');
|
||||
}
|
||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||
if (!isLt2M) {
|
||||
message.error('Image must smaller than 2MB!');
|
||||
}
|
||||
return isJpgOrPng && isLt2M;
|
||||
};
|
||||
|
||||
return {
|
||||
fileList,
|
||||
loading,
|
||||
imageUrl,
|
||||
handleChange,
|
||||
beforeUpload,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
.avatar-uploader > .ant-upload {
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
}
|
||||
.ant-upload-select-picture-card i {
|
||||
font-size: 32px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.ant-upload-select-picture-card .ant-upload-text {
|
||||
margin-top: 8px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,65 @@
|
|||
<docs>
|
||||
---
|
||||
order: 0
|
||||
title:
|
||||
zh-CN: 点击上传
|
||||
en-US: Upload by clicking
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
经典款式,用户点击按钮弹出文件选择框。
|
||||
|
||||
## en-US
|
||||
|
||||
Classic mode. File selection dialog pops up when upload button is clicked.
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<a-upload
|
||||
v-model:file-list="fileList"
|
||||
name="file"
|
||||
:multiple="true"
|
||||
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
|
||||
:headers="headers"
|
||||
@change="handleChange"
|
||||
>
|
||||
<a-button>
|
||||
<upload-outlined></upload-outlined>
|
||||
Click to Upload
|
||||
</a-button>
|
||||
</a-upload>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { message } from 'ant-design-vue';
|
||||
import { UploadOutlined } from '@ant-design/icons-vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import type { UploadChangeParam } from 'ant-design-vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
UploadOutlined,
|
||||
},
|
||||
setup() {
|
||||
const handleChange = (info: UploadChangeParam) => {
|
||||
if (info.file.status !== 'uploading') {
|
||||
console.log(info.file, info.fileList);
|
||||
}
|
||||
if (info.file.status === 'done') {
|
||||
message.success(`${info.file.name} file uploaded successfully`);
|
||||
} else if (info.file.status === 'error') {
|
||||
message.error(`${info.file.name} file upload failed.`);
|
||||
}
|
||||
};
|
||||
|
||||
const fileList = ref([]);
|
||||
return {
|
||||
fileList,
|
||||
headers: {
|
||||
authorization: 'authorization-text',
|
||||
},
|
||||
handleChange,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,70 @@
|
|||
<docs>
|
||||
---
|
||||
order: 2
|
||||
title:
|
||||
zh-CN: 已上传的文件列表
|
||||
en-US: Default Files
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
使用 `defaultFileList` 设置已上传的内容。
|
||||
|
||||
## en-US
|
||||
|
||||
Use `defaultFileList` for uploaded files when page init.
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<a-upload v-model:file-list="fileList" action="https://www.mocky.io/v2/5cc8019d300000980a055e76">
|
||||
<a-button>
|
||||
<upload-outlined></upload-outlined>
|
||||
Upload
|
||||
</a-button>
|
||||
</a-upload>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { UploadOutlined } from '@ant-design/icons-vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import type { UploadChangeParam, UploadProps } from 'ant-design-vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
UploadOutlined,
|
||||
},
|
||||
setup() {
|
||||
const fileList = ref<UploadProps['fileList']>([
|
||||
{
|
||||
uid: '1',
|
||||
name: 'xxx.png',
|
||||
status: 'done',
|
||||
response: 'Server Error 500', // custom error message to show
|
||||
url: 'http://www.baidu.com/xxx.png',
|
||||
},
|
||||
{
|
||||
uid: '2',
|
||||
name: 'yyy.png',
|
||||
status: 'done',
|
||||
url: 'http://www.baidu.com/yyy.png',
|
||||
},
|
||||
{
|
||||
uid: '3',
|
||||
name: 'zzz.png',
|
||||
status: 'error',
|
||||
response: 'Server Error 500', // custom error message to show
|
||||
url: 'http://www.baidu.com/zzz.png',
|
||||
},
|
||||
]);
|
||||
|
||||
const handleChange = ({ file, fileList }: UploadChangeParam) => {
|
||||
if (file.status !== 'uploading') {
|
||||
console.log(file, fileList);
|
||||
}
|
||||
};
|
||||
return {
|
||||
fileList,
|
||||
handleChange,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,35 @@
|
|||
<docs>
|
||||
---
|
||||
order: 8
|
||||
title:
|
||||
zh-CN: 文件夹上传
|
||||
en-US: Upload directory
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
支持上传一个文件夹里的所有文件。
|
||||
|
||||
## en-US
|
||||
|
||||
You can select and upload a whole directory.
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<a-upload action="https://www.mocky.io/v2/5cc8019d300000980a055e76" directory>
|
||||
<a-button>
|
||||
<upload-outlined></upload-outlined>
|
||||
Upload Directory
|
||||
</a-button>
|
||||
</a-upload>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { UploadOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
UploadOutlined,
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,68 @@
|
|||
<docs>
|
||||
---
|
||||
order: 5
|
||||
title:
|
||||
zh-CN: 拖拽上传
|
||||
en-US: Drag and Drop
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
把文件拖入指定区域,完成上传,同样支持点击上传。
|
||||
|
||||
设置 `multiple` 后,在 `IE10+` 可以一次上传多个文件。
|
||||
|
||||
## en-US
|
||||
|
||||
You can drag files to a specific area, to upload. Alternatively, you can also upload by selecting.
|
||||
|
||||
We can upload serveral files at once in modern browsers by giving the input the `multiple` attribute.
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<a-upload-dragger
|
||||
v-model:fileList="fileList"
|
||||
name="file"
|
||||
:multiple="true"
|
||||
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
|
||||
@change="handleChange"
|
||||
>
|
||||
<p class="ant-upload-drag-icon">
|
||||
<inbox-outlined></inbox-outlined>
|
||||
</p>
|
||||
<p class="ant-upload-text">Click or drag file to this area to upload</p>
|
||||
<p class="ant-upload-hint">
|
||||
Support for a single or bulk upload. Strictly prohibit from uploading company data or other
|
||||
band files
|
||||
</p>
|
||||
</a-upload-dragger>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { InboxOutlined } from '@ant-design/icons-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import type { UploadChangeParam } from 'ant-design-vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
InboxOutlined,
|
||||
},
|
||||
setup() {
|
||||
const handleChange = (info: UploadChangeParam) => {
|
||||
const status = info.file.status;
|
||||
if (status !== 'uploading') {
|
||||
console.log(info.file, info.fileList);
|
||||
}
|
||||
if (status === 'done') {
|
||||
message.success(`${info.file.name} file uploaded successfully.`);
|
||||
} else if (status === 'error') {
|
||||
message.error(`${info.file.name} file upload failed.`);
|
||||
}
|
||||
};
|
||||
return {
|
||||
handleChange,
|
||||
fileList: ref([]),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,81 @@
|
|||
<docs>
|
||||
---
|
||||
order: 4
|
||||
title:
|
||||
zh-CN: 完全控制的上传列表
|
||||
en-US: Complete control over file list
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
使用 `fileList` 对列表进行完全控制,可以实现各种自定义功能,以下演示二种情况:
|
||||
|
||||
1. 上传列表数量的限制。
|
||||
|
||||
2. 读取远程路径并显示链接。
|
||||
|
||||
## en-US
|
||||
|
||||
You can gain full control over filelist by configuring `fileList`. You can accomplish all kinds of customed functions. The following shows two circumstances:
|
||||
|
||||
1. limit the number of uploaded files.
|
||||
|
||||
2. read from response and show file link.
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<a-upload
|
||||
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
|
||||
:multiple="true"
|
||||
:file-list="fileList"
|
||||
@change="handleChange"
|
||||
>
|
||||
<a-button>
|
||||
<upload-outlined></upload-outlined>
|
||||
Upload
|
||||
</a-button>
|
||||
</a-upload>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { UploadOutlined } from '@ant-design/icons-vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import type { UploadChangeParam, UploadProps } from 'ant-design-vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
UploadOutlined,
|
||||
},
|
||||
setup() {
|
||||
const fileList = ref<UploadProps['fileList']>([
|
||||
{
|
||||
uid: '-1',
|
||||
name: 'xxx.png',
|
||||
status: 'done',
|
||||
url: 'http://www.baidu.com/xxx.png',
|
||||
},
|
||||
]);
|
||||
const handleChange = (info: UploadChangeParam) => {
|
||||
let resFileList = [...info.fileList];
|
||||
|
||||
// 1. Limit the number of uploaded files
|
||||
// Only to show two recent uploaded files, and old ones will be replaced by the new
|
||||
resFileList = resFileList.slice(-2);
|
||||
|
||||
// 2. read from response and show file link
|
||||
resFileList = resFileList.map(file => {
|
||||
if (file.response) {
|
||||
// Component will show file.url as link
|
||||
file.url = file.response.url;
|
||||
}
|
||||
return file;
|
||||
});
|
||||
|
||||
fileList.value = resFileList;
|
||||
};
|
||||
return {
|
||||
fileList,
|
||||
handleChange,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,52 @@
|
|||
<template>
|
||||
<demo-sort>
|
||||
<Basic />
|
||||
<Avatar />
|
||||
<DefaultFileList />
|
||||
<PictureCard />
|
||||
<FileList />
|
||||
<Drag />
|
||||
<PictureStyle />
|
||||
<UploadManually />
|
||||
<Directory />
|
||||
<PreviewFile />
|
||||
<TransformFile />
|
||||
</demo-sort>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import Basic from './basic.vue';
|
||||
import Avatar from './avatar.vue';
|
||||
import DefaultFileList from './defaultFileList.vue';
|
||||
import PictureCard from './picture-card.vue';
|
||||
import FileList from './fileList.vue';
|
||||
import Drag from './drag.vue';
|
||||
import PictureStyle from './picture-style.vue';
|
||||
import UploadManually from './upload-manually.vue';
|
||||
import Directory from './directory.vue';
|
||||
import PreviewFile from './preview-file.vue';
|
||||
import TransformFile from './transform-file.vue';
|
||||
import CN from '../index.zh-CN.md';
|
||||
import US from '../index.en-US.md';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
CN,
|
||||
US,
|
||||
components: {
|
||||
Basic,
|
||||
Avatar,
|
||||
DefaultFileList,
|
||||
PictureCard,
|
||||
FileList,
|
||||
Drag,
|
||||
PictureStyle,
|
||||
UploadManually,
|
||||
Directory,
|
||||
PreviewFile,
|
||||
TransformFile,
|
||||
},
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,126 @@
|
|||
<docs>
|
||||
---
|
||||
order: 3
|
||||
title:
|
||||
zh-CN: 照片墙
|
||||
en-US: Pictures Wall
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
用户可以上传图片并在列表中显示缩略图。当上传照片数到达限制后,上传按钮消失。
|
||||
|
||||
## en-US
|
||||
|
||||
After users upload picture, the thumbnail will be shown in list. The upload button will disappear when count meets limitation.
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<div class="clearfix">
|
||||
<a-upload
|
||||
v-model:file-list="fileList"
|
||||
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
|
||||
list-type="picture-card"
|
||||
@preview="handlePreview"
|
||||
>
|
||||
<div v-if="fileList.length < 8">
|
||||
<plus-outlined />
|
||||
<div class="ant-upload-text">Upload</div>
|
||||
</div>
|
||||
</a-upload>
|
||||
<a-modal :visible="previewVisible" :footer="null" @cancel="handleCancel">
|
||||
<img alt="example" style="width: 100%" :src="previewImage" />
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import type { UploadChangeParam, UploadProps } from 'ant-design-vue';
|
||||
|
||||
function getBase64(file: File) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => resolve(reader.result);
|
||||
reader.onerror = error => reject(error);
|
||||
});
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
PlusOutlined,
|
||||
},
|
||||
setup() {
|
||||
const previewVisible = ref<boolean>(false);
|
||||
const previewImage = ref<string | undefined>('');
|
||||
|
||||
const fileList = ref<UploadProps['fileList']>([
|
||||
{
|
||||
uid: '-1',
|
||||
name: 'image.png',
|
||||
status: 'done',
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
},
|
||||
{
|
||||
uid: '-2',
|
||||
name: 'image.png',
|
||||
status: 'done',
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
},
|
||||
{
|
||||
uid: '-3',
|
||||
name: 'image.png',
|
||||
status: 'done',
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
},
|
||||
{
|
||||
uid: '-4',
|
||||
name: 'image.png',
|
||||
status: 'done',
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
},
|
||||
{
|
||||
uid: '-5',
|
||||
name: 'image.png',
|
||||
status: 'error',
|
||||
},
|
||||
]);
|
||||
|
||||
const handleCancel = () => {
|
||||
previewVisible.value = false;
|
||||
};
|
||||
const handlePreview = async (file: UploadProps['fileList'][number]) => {
|
||||
if (!file.url && !file.preview) {
|
||||
file.preview = (await getBase64(file.originFileObj)) as string;
|
||||
}
|
||||
previewImage.value = file.url || file.preview;
|
||||
previewVisible.value = true;
|
||||
};
|
||||
const handleChange = ({ fileList: newFileList }: UploadChangeParam) => {
|
||||
fileList.value = newFileList;
|
||||
};
|
||||
|
||||
return {
|
||||
previewVisible,
|
||||
previewImage,
|
||||
fileList,
|
||||
handleCancel,
|
||||
handlePreview,
|
||||
handleChange,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
/* you can make up upload button and sample style by using stylesheets */
|
||||
.ant-upload-select-picture-card i {
|
||||
font-size: 32px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.ant-upload-select-picture-card .ant-upload-text {
|
||||
margin-top: 8px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,108 @@
|
|||
<docs>
|
||||
---
|
||||
order: 6
|
||||
title:
|
||||
zh-CN: 图片列表样式
|
||||
en-US: Pictures with list style
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
上传文件为图片,可展示本地缩略图。`IE8/9` 不支持浏览器本地缩略图展示([Ref](https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL)),可以写 `thumbUrl` 属性来代替。
|
||||
|
||||
## en-US
|
||||
|
||||
If uploaded file is a picture, the thumbnail can be shown. `IE8/9` do not support local thumbnail show. Please use `thumbUrl` instead.
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<a-upload
|
||||
v-model:file-list="fileList"
|
||||
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
|
||||
list-type="picture"
|
||||
>
|
||||
<a-button>
|
||||
<upload-outlined></upload-outlined>
|
||||
upload
|
||||
</a-button>
|
||||
</a-upload>
|
||||
<br />
|
||||
<br />
|
||||
<a-upload
|
||||
v-model:file-list="fileList1"
|
||||
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
|
||||
list-type="picture"
|
||||
class="upload-list-inline"
|
||||
>
|
||||
<a-button>
|
||||
<upload-outlined></upload-outlined>
|
||||
upload
|
||||
</a-button>
|
||||
</a-upload>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { UploadOutlined } from '@ant-design/icons-vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import type { UploadProps } from 'ant-design-vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
UploadOutlined,
|
||||
},
|
||||
setup() {
|
||||
const fileList = ref<UploadProps['fileList']>([
|
||||
{
|
||||
uid: '-1',
|
||||
name: 'xxx.png',
|
||||
status: 'done',
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
},
|
||||
{
|
||||
uid: '-2',
|
||||
name: 'yyy.png',
|
||||
status: 'done',
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
},
|
||||
]);
|
||||
const fileList1 = ref<UploadProps['fileList']>([
|
||||
{
|
||||
uid: '-1',
|
||||
name: 'xxx.png',
|
||||
status: 'done',
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
},
|
||||
{
|
||||
uid: '-2',
|
||||
name: 'yyy.png',
|
||||
status: 'done',
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
},
|
||||
]);
|
||||
return {
|
||||
fileList,
|
||||
fileList1,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
/* tile uploaded pictures */
|
||||
.upload-list-inline :deep(.ant-upload-list-item) {
|
||||
float: left;
|
||||
width: 200px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.upload-list-inline :deep(.ant-upload-animate-enter) {
|
||||
animation-name: uploadAnimateInlineIn;
|
||||
}
|
||||
.upload-list-inline :deep(.ant-upload-animate-leave) {
|
||||
animation-name: uploadAnimateInlineOut;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,58 @@
|
|||
<docs>
|
||||
---
|
||||
order: 9
|
||||
title:
|
||||
zh-CN: 自定义预览
|
||||
en-US: Customize preview file
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
自定义本地预览,用于处理非图片格式文件(例如视频文件)。
|
||||
|
||||
## en-US
|
||||
|
||||
Customize local preview. Can handle with non-image format files such as video.
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<a-upload
|
||||
v-model:file-list="fileList"
|
||||
list-type="picture"
|
||||
action="//jsonplaceholder.typicode.com/posts/"
|
||||
:preview-file="previewFile"
|
||||
>
|
||||
<a-button>
|
||||
<upload-outlined></upload-outlined>
|
||||
Upload
|
||||
</a-button>
|
||||
</a-upload>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { UploadOutlined } from '@ant-design/icons-vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
UploadOutlined,
|
||||
},
|
||||
setup() {
|
||||
const previewFile = async (file: any): Promise<Response> => {
|
||||
console.log('Your upload file:', file);
|
||||
// Your process logic. Here we just mock to the same file
|
||||
const res = await fetch('https://next.json-generator.com/api/json/get/4ytyBoLK8', {
|
||||
method: 'POST',
|
||||
body: file,
|
||||
});
|
||||
const { thumbnail } = await res.json();
|
||||
return thumbnail;
|
||||
};
|
||||
return {
|
||||
previewFile,
|
||||
fileList: ref([]),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,66 @@
|
|||
<docs>
|
||||
---
|
||||
order: 10
|
||||
title:
|
||||
zh-CN: 上传前转换文件
|
||||
en-US: Transform file before request
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
使用 `beforeUpload` 转换上传的文件(例如添加水印)。
|
||||
|
||||
## en-US
|
||||
|
||||
Use `beforeUpload` for transform file before request such as add a watermark.
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<a-upload
|
||||
v-model:file-list="fileList"
|
||||
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
|
||||
:transform-file="transformFile"
|
||||
>
|
||||
<a-button>
|
||||
<upload-outlined></upload-outlined>
|
||||
Upload
|
||||
</a-button>
|
||||
</a-upload>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { UploadOutlined } from '@ant-design/icons-vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
UploadOutlined,
|
||||
},
|
||||
setup() {
|
||||
const transformFile = (file: any) => {
|
||||
return new Promise(resolve => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const img: HTMLImageElement = document.createElement('img');
|
||||
img.src = reader.result as string;
|
||||
img.onload = () => {
|
||||
const ctx: CanvasRenderingContext2D = canvas.getContext('2d')!;
|
||||
ctx.drawImage(img, 0, 0);
|
||||
ctx.fillStyle = 'red';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText('Ant Design', 20, 20);
|
||||
canvas.toBlob(resolve);
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
return {
|
||||
transformFile,
|
||||
fileList: ref([]),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,96 @@
|
|||
<docs>
|
||||
---
|
||||
order: 7
|
||||
title:
|
||||
zh-CN: 手动上传
|
||||
en-US: Upload manually
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
`beforeUpload` 返回 `false` 后,手动上传文件。
|
||||
|
||||
## en-US
|
||||
|
||||
Upload files manually after `beforeUpload` returns `false`.
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<div class="clearfix">
|
||||
<a-upload :file-list="fileList" :remove="handleRemove" :before-upload="beforeUpload">
|
||||
<a-button>
|
||||
<upload-outlined></upload-outlined>
|
||||
Select File
|
||||
</a-button>
|
||||
</a-upload>
|
||||
<a-button
|
||||
type="primary"
|
||||
:disabled="fileList.length === 0"
|
||||
:loading="uploading"
|
||||
style="margin-top: 16px"
|
||||
@click="handleUpload"
|
||||
>
|
||||
{{ uploading ? 'Uploading' : 'Start Upload' }}
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import request from 'umi-request';
|
||||
import { UploadOutlined } from '@ant-design/icons-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import type { UploadProps } from 'ant-design-vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
UploadOutlined,
|
||||
},
|
||||
setup() {
|
||||
const fileList = ref<UploadProps['fileList']>([]);
|
||||
const uploading = ref<boolean>(false);
|
||||
|
||||
const handleRemove: UploadProps['onRemove'] = file => {
|
||||
const index = fileList.value.indexOf(file);
|
||||
const newFileList = fileList.value.slice();
|
||||
newFileList.splice(index, 1);
|
||||
fileList.value = newFileList;
|
||||
};
|
||||
|
||||
const beforeUpload: UploadProps['beforeUpload'] = file => {
|
||||
fileList.value = [...fileList.value, file];
|
||||
return false;
|
||||
};
|
||||
|
||||
const handleUpload = () => {
|
||||
const formData = new FormData();
|
||||
fileList.value.forEach((file: UploadProps['fileList'][number]) => {
|
||||
formData.append('files[]', file as any);
|
||||
});
|
||||
uploading.value = true;
|
||||
|
||||
// You can use any AJAX library you like
|
||||
request('https://www.mocky.io/v2/5cc8019d300000980a055e76', {
|
||||
method: 'post',
|
||||
data: formData,
|
||||
})
|
||||
.then(() => {
|
||||
fileList.value = [];
|
||||
uploading.value = false;
|
||||
message.success('upload successfully.');
|
||||
})
|
||||
.catch(() => {
|
||||
uploading.value = false;
|
||||
message.error('upload failed.');
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
fileList,
|
||||
uploading,
|
||||
handleRemove,
|
||||
beforeUpload,
|
||||
handleUpload,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,80 @@
|
|||
---
|
||||
category: Components
|
||||
type: Data Entry
|
||||
title: Upload
|
||||
cover: https://gw.alipayobjects.com/zos/alicdn/QaeBt_ZMg/Upload.svg
|
||||
---
|
||||
|
||||
Upload file by selecting or dragging.
|
||||
|
||||
## When To Use
|
||||
|
||||
Uploading is the process of publishing information (web pages, text, pictures, video, etc.) to a remote server via a web page or upload tool.
|
||||
|
||||
- When you need to upload one or more files.
|
||||
- When you need to show the process of uploading.
|
||||
- When you need to upload files by dragging and dropping.
|
||||
|
||||
## API
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| accept | File types that can be accepted. See [input accept Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) | string | - | |
|
||||
| action | Uploading URL | string\|(file) => `Promise` | - | |
|
||||
| method | http method of upload request | string | `post` | 1.5.0 |
|
||||
| directory | support upload whole directory ([caniuse](https://caniuse.com/#feat=input-file-directory)) | boolean | false | |
|
||||
| beforeUpload | Hook function which will be executed before uploading. Uploading will be stopped with `false` or a rejected Promise returned. **Warning:this function is not supported in IE9**。 | (file, fileList) => `boolean | Promise` | - | |
|
||||
| customRequest | override for the default xhr behavior allowing for additional customization and ability to implement your own XMLHttpRequest | Function | - | |
|
||||
| data | Uploading params or function which can return uploading params. | object\|function(file) | - | |
|
||||
| disabled | disable upload button | boolean | false | |
|
||||
| fileList | List of files that have been uploaded (controlled). Here is a common issue [#2423](https://github.com/ant-design/ant-design/issues/2423) when using it | object\[] | - | |
|
||||
| headers | Set request headers, valid above IE10. | object | - | |
|
||||
| listType | Built-in stylesheets, support for three types: `text`, `picture` or `picture-card` | string | `text` | |
|
||||
| multiple | Whether to support selected multiple file. `IE10+` supported. You can select multiple files with CTRL holding down while multiple is set to be true | boolean | false | |
|
||||
| name | The name of uploading file | string | `file` | |
|
||||
| previewFile | Customize preview file logic | (file: File \| Blob) => Promise<dataURL: string> | - | 1.5.0 |
|
||||
| showUploadList | Whether to show default upload list, could be an object to specify `showPreviewIcon` and `showRemoveIcon` individually | Boolean or { showPreviewIcon?: boolean, showRemoveIcon?: boolean } | true | |
|
||||
| supportServerRender | Need to be turned on while the server side is rendering. | boolean | false | |
|
||||
| withCredentials | ajax upload with cookie sent | boolean | false | |
|
||||
| openFileDialogOnClick | click open file dialog | boolean | true | |
|
||||
| remove | A callback function, will be executed when removing file button is clicked, remove event will be prevented when return value is `false` or a Promise which resolve(false) or reject. | Function(file): `boolean | Promise` | - | |
|
||||
| transformFile | Customize transform file before request | Function(file): `string | Blob | File | Promise<string | Blob | File>` | - | 1.5.0 |
|
||||
|
||||
### events
|
||||
|
||||
| Events Name | Description | Arguments | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| change | A callback function, can be executed when uploading state is changing. See [change](#change) | Function | - | |
|
||||
| preview | A callback function, will be executed when file link or preview icon is clicked. | Function(file) | - | |
|
||||
| download | Click the method to download the file, pass the method to perform the method logic, do not pass the default jump to the new TAB. | Function(file): void | Jump to new TAB | 1.5.0 |
|
||||
| reject | A callback function, will be executed when drop files is not accept. | Function(fileList) | - | |
|
||||
|
||||
### change
|
||||
|
||||
> The function will be called when uploading is in progress, completed or failed
|
||||
|
||||
When uploading state change, it returns:
|
||||
|
||||
```jsx
|
||||
{
|
||||
file: { /* ... */ },
|
||||
fileList: [ /* ... */ ],
|
||||
event: { /* ... */ },
|
||||
}
|
||||
```
|
||||
|
||||
1. `file` File object for the current operation.
|
||||
|
||||
```jsx
|
||||
{
|
||||
uid: 'uid', // unique identifier, negative is recommend, to prevent interference with internal generated id
|
||||
name: 'xx.png', // file name
|
||||
status: 'done', // options:uploading, done, error, removed
|
||||
response: '{"status": "success"}', // response from server
|
||||
linkProps: '{"download": "image"}', // additional html props of file link
|
||||
xhr: 'XMLHttpRequest{ ... }', // XMLHttpRequest Header
|
||||
}
|
||||
```
|
||||
|
||||
2. `fileList` current list of files
|
||||
3. `event` response from server, including uploading progress, supported by advanced browsers.
|
|
@ -0,0 +1,17 @@
|
|||
import type { App } from 'vue';
|
||||
import Upload from './Upload';
|
||||
import Dragger from './Dragger';
|
||||
|
||||
export type { UploadProps, UploadListProps, UploadChangeParam } from './interface';
|
||||
|
||||
/* istanbul ignore next */
|
||||
export const UploadDragger = Dragger;
|
||||
|
||||
export default Object.assign(Upload, {
|
||||
Dragger,
|
||||
install(app: App) {
|
||||
app.component(Upload.name, Upload);
|
||||
app.component(Dragger.name, Dragger);
|
||||
return app;
|
||||
},
|
||||
});
|
|
@ -0,0 +1,81 @@
|
|||
---
|
||||
category: Components
|
||||
subtitle: 上传
|
||||
type: 数据录入
|
||||
title: Upload
|
||||
cover: https://gw.alipayobjects.com/zos/alicdn/QaeBt_ZMg/Upload.svg
|
||||
---
|
||||
|
||||
文件选择上传和拖拽上传控件。
|
||||
|
||||
## 何时使用
|
||||
|
||||
上传是将信息(网页、文字、图片、视频等)通过网页或者上传工具发布到远程服务器上的过程。
|
||||
|
||||
- 当需要上传一个或一些文件时。
|
||||
- 当需要展现上传的进度时。
|
||||
- 当需要使用拖拽交互时。
|
||||
|
||||
## API
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| accept | 接受上传的文件类型, 详见 [input accept Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) | string | 无 | |
|
||||
| action | 上传的地址 | string\|(file) => `Promise` | 无 | |
|
||||
| method | 上传请求的 http method | string | `post` | 1.5.0 |
|
||||
| directory | 支持上传文件夹([caniuse](https://caniuse.com/#feat=input-file-directory)) | boolean | false | |
|
||||
| beforeUpload | 上传文件之前的钩子,参数为上传的文件,若返回 `false` 则停止上传。支持返回一个 Promise 对象,Promise 对象 reject 时则停止上传,resolve 时开始上传( resolve 传入 `File` 或 `Blob` 对象则上传 resolve 传入对象)。**注意:IE9 不支持该方法**。 | (file, fileList) => `boolean | Promise` | 无 | |
|
||||
| customRequest | 通过覆盖默认的上传行为,可以自定义自己的上传实现 | Function | 无 | |
|
||||
| data | 上传所需参数或返回上传参数的方法 | object\|(file) => object | 无 | |
|
||||
| disabled | 是否禁用 | boolean | false | |
|
||||
| fileList | 已经上传的文件列表(受控) | object\[] | 无 | |
|
||||
| headers | 设置上传的请求头部,IE10 以上有效 | object | 无 | |
|
||||
| listType | 上传列表的内建样式,支持三种基本样式 `text`, `picture` 和 `picture-card` | string | `text` | |
|
||||
| multiple | 是否支持多选文件,`ie10+` 支持。开启后按住 ctrl 可选择多个文件。 | boolean | false | |
|
||||
| name | 发到后台的文件参数名 | string | `file` | |
|
||||
| previewFile | 自定义文件预览逻辑 | (file: File \| Blob) => Promise<dataURL: string> | 无 | 1.5.0 |
|
||||
| showUploadList | 是否展示 uploadList, 可设为一个对象,用于单独设定 showPreviewIcon 和 showRemoveIcon | Boolean or { showPreviewIcon?: boolean, showRemoveIcon?: boolean } | true | |
|
||||
| supportServerRender | 服务端渲染时需要打开这个 | boolean | false | |
|
||||
| withCredentials | 上传请求时是否携带 cookie | boolean | false | |
|
||||
| openFileDialogOnClick | 点击打开文件对话框 | boolean | true | |
|
||||
| remove | 点击移除文件时的回调,返回值为 false 时不移除。支持返回一个 Promise 对象,Promise 对象 resolve(false) 或 reject 时不移除。 | Function(file): `boolean | Promise` | 无 | |
|
||||
| transformFile | 在上传之前转换文件。支持返回一个 Promise 对象 | Function(file): `string | Blob | File | Promise<string | Blob | File>` | 无 | 1.5.0 |
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件名称 | 说明 | 回调参数 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| change | 上传文件改变时的状态,详见 [change](#change) | Function | 无 | |
|
||||
| preview | 点击文件链接或预览图标时的回调 | Function(file) | 无 | |
|
||||
| download | 点击下载文件时的回调,如果没有指定,则默认跳转到文件 url 对应的标签页。 | Function(file): void | 跳转新标签页 | 1.5.0 |
|
||||
| reject | 拖拽文件不符合 accept 类型时的回调 | Function(fileList) | 无 | |
|
||||
|
||||
### change
|
||||
|
||||
> 上传中、完成、失败都会调用这个函数。
|
||||
|
||||
文件状态改变的回调,返回为:
|
||||
|
||||
```jsx
|
||||
{
|
||||
file: { /* ... */ },
|
||||
fileList: [ /* ... */ ],
|
||||
event: { /* ... */ },
|
||||
}
|
||||
```
|
||||
|
||||
1. `file` 当前操作的文件对象。
|
||||
|
||||
```jsx
|
||||
{
|
||||
uid: 'uid', // 文件唯一标识,建议设置为负数,防止和内部产生的 id 冲突
|
||||
name: 'xx.png', // 文件名
|
||||
status: 'done', // 状态有:uploading done error removed
|
||||
response: '{"status": "success"}', // 服务端响应内容
|
||||
linkProps: '{"download": "image"}', // 下载链接额外的 HTML 属性
|
||||
xhr: 'XMLHttpRequest{ ... }', // XMLHttpRequest Header
|
||||
}
|
||||
```
|
||||
|
||||
2. `fileList` 当前的文件列表。
|
||||
3. `event` 上传中的服务端响应内容,包含了上传进度等信息,高级浏览器支持。
|
|
@ -0,0 +1,183 @@
|
|||
import type {
|
||||
RcFile as OriRcFile,
|
||||
UploadRequestOption as RcCustomRequestOptions,
|
||||
} from '../vc-upload/interface';
|
||||
import type { ProgressProps } from '../progress';
|
||||
import type { VueNode } from '../_util/type';
|
||||
import type { ExtractPropTypes, PropType } from 'vue';
|
||||
|
||||
export interface RcFile extends OriRcFile {
|
||||
readonly lastModifiedDate: Date;
|
||||
}
|
||||
|
||||
export type UploadFileStatus = 'error' | 'success' | 'done' | 'uploading' | 'removed';
|
||||
|
||||
export interface HttpRequestHeader {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export interface UploadFile<T = any> {
|
||||
uid: string;
|
||||
size?: number;
|
||||
name: string;
|
||||
fileName?: string;
|
||||
lastModified?: number;
|
||||
lastModifiedDate?: Date;
|
||||
url?: string;
|
||||
status?: UploadFileStatus;
|
||||
percent?: number;
|
||||
thumbUrl?: string;
|
||||
originFileObj?: RcFile;
|
||||
response?: T;
|
||||
error?: any;
|
||||
linkProps?: any;
|
||||
type?: string;
|
||||
xhr?: T;
|
||||
preview?: string;
|
||||
}
|
||||
|
||||
export interface InternalUploadFile<T = any> extends UploadFile<T> {
|
||||
originFileObj: RcFile;
|
||||
}
|
||||
|
||||
export interface UploadChangeParam<T = UploadFile> {
|
||||
// https://github.com/ant-design/ant-design/issues/14420
|
||||
file: T;
|
||||
fileList: UploadFile[];
|
||||
event?: { percent: number };
|
||||
}
|
||||
|
||||
// export interface ShowUploadListInterface {
|
||||
// showRemoveIcon?: boolean;
|
||||
// showPreviewIcon?: boolean;
|
||||
// showDownloadIcon?: boolean;
|
||||
// removeIcon?: VueNode | ((file: UploadFile) => VueNode);
|
||||
// downloadIcon?: VueNode | ((file: UploadFile) => VueNode);
|
||||
// previewIcon?: VueNode | ((file: UploadFile) => VueNode);
|
||||
// }
|
||||
|
||||
export interface UploadLocale {
|
||||
uploading?: string;
|
||||
removeFile?: string;
|
||||
downloadFile?: string;
|
||||
uploadError?: string;
|
||||
previewFile?: string;
|
||||
}
|
||||
|
||||
export type UploadType = 'drag' | 'select';
|
||||
export type UploadListType = 'text' | 'picture' | 'picture-card';
|
||||
export type UploadListProgressProps = Omit<ProgressProps, 'percent' | 'type'>;
|
||||
|
||||
export type ItemRender<T = any> = (opt: {
|
||||
originNode: VueNode;
|
||||
file: UploadFile;
|
||||
fileList: Array<UploadFile<T>>;
|
||||
actions: {
|
||||
download: () => void;
|
||||
preview: () => void;
|
||||
remove: () => void;
|
||||
};
|
||||
}) => VueNode;
|
||||
|
||||
type PreviewFileHandler = (file: File | Blob) => PromiseLike<string>;
|
||||
type TransformFileHandler = (
|
||||
file: RcFile,
|
||||
) => string | Blob | File | PromiseLike<string | Blob | File>;
|
||||
type BeforeUploadValueType = void | boolean | string | Blob | File;
|
||||
|
||||
function uploadProps<T = any>() {
|
||||
return {
|
||||
capture: [Boolean, String] as PropType<boolean | 'user' | 'environment'>,
|
||||
type: String as PropType<UploadType>,
|
||||
name: String,
|
||||
defaultFileList: Array as PropType<Array<UploadFile<T>>>,
|
||||
fileList: Array as PropType<Array<UploadFile<T>>>,
|
||||
action: [String, Function] as PropType<
|
||||
string | ((file: RcFile) => string) | ((file: RcFile) => PromiseLike<string>)
|
||||
>,
|
||||
directory: Boolean,
|
||||
data: [Object, Function] as PropType<
|
||||
| Record<string, unknown>
|
||||
| ((file: UploadFile<T>) => Record<string, unknown> | Promise<Record<string, unknown>>)
|
||||
>,
|
||||
method: String as PropType<'POST' | 'PUT' | 'PATCH' | 'post' | 'put' | 'patch'>,
|
||||
headers: Object as PropType<HttpRequestHeader>,
|
||||
showUploadList: Boolean,
|
||||
multiple: Boolean,
|
||||
accept: String,
|
||||
beforeUpload: Function as PropType<
|
||||
(file: RcFile, FileList: RcFile[]) => BeforeUploadValueType | Promise<BeforeUploadValueType>
|
||||
>,
|
||||
onChange: Function as PropType<(info: UploadChangeParam<T>) => void>,
|
||||
onDrop: Function as PropType<(event: DragEvent) => void>,
|
||||
listType: String as PropType<UploadListType>,
|
||||
onPreview: Function as PropType<(file: UploadFile<T>) => void>,
|
||||
onDownload: Function as PropType<(file: UploadFile<T>) => void>,
|
||||
onRemove: Function as PropType<
|
||||
(file: UploadFile<T>) => void | boolean | Promise<void | boolean>
|
||||
>,
|
||||
supportServerRender: Boolean,
|
||||
disabled: Boolean,
|
||||
prefixCls: String,
|
||||
customRequest: Function as PropType<(options: RcCustomRequestOptions) => void>,
|
||||
withCredentials: Boolean,
|
||||
openFileDialogOnClick: Boolean,
|
||||
locale: Object as PropType<UploadLocale>,
|
||||
id: String,
|
||||
previewFile: Function as PropType<PreviewFileHandler>,
|
||||
/** @deprecated Please use `beforeUpload` directly */
|
||||
transformFile: Function as PropType<TransformFileHandler>,
|
||||
iconRender: Function as PropType<
|
||||
(opt: { file: UploadFile<T>; listType?: UploadListType }) => VueNode
|
||||
>,
|
||||
isImageUrl: Function as PropType<(file: UploadFile) => boolean>,
|
||||
progress: Object as PropType<UploadListProgressProps>,
|
||||
itemRender: Function as PropType<ItemRender<T>>,
|
||||
/** Config max count of `fileList`. Will replace current one when `maxCount` is 1 */
|
||||
maxCount: Number,
|
||||
height: [Number, String],
|
||||
|
||||
showRemoveIcon: Boolean,
|
||||
showDownloadIcon: Boolean,
|
||||
showPreviewIcon: Boolean,
|
||||
removeIcon: Function as PropType<(opt: { file: UploadFile }) => VueNode>,
|
||||
downloadIcon: Function as PropType<(opt: { file: UploadFile }) => VueNode>,
|
||||
previewIcon: Function as PropType<(opt: { file: UploadFile }) => VueNode>,
|
||||
};
|
||||
}
|
||||
|
||||
export type UploadProps = Partial<ExtractPropTypes<ReturnType<typeof uploadProps>>>;
|
||||
|
||||
export interface UploadState<T = any> {
|
||||
fileList: UploadFile<T>[];
|
||||
dragState: string;
|
||||
}
|
||||
|
||||
function uploadListProps<T = any>() {
|
||||
return {
|
||||
listType: String as PropType<UploadListType>,
|
||||
onPreview: Function as PropType<(file: UploadFile<T>) => void>,
|
||||
onDownload: Function as PropType<(file: UploadFile<T>) => void>,
|
||||
onRemove: Function as PropType<(file: UploadFile<T>) => void | boolean>,
|
||||
items: Array as PropType<Array<UploadFile<T>>>,
|
||||
progress: Object as PropType<UploadListProgressProps>,
|
||||
prefixCls: String as PropType<string>,
|
||||
showRemoveIcon: Boolean,
|
||||
showDownloadIcon: Boolean,
|
||||
showPreviewIcon: Boolean,
|
||||
removeIcon: Function as PropType<(opt: { file: UploadFile }) => VueNode>,
|
||||
downloadIcon: Function as PropType<(opt: { file: UploadFile }) => VueNode>,
|
||||
previewIcon: Function as PropType<(opt: { file: UploadFile }) => VueNode>,
|
||||
locale: Object as PropType<UploadLocale>,
|
||||
previewFile: Function as PropType<PreviewFileHandler>,
|
||||
iconRender: Function as PropType<
|
||||
(opt: { file: UploadFile<T>; listType?: UploadListType }) => VueNode
|
||||
>,
|
||||
isImageUrl: Function as PropType<(file: UploadFile) => boolean>,
|
||||
appendAction: Function as PropType<() => VueNode>,
|
||||
itemRender: Function as PropType<ItemRender<T>>,
|
||||
};
|
||||
}
|
||||
|
||||
export type UploadListProps = Partial<ExtractPropTypes<ReturnType<typeof uploadListProps>>>;
|
||||
export { uploadProps, uploadListProps };
|
|
@ -0,0 +1,589 @@
|
|||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
|
||||
@upload-prefix-cls: ~'@{ant-prefix}-upload';
|
||||
@upload-item: ~'@{ant-prefix}-upload-list-item';
|
||||
@upload-picture-card-size: 104px;
|
||||
@upload-picture-card-border-style: @border-style-base;
|
||||
|
||||
.@{upload-prefix-cls} {
|
||||
.reset-component();
|
||||
|
||||
outline: 0;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&-btn {
|
||||
display: block;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type='file'] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&&-select {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&&-disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&&-select-picture-card {
|
||||
width: @upload-picture-card-size;
|
||||
height: @upload-picture-card-size;
|
||||
margin-right: 8px;
|
||||
margin-bottom: 8px;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
background-color: @background-color-light;
|
||||
border: @border-width-base dashed @border-color-base;
|
||||
border-radius: @border-radius-base;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.3s;
|
||||
|
||||
> .@{upload-prefix-cls} {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: @primary-color;
|
||||
.@{upload-prefix-cls}-disabled& {
|
||||
border-color: @border-color-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&&-drag {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
background: @background-color-light;
|
||||
border: @border-width-base dashed @border-color-base;
|
||||
border-radius: @border-radius-base;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.3s;
|
||||
|
||||
.@{upload-prefix-cls} {
|
||||
padding: @padding-md 0;
|
||||
}
|
||||
|
||||
&.@{upload-prefix-cls}-drag-hover:not(.@{upload-prefix-cls}-disabled) {
|
||||
border-color: @primary-7;
|
||||
}
|
||||
|
||||
&.@{upload-prefix-cls}-disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.@{upload-prefix-cls}-btn {
|
||||
display: table;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.@{upload-prefix-cls}-drag-container {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&:not(.@{upload-prefix-cls}-disabled):hover {
|
||||
border-color: @primary-5;
|
||||
}
|
||||
|
||||
p.@{upload-prefix-cls}-drag-icon {
|
||||
.@{iconfont-css-prefix} {
|
||||
color: @primary-5;
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
p.@{upload-prefix-cls}-text {
|
||||
margin: 0 0 4px;
|
||||
color: @heading-color;
|
||||
font-size: @font-size-lg;
|
||||
}
|
||||
p.@{upload-prefix-cls}-hint {
|
||||
color: @text-color-secondary;
|
||||
font-size: @font-size-base;
|
||||
}
|
||||
|
||||
.@{iconfont-css-prefix}-plus {
|
||||
color: @disabled-color;
|
||||
font-size: 30px;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
color: @text-color-secondary;
|
||||
}
|
||||
}
|
||||
&:hover .@{iconfont-css-prefix}-plus {
|
||||
color: @text-color-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
&-picture-card-wrapper {
|
||||
.clearfix();
|
||||
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.@{upload-prefix-cls}-list {
|
||||
.reset-component();
|
||||
.clearfix();
|
||||
line-height: @line-height-base;
|
||||
|
||||
// ============================ Item ============================
|
||||
&-item {
|
||||
position: relative;
|
||||
height: @line-height-base * @font-size-base;
|
||||
margin-top: @margin-xs;
|
||||
font-size: @font-size-base;
|
||||
|
||||
&-name {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
padding-left: @font-size-base + 8px;
|
||||
overflow: hidden;
|
||||
line-height: @line-height-base;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&-card-actions {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
|
||||
&-btn {
|
||||
opacity: 0;
|
||||
}
|
||||
&-btn.@{ant-prefix}-btn-sm {
|
||||
height: 20px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
&.picture {
|
||||
top: 22px;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
&-btn:focus,
|
||||
&.picture &-btn {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.@{iconfont-css-prefix} {
|
||||
color: @upload-actions-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-info {
|
||||
height: 100%;
|
||||
padding: 0 4px;
|
||||
transition: background-color 0.3s;
|
||||
|
||||
> span {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.@{iconfont-css-prefix}-loading,
|
||||
.@{upload-prefix-cls}-text-icon {
|
||||
.@{iconfont-css-prefix} {
|
||||
position: absolute;
|
||||
top: (@font-size-base / 2) - 2px;
|
||||
color: @text-color-secondary;
|
||||
font-size: @font-size-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{iconfont-css-prefix}-close {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 4px;
|
||||
color: @text-color-secondary;
|
||||
font-size: 10px;
|
||||
line-height: 0;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
color: @text-color;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover &-info {
|
||||
background-color: @item-hover-bg;
|
||||
}
|
||||
|
||||
&:hover .@{iconfont-css-prefix}-close {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:hover &-card-actions-btn {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&-error,
|
||||
&-error .@{upload-prefix-cls}-text-icon > .@{iconfont-css-prefix},
|
||||
&-error &-name {
|
||||
color: @error-color;
|
||||
}
|
||||
|
||||
&-error &-card-actions {
|
||||
.@{iconfont-css-prefix} {
|
||||
color: @error-color;
|
||||
}
|
||||
|
||||
&-btn {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&-progress {
|
||||
position: absolute;
|
||||
bottom: -12px;
|
||||
width: 100%;
|
||||
padding-left: @font-size-base + 12px;
|
||||
font-size: @font-size-base;
|
||||
line-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// =================== Picture & Picture Card ===================
|
||||
&-picture,
|
||||
&-picture-card {
|
||||
.@{upload-item} {
|
||||
position: relative;
|
||||
height: 66px;
|
||||
padding: @padding-xs;
|
||||
border: @border-width-base @upload-picture-card-border-style @border-color-base;
|
||||
border-radius: @border-radius-base;
|
||||
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&-error {
|
||||
border-color: @error-color;
|
||||
}
|
||||
}
|
||||
|
||||
.@{upload-item}-info {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.@{upload-item}:hover .@{upload-item}-info {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.@{upload-item}-uploading {
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
.@{upload-item}-thumbnail {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
line-height: 60px;
|
||||
text-align: center;
|
||||
opacity: 0.8;
|
||||
|
||||
.@{iconfont-css-prefix} {
|
||||
font-size: 26px;
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust the color of the error icon : https://github.com/ant-design/ant-design/pull/24160
|
||||
.@{upload-item}-error .@{upload-item}-thumbnail {
|
||||
.@{iconfont-css-prefix} {
|
||||
svg path {
|
||||
&[fill='#e6f7ff'] {
|
||||
fill: @error-color-deprecated-bg;
|
||||
}
|
||||
|
||||
&[fill='#1890ff'] {
|
||||
fill: @error-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{upload-item}-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
font-size: 26px;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
.@{iconfont-css-prefix} {
|
||||
font-size: 26px;
|
||||
}
|
||||
}
|
||||
|
||||
.@{upload-item}-image {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.@{upload-item}-thumbnail img {
|
||||
display: block;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.@{upload-item}-name {
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
max-width: 100%;
|
||||
margin: 0 0 0 8px;
|
||||
padding-right: 8px;
|
||||
padding-left: 48px;
|
||||
overflow: hidden;
|
||||
line-height: 44px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.@{upload-item}-uploading .@{upload-item}-name {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.@{upload-item}-progress {
|
||||
bottom: 14px;
|
||||
width: ~'calc(100% - 24px)';
|
||||
margin-top: 0;
|
||||
padding-left: 56px;
|
||||
}
|
||||
|
||||
.@{iconfont-css-prefix}-close {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
line-height: 1;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== Picture Card ========================
|
||||
&-picture-card {
|
||||
&-container {
|
||||
display: inline-block;
|
||||
width: @upload-picture-card-size;
|
||||
height: @upload-picture-card-size;
|
||||
margin: 0 @margin-xs @margin-xs 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
&.@{upload-prefix-cls}-list::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.@{upload-item} {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.@{upload-item}-info {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: fade(@black, 50%);
|
||||
opacity: 0;
|
||||
transition: all 0.3s;
|
||||
content: ' ';
|
||||
}
|
||||
}
|
||||
|
||||
.@{upload-item}:hover .@{upload-item}-info::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.@{upload-item}-actions {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
z-index: 10;
|
||||
white-space: nowrap;
|
||||
transform: translate(-50%, -50%);
|
||||
opacity: 0;
|
||||
transition: all 0.3s;
|
||||
|
||||
.@{iconfont-css-prefix}-eye,
|
||||
.@{iconfont-css-prefix}-download,
|
||||
.@{iconfont-css-prefix}-delete {
|
||||
z-index: 10;
|
||||
width: 16px;
|
||||
margin: 0 4px;
|
||||
color: @text-color-dark;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
color: @text-color-inverse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{upload-item}-info:hover + .@{upload-item}-actions,
|
||||
.@{upload-item}-actions:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.@{upload-item}-thumbnail,
|
||||
.@{upload-item}-thumbnail img {
|
||||
position: static;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.@{upload-item}-name {
|
||||
display: none;
|
||||
margin: 8px 0 0;
|
||||
padding: 0;
|
||||
line-height: @line-height-base;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.@{upload-item}-file + .@{upload-item}-name {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.@{upload-item}-uploading {
|
||||
&.@{upload-item} {
|
||||
background-color: @background-color-light;
|
||||
}
|
||||
|
||||
.@{upload-item}-info {
|
||||
height: auto;
|
||||
|
||||
&::before,
|
||||
.@{iconfont-css-prefix}-eye,
|
||||
.@{iconfont-css-prefix}-delete {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{upload-item}-progress {
|
||||
bottom: 32px;
|
||||
width: calc(100% - 14px);
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================= Picture & Text =======================
|
||||
&-text,
|
||||
&-picture {
|
||||
&-container {
|
||||
transition: opacity @animation-duration-slow, height @animation-duration-slow;
|
||||
|
||||
&::before {
|
||||
display: table;
|
||||
width: 0;
|
||||
height: 0;
|
||||
content: '';
|
||||
}
|
||||
|
||||
// Don't know why span here, just stretch it
|
||||
.@{upload-prefix-cls}-span {
|
||||
display: block;
|
||||
flex: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// text & picture no need this additional element.
|
||||
// But it used for picture-card, let's keep it.
|
||||
.@{upload-prefix-cls}-span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> * {
|
||||
flex: none;
|
||||
}
|
||||
}
|
||||
|
||||
.@{upload-item}-name {
|
||||
flex: auto;
|
||||
margin: 0;
|
||||
padding: 0 @padding-xs;
|
||||
}
|
||||
|
||||
.@{upload-item}-card-actions {
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================ Text ============================
|
||||
&-text {
|
||||
.@{upload-prefix-cls}-text-icon {
|
||||
.@{iconfont-css-prefix} {
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =========================== Motion ===========================
|
||||
.@{upload-prefix-cls}-animate-inline-appear,
|
||||
.@{upload-prefix-cls}-animate-inline-enter,
|
||||
.@{upload-prefix-cls}-animate-inline-leave {
|
||||
animation-duration: @animation-duration-slow;
|
||||
animation-fill-mode: @ease-in-out-circ;
|
||||
}
|
||||
|
||||
.@{upload-prefix-cls}-animate-inline-appear,
|
||||
.@{upload-prefix-cls}-animate-inline-enter {
|
||||
animation-name: uploadAnimateInlineIn;
|
||||
}
|
||||
|
||||
.@{upload-prefix-cls}-animate-inline-leave {
|
||||
animation-name: uploadAnimateInlineOut;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes uploadAnimateInlineIn {
|
||||
from {
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes uploadAnimateInlineOut {
|
||||
to {
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@import './rtl';
|
|
@ -2,5 +2,6 @@ import '../../style/index.less';
|
|||
import './index.less';
|
||||
|
||||
// style dependencies
|
||||
import '../../button/style';
|
||||
import '../../progress/style';
|
||||
import '../../tooltip/style';
|
|
@ -0,0 +1,179 @@
|
|||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
|
||||
@upload-prefix-cls: ~'@{ant-prefix}-upload';
|
||||
@upload-item: ~'@{ant-prefix}-upload-list-item';
|
||||
|
||||
.@{upload-prefix-cls} {
|
||||
&-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
&&-select-picture-card {
|
||||
.@{upload-prefix-cls}-rtl& {
|
||||
margin-right: auto;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{upload-prefix-cls}-list {
|
||||
&-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
&-item-list-type-text {
|
||||
&:hover {
|
||||
.@{upload-prefix-cls}-list-item-name-icon-count-1 {
|
||||
.@{upload-prefix-cls}-list-rtl & {
|
||||
padding-right: 22px;
|
||||
padding-left: 14px;
|
||||
}
|
||||
}
|
||||
.@{upload-prefix-cls}-list-item-name-icon-count-2 {
|
||||
.@{upload-prefix-cls}-list-rtl & {
|
||||
padding-right: 22px;
|
||||
padding-left: 28px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-item {
|
||||
&-name {
|
||||
.@{upload-prefix-cls}-list-rtl & {
|
||||
padding-right: @font-size-base + 8px;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-name-icon-count-1 {
|
||||
.@{upload-prefix-cls}-list-rtl & {
|
||||
padding-left: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
&-card-actions {
|
||||
.@{upload-prefix-cls}-list-rtl & {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
.@{iconfont-css-prefix} {
|
||||
.@{upload-prefix-cls}-list-rtl & {
|
||||
padding-right: 0;
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-info {
|
||||
.@{upload-prefix-cls}-list-rtl & {
|
||||
padding: 0 4px 0 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.@{iconfont-css-prefix}-close {
|
||||
.@{upload-prefix-cls}-list-rtl & {
|
||||
right: auto;
|
||||
left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&-error &-card-actions {
|
||||
.@{iconfont-css-prefix} {
|
||||
.@{upload-prefix-cls}-list-rtl & {
|
||||
padding-right: 0;
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-progress {
|
||||
.@{upload-prefix-cls}-list-rtl & {
|
||||
padding-right: @font-size-base + 12px;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-picture,
|
||||
&-picture-card {
|
||||
.@{upload-item}-info {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.@{upload-item}-thumbnail {
|
||||
.@{upload-prefix-cls}-list-rtl& {
|
||||
right: 8px;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.@{upload-item}-icon {
|
||||
.@{upload-prefix-cls}-list-rtl& {
|
||||
right: 50%;
|
||||
left: auto;
|
||||
transform: translate(50%, -50%);
|
||||
}
|
||||
}
|
||||
|
||||
.@{upload-item}-name {
|
||||
.@{upload-prefix-cls}-list-rtl& {
|
||||
margin: 0 8px 0 0;
|
||||
padding-right: 48px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.@{upload-item}-name-icon-count-1 {
|
||||
.@{upload-prefix-cls}-list-rtl& {
|
||||
padding-right: 48px;
|
||||
padding-left: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.@{upload-item}-name-icon-count-2 {
|
||||
.@{upload-prefix-cls}-list-rtl& {
|
||||
padding-right: 48px;
|
||||
padding-left: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
.@{upload-item}-progress {
|
||||
.@{upload-prefix-cls}-list-rtl& {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.@{iconfont-css-prefix}-close {
|
||||
.@{upload-prefix-cls}-list-rtl& {
|
||||
right: auto;
|
||||
left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-picture-card {
|
||||
&-container {
|
||||
.@{upload-prefix-cls}-list-rtl & {
|
||||
margin: 0 0 @margin-xs @margin-xs;
|
||||
}
|
||||
}
|
||||
|
||||
.@{upload-item}-actions {
|
||||
.@{upload-prefix-cls}-list-rtl& {
|
||||
right: 50%;
|
||||
left: auto;
|
||||
transform: translate(50%, -50%);
|
||||
}
|
||||
}
|
||||
|
||||
.@{upload-item}-file + .@{upload-item}-name {
|
||||
.@{upload-prefix-cls}-list-rtl& {
|
||||
margin: 8px 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
import type { RcFile, UploadFile, InternalUploadFile } from './interface';
|
||||
|
||||
export function file2Obj(file: RcFile): InternalUploadFile {
|
||||
return {
|
||||
...file,
|
||||
lastModified: file.lastModified,
|
||||
lastModifiedDate: file.lastModifiedDate,
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
uid: file.uid,
|
||||
percent: 0,
|
||||
originFileObj: file,
|
||||
};
|
||||
}
|
||||
|
||||
/** Upload fileList. Replace file if exist or just push into it. */
|
||||
export function updateFileList(file: UploadFile<any>, fileList: UploadFile<any>[]) {
|
||||
const nextFileList = [...fileList];
|
||||
const fileIndex = nextFileList.findIndex(({ uid }: UploadFile) => uid === file.uid);
|
||||
if (fileIndex === -1) {
|
||||
nextFileList.push(file);
|
||||
} else {
|
||||
nextFileList[fileIndex] = file;
|
||||
}
|
||||
return nextFileList;
|
||||
}
|
||||
|
||||
export function getFileItem(file: RcFile, fileList: UploadFile[]) {
|
||||
const matchKey = file.uid !== undefined ? 'uid' : 'name';
|
||||
return fileList.filter(item => item[matchKey] === file[matchKey])[0];
|
||||
}
|
||||
|
||||
export function removeFileItem(file: UploadFile, fileList: UploadFile[]) {
|
||||
const matchKey = file.uid !== undefined ? 'uid' : 'name';
|
||||
const removed = fileList.filter(item => item[matchKey] !== file[matchKey]);
|
||||
if (removed.length === fileList.length) {
|
||||
return null;
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
// ==================== Default Image Preview ====================
|
||||
const extname = (url = '') => {
|
||||
const temp = url.split('/');
|
||||
const filename = temp[temp.length - 1];
|
||||
const filenameWithoutSuffix = filename.split(/#|\?/)[0];
|
||||
return (/\.[^./\\]*$/.exec(filenameWithoutSuffix) || [''])[0];
|
||||
};
|
||||
|
||||
const isImageFileType = (type: string): boolean => type.indexOf('image/') === 0;
|
||||
|
||||
export const isImageUrl = (file: UploadFile): boolean => {
|
||||
if (file.type && !file.thumbUrl) {
|
||||
return isImageFileType(file.type);
|
||||
}
|
||||
const url: string = (file.thumbUrl || file.url || '') as string;
|
||||
const extension = extname(url);
|
||||
if (
|
||||
/^data:image\//.test(url) ||
|
||||
/(webp|svg|png|gif|jpg|jpeg|jfif|bmp|dpg|ico)$/i.test(extension)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (/^data:/.test(url)) {
|
||||
// other file types of base64
|
||||
return false;
|
||||
}
|
||||
if (extension) {
|
||||
// other file types which have extension
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const MEASURE_SIZE = 200;
|
||||
export function previewImage(file: File | Blob): Promise<string> {
|
||||
return new Promise(resolve => {
|
||||
if (!file.type || !isImageFileType(file.type)) {
|
||||
resolve('');
|
||||
return;
|
||||
}
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = MEASURE_SIZE;
|
||||
canvas.height = MEASURE_SIZE;
|
||||
canvas.style.cssText = `position: fixed; left: 0; top: 0; width: ${MEASURE_SIZE}px; height: ${MEASURE_SIZE}px; z-index: 9999; display: none;`;
|
||||
document.body.appendChild(canvas);
|
||||
const ctx = canvas.getContext('2d');
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
const { width, height } = img;
|
||||
|
||||
let drawWidth = MEASURE_SIZE;
|
||||
let drawHeight = MEASURE_SIZE;
|
||||
let offsetX = 0;
|
||||
let offsetY = 0;
|
||||
|
||||
if (width > height) {
|
||||
drawHeight = height * (MEASURE_SIZE / width);
|
||||
offsetY = -(drawHeight - drawWidth) / 2;
|
||||
} else {
|
||||
drawWidth = width * (MEASURE_SIZE / height);
|
||||
offsetX = -(drawWidth - drawHeight) / 2;
|
||||
}
|
||||
|
||||
ctx!.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
|
||||
const dataURL = canvas.toDataURL();
|
||||
document.body.removeChild(canvas);
|
||||
|
||||
resolve(dataURL);
|
||||
};
|
||||
img.src = window.URL.createObjectURL(file);
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue