refactor: vc-upload
parent
b0c3677d50
commit
67f5226cdc
|
@ -965,6 +965,10 @@
|
||||||
@typography-title-margin-top: 1.2em;
|
@typography-title-margin-top: 1.2em;
|
||||||
@typography-title-margin-bottom: 0.5em;
|
@typography-title-margin-bottom: 0.5em;
|
||||||
|
|
||||||
|
// Upload
|
||||||
|
// ---
|
||||||
|
@upload-actions-color: @text-color-secondary;
|
||||||
|
|
||||||
// Image
|
// Image
|
||||||
// ---
|
// ---
|
||||||
@image-size-base: 48px;
|
@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';
|
import './index.less';
|
||||||
|
|
||||||
// style dependencies
|
// style dependencies
|
||||||
|
import '../../button/style';
|
||||||
import '../../progress/style';
|
import '../../progress/style';
|
||||||
import '../../tooltip/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