feat: add download transformFile previewFile actio
parent
3e8e90da5e
commit
548595959d
|
@ -1,5 +1,5 @@
|
|||
module.exports = {
|
||||
dev: {
|
||||
componentName: 'select', // dev components
|
||||
componentName: 'upload', // dev components
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import classNames from 'classnames';
|
||||
import uniqBy from 'lodash/uniqBy';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import pick from 'lodash/pick';
|
||||
import VcUpload from '../vc-upload';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import { getOptionProps, initDefaultProps, hasProp, getListeners } from '../_util/props-util';
|
||||
|
@ -66,25 +67,12 @@ export default {
|
|||
fileList: nextFileList,
|
||||
});
|
||||
// fix ie progress
|
||||
if (!window.FormData) {
|
||||
if (!window.File || process.env.TEST_IE) {
|
||||
this.autoUpdateProgress(0, targetItem);
|
||||
}
|
||||
},
|
||||
autoUpdateProgress(_, file) {
|
||||
const getPercent = genPercentAdd();
|
||||
let curPercent = 0;
|
||||
this.clearProgressTimer();
|
||||
this.progressTimer = setInterval(() => {
|
||||
curPercent = getPercent(curPercent);
|
||||
this.onProgress(
|
||||
{
|
||||
percent: curPercent * 100,
|
||||
},
|
||||
file,
|
||||
);
|
||||
}, 200);
|
||||
},
|
||||
onSuccess(response, file) {
|
||||
|
||||
onSuccess(response, file, xhr) {
|
||||
this.clearProgressTimer();
|
||||
try {
|
||||
if (typeof response === 'string') {
|
||||
|
@ -101,6 +89,7 @@ export default {
|
|||
}
|
||||
targetItem.status = 'done';
|
||||
targetItem.response = response;
|
||||
targetItem.xhr = xhr;
|
||||
this.onChange({
|
||||
file: { ...targetItem },
|
||||
fileList,
|
||||
|
@ -140,19 +129,24 @@ export default {
|
|||
this.$emit('reject', fileList);
|
||||
},
|
||||
handleRemove(file) {
|
||||
const { remove } = this;
|
||||
const { status } = file;
|
||||
file.status = 'removed'; // eslint-disable-line
|
||||
const { remove: onRemove } = this;
|
||||
const { sFileList: fileList } = this.$data;
|
||||
|
||||
Promise.resolve(typeof remove === 'function' ? remove(file) : remove).then(ret => {
|
||||
Promise.resolve(typeof onRemove === 'function' ? onRemove(file) : onRemove).then(ret => {
|
||||
// Prevent removing file
|
||||
if (ret === false) {
|
||||
file.status = status;
|
||||
return;
|
||||
}
|
||||
|
||||
const removedFileList = removeFileItem(file, this.sFileList);
|
||||
const removedFileList = removeFileItem(file, fileList);
|
||||
|
||||
if (removedFileList) {
|
||||
file.status = 'removed'; // eslint-disable-line
|
||||
|
||||
if (this.upload) {
|
||||
this.upload.abort(file);
|
||||
}
|
||||
|
||||
this.onChange({
|
||||
file,
|
||||
fileList: removedFileList,
|
||||
|
@ -178,14 +172,16 @@ export default {
|
|||
});
|
||||
},
|
||||
reBeforeUpload(file, fileList) {
|
||||
if (!this.beforeUpload) {
|
||||
const { beforeUpload } = this.$props;
|
||||
const { sFileList: stateFileList } = this.$data;
|
||||
if (!beforeUpload) {
|
||||
return true;
|
||||
}
|
||||
const result = this.beforeUpload(file, fileList);
|
||||
const result = beforeUpload(file, fileList);
|
||||
if (result === false) {
|
||||
this.onChange({
|
||||
file,
|
||||
fileList: uniqBy(this.sFileList.concat(fileList.map(fileToObject)), item => item.uid),
|
||||
fileList: uniqBy(stateFileList.concat(fileList.map(fileToObject)), item => item.uid),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
@ -197,25 +193,45 @@ export default {
|
|||
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 } = getOptionProps(this);
|
||||
const { showRemoveIcon, showPreviewIcon } = showUploadList;
|
||||
const {
|
||||
showUploadList = {},
|
||||
listType,
|
||||
previewFile,
|
||||
disabled,
|
||||
locale: propLocale,
|
||||
} = getOptionProps(this);
|
||||
const { showRemoveIcon, showPreviewIcon, showDownloadIcon } = showUploadList;
|
||||
const { sFileList: fileList } = this.$data;
|
||||
const uploadListProps = {
|
||||
props: {
|
||||
listType,
|
||||
items: this.sFileList,
|
||||
showRemoveIcon,
|
||||
items: fileList,
|
||||
previewFile,
|
||||
showRemoveIcon: !disabled && showRemoveIcon,
|
||||
showPreviewIcon,
|
||||
locale: { ...locale, ...this.$props.locale },
|
||||
showDownloadIcon,
|
||||
locale: { ...locale, ...propLocale },
|
||||
},
|
||||
on: {
|
||||
remove: this.handleManualRemove,
|
||||
...pick(getListeners(this), ['download', 'preview']), // 如果没有配置该事件,不要传递, uploadlist 会有相应逻辑
|
||||
},
|
||||
};
|
||||
const listeners = getListeners(this);
|
||||
if (listeners.preview) {
|
||||
uploadListProps.on.preview = listeners.preview;
|
||||
}
|
||||
return <UploadList {...uploadListProps} />;
|
||||
},
|
||||
},
|
||||
|
@ -227,7 +243,7 @@ export default {
|
|||
type,
|
||||
disabled,
|
||||
} = getOptionProps(this);
|
||||
|
||||
const { sFileList: fileList, dragState } = this.$data;
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('upload', customizePrefixCls);
|
||||
|
||||
|
@ -261,8 +277,8 @@ export default {
|
|||
if (type === 'drag') {
|
||||
const dragCls = classNames(prefixCls, {
|
||||
[`${prefixCls}-drag`]: true,
|
||||
[`${prefixCls}-drag-uploading`]: this.sFileList.some(file => file.status === 'uploading'),
|
||||
[`${prefixCls}-drag-hover`]: this.dragState === 'dragover',
|
||||
[`${prefixCls}-drag-uploading`]: fileList.some(file => file.status === 'uploading'),
|
||||
[`${prefixCls}-drag-hover`]: dragState === 'dragover',
|
||||
[`${prefixCls}-disabled`]: disabled,
|
||||
});
|
||||
return (
|
||||
|
@ -290,7 +306,7 @@ export default {
|
|||
|
||||
// Remove id to avoid open by label when trigger is hidden
|
||||
// https://github.com/ant-design/ant-design/issues/14298
|
||||
if (!children) {
|
||||
if (!children || disabled) {
|
||||
delete vcUploadProps.props.id;
|
||||
}
|
||||
|
||||
|
@ -302,7 +318,7 @@ export default {
|
|||
|
||||
if (listType === 'picture-card') {
|
||||
return (
|
||||
<span>
|
||||
<span class={`${prefixCls}-picture-card-wrapper`}>
|
||||
{uploadList}
|
||||
{uploadButton}
|
||||
</span>
|
||||
|
|
|
@ -2,51 +2,13 @@ import BaseMixin from '../_util/BaseMixin';
|
|||
import { getOptionProps, initDefaultProps, getListeners } from '../_util/props-util';
|
||||
import getTransitionProps from '../_util/getTransitionProps';
|
||||
import { ConfigConsumerProps } from '../config-provider';
|
||||
import { previewImage, isImageUrl } from './utils';
|
||||
import Icon from '../icon';
|
||||
import Tooltip from '../tooltip';
|
||||
import Progress from '../progress';
|
||||
import classNames from 'classnames';
|
||||
import { UploadListProps } from './interface';
|
||||
|
||||
const imageTypes = ['image', 'webp', 'png', 'svg', 'gif', 'jpg', 'jpeg', 'bmp', 'dpg', 'ico'];
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
|
||||
const previewFile = (file, callback) => {
|
||||
if (file.type && !imageTypes.includes(file.type)) {
|
||||
callback('');
|
||||
}
|
||||
const reader = new window.FileReader();
|
||||
reader.onloadend = () => callback(reader.result);
|
||||
reader.readAsDataURL(file);
|
||||
};
|
||||
|
||||
const extname = url => {
|
||||
if (!url) {
|
||||
return '';
|
||||
}
|
||||
const temp = url.split('/');
|
||||
const filename = temp[temp.length - 1];
|
||||
const filenameWithoutSuffix = filename.split(/#|\?/)[0];
|
||||
return (/\.[^./\\]*$/.exec(filenameWithoutSuffix) || [''])[0];
|
||||
};
|
||||
|
||||
const isImageUrl = file => {
|
||||
if (imageTypes.includes(file.type)) {
|
||||
return true;
|
||||
}
|
||||
const url = file.thumbUrl || file.url;
|
||||
const extension = extname(url);
|
||||
if (/^data:image\//.test(url) || /(webp|svg|png|gif|jpg|jpeg|bmp|dpg|ico)$/i.test(extension)) {
|
||||
return true;
|
||||
} else if (/^data:/.test(url)) {
|
||||
// other file types of base64
|
||||
return false;
|
||||
} else if (extension) {
|
||||
// other file types which have extension
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'AUploadList',
|
||||
mixins: [BaseMixin],
|
||||
|
@ -57,43 +19,43 @@ export default {
|
|||
showInfo: false,
|
||||
},
|
||||
showRemoveIcon: true,
|
||||
showDownloadIcon: true,
|
||||
showPreviewIcon: true,
|
||||
previewFile: previewImage,
|
||||
}),
|
||||
inject: {
|
||||
configProvider: { default: () => ConfigConsumerProps },
|
||||
},
|
||||
updated() {
|
||||
this.$nextTick(() => {
|
||||
if (this.listType !== 'picture' && this.listType !== 'picture-card') {
|
||||
const { listType, items, previewFile } = this.$props;
|
||||
if (listType !== 'picture' && listType !== 'picture-card') {
|
||||
return;
|
||||
}
|
||||
(this.items || []).forEach(file => {
|
||||
(items || []).forEach(file => {
|
||||
if (
|
||||
typeof document === 'undefined' ||
|
||||
typeof window === 'undefined' ||
|
||||
!window.FileReader ||
|
||||
!window.File ||
|
||||
!(file.originFileObj instanceof window.File) ||
|
||||
!(file.originFileObj instanceof File || file.originFileObj instanceof Blob) ||
|
||||
file.thumbUrl !== undefined
|
||||
) {
|
||||
return;
|
||||
}
|
||||
/*eslint-disable */
|
||||
file.thumbUrl = '';
|
||||
/*eslint -enable */
|
||||
previewFile(file.originFileObj, previewDataUrl => {
|
||||
// Need append '' to avoid dead loop
|
||||
file.thumbUrl = previewDataUrl || '';
|
||||
/*eslint -enable */
|
||||
this.$forceUpdate();
|
||||
});
|
||||
if (previewFile) {
|
||||
previewFile(file.originFileObj).then(previewDataUrl => {
|
||||
// Need append '' to avoid dead loop
|
||||
file.thumbUrl = previewDataUrl || '';
|
||||
this.$forceUpdate();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
handleClose(file) {
|
||||
this.$emit('remove', file);
|
||||
},
|
||||
handlePreview(file, e) {
|
||||
const { preview } = getListeners(this);
|
||||
if (!preview) {
|
||||
|
@ -102,6 +64,18 @@ export default {
|
|||
e.preventDefault();
|
||||
return this.$emit('preview', file);
|
||||
},
|
||||
handleDownload(file) {
|
||||
const { download } = getListeners(this);
|
||||
if (typeof download === 'function') {
|
||||
download(file);
|
||||
} else if (file.url) {
|
||||
window.open(file.url);
|
||||
}
|
||||
},
|
||||
|
||||
handleClose(file) {
|
||||
this.$emit('remove', file);
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const {
|
||||
|
@ -110,7 +84,9 @@ export default {
|
|||
listType,
|
||||
showPreviewIcon,
|
||||
showRemoveIcon,
|
||||
showDownloadIcon,
|
||||
locale,
|
||||
progressAttr,
|
||||
} = getOptionProps(this);
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('upload', customizePrefixCls);
|
||||
|
@ -126,7 +102,11 @@ export default {
|
|||
icon = <Icon class={`${prefixCls}-list-item-thumbnail`} type="picture" theme="twoTone" />;
|
||||
} else {
|
||||
const thumbnail = isImageUrl(file) ? (
|
||||
<img src={file.thumbUrl || file.url} alt={file.name} />
|
||||
<img
|
||||
src={file.thumbUrl || file.url}
|
||||
alt={file.name}
|
||||
class={`${prefixCls}-list-item-image`}
|
||||
/>
|
||||
) : (
|
||||
<Icon type="file" class={`${prefixCls}-list-item-icon`} theme="twoTone" />
|
||||
);
|
||||
|
@ -147,7 +127,7 @@ export default {
|
|||
if (file.status === 'uploading') {
|
||||
const progressProps = {
|
||||
props: {
|
||||
...this.progressAttr,
|
||||
...progressAttr,
|
||||
type: 'line',
|
||||
percent: file.percent,
|
||||
},
|
||||
|
@ -164,30 +144,64 @@ export default {
|
|||
const infoUploadingClass = classNames({
|
||||
[`${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 preview = file.url ? (
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class={`${prefixCls}-list-item-name`}
|
||||
title={file.name}
|
||||
{...linkProps}
|
||||
href={file.url}
|
||||
onClick={e => this.handlePreview(file, e)}
|
||||
>
|
||||
{file.name}
|
||||
</a>
|
||||
) : (
|
||||
|
||||
const removeIcon = showRemoveIcon ? (
|
||||
<Icon type="delete" title={locale.removeFile} onClick={() => this.handleClose(file)} />
|
||||
) : null;
|
||||
const downloadIcon =
|
||||
showDownloadIcon && file.status === 'done' ? (
|
||||
<Icon
|
||||
type="download"
|
||||
title={locale.downloadFile}
|
||||
onClick={() => this.handleDownload(file)}
|
||||
/>
|
||||
) : null;
|
||||
const downloadOrDelete = listType !== 'picture-card' && (
|
||||
<span
|
||||
class={`${prefixCls}-list-item-name`}
|
||||
onClick={e => this.handlePreview(file, e)}
|
||||
title={file.name}
|
||||
key="download-delete"
|
||||
class={`${prefixCls}-list-item-card-actions ${listType === 'picture' ? 'picture' : ''}`}
|
||||
>
|
||||
{file.name}
|
||||
{downloadIcon && <a title={locale.downloadFile}>{downloadIcon}</a>}
|
||||
{removeIcon && <a title={locale.removeFile}>{removeIcon}</a>}
|
||||
</span>
|
||||
);
|
||||
const listItemNameClass = classNames({
|
||||
[`${prefixCls}-list-item-name`]: true,
|
||||
[`${prefixCls}-list-item-name-icon-count-${
|
||||
[downloadIcon, removeIcon].filter(x => x).length
|
||||
}`]: true,
|
||||
});
|
||||
|
||||
const preview = file.url
|
||||
? [
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class={listItemNameClass}
|
||||
title={file.name}
|
||||
{...linkProps}
|
||||
href={file.url}
|
||||
onClick={e => this.handlePreview(file, e)}
|
||||
>
|
||||
{file.name}
|
||||
</a>,
|
||||
downloadOrDelete,
|
||||
]
|
||||
: [
|
||||
<span
|
||||
key="view"
|
||||
class={`${prefixCls}-list-item-name`}
|
||||
onClick={e => this.handlePreview(file, e)}
|
||||
title={file.name}
|
||||
>
|
||||
{file.name}
|
||||
</span>,
|
||||
downloadOrDelete,
|
||||
];
|
||||
const style =
|
||||
file.url || file.thumbUrl
|
||||
? undefined
|
||||
|
@ -207,55 +221,41 @@ export default {
|
|||
<Icon type="eye-o" />
|
||||
</a>
|
||||
) : null;
|
||||
const iconProps = {
|
||||
props: {
|
||||
type: 'delete',
|
||||
title: locale.removeFile,
|
||||
},
|
||||
on: {
|
||||
click: () => {
|
||||
this.handleClose(file);
|
||||
},
|
||||
},
|
||||
};
|
||||
const iconProps1 = { ...iconProps, ...{ props: { type: 'close' } } };
|
||||
const removeIcon = showRemoveIcon ? <Icon {...iconProps} /> : null;
|
||||
const removeIconClose = showRemoveIcon ? <Icon {...iconProps1} /> : null;
|
||||
const actions =
|
||||
listType === 'picture-card' && file.status !== 'uploading' ? (
|
||||
<span class={`${prefixCls}-list-item-actions`}>
|
||||
{previewIcon}
|
||||
{removeIcon}
|
||||
</span>
|
||||
) : (
|
||||
removeIconClose
|
||||
);
|
||||
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 && file.error.statusText) || locale.uploadError;
|
||||
}
|
||||
const iconAndPreview =
|
||||
file.status === 'error' ? (
|
||||
<Tooltip title={message}>
|
||||
{icon}
|
||||
{preview}
|
||||
</Tooltip>
|
||||
) : (
|
||||
<span>
|
||||
{icon}
|
||||
{preview}
|
||||
</span>
|
||||
);
|
||||
const iconAndPreview = (
|
||||
<span>
|
||||
{icon}
|
||||
{preview}
|
||||
</span>
|
||||
);
|
||||
const transitionProps = getTransitionProps('fade');
|
||||
return (
|
||||
const dom = (
|
||||
<div class={infoUploadingClass} key={file.uid}>
|
||||
<div class={`${prefixCls}-list-item-info`}>{iconAndPreview}</div>
|
||||
{actions}
|
||||
<transition {...transitionProps}>{progress}</transition>
|
||||
</div>
|
||||
);
|
||||
const listContainerNameClass = classNames({
|
||||
[`${prefixCls}-list-picture-card-container`]: listType === 'picture-card',
|
||||
});
|
||||
return (
|
||||
<div key={file.uid} class={listContainerNameClass}>
|
||||
{file.status === 'error' ? <Tooltip title={message}>{dom}</Tooltip> : <span>{dom}</span>}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
const listClassNames = classNames({
|
||||
[`${prefixCls}-list`]: true,
|
||||
|
|
|
@ -56,15 +56,15 @@ The return value of function `beforeUpload` can be a Promise to check asynchrono
|
|||
}
|
||||
},
|
||||
beforeUpload(file) {
|
||||
const isJPG = file.type === 'image/jpeg';
|
||||
if (!isJPG) {
|
||||
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
|
||||
if (!isJpgOrPng) {
|
||||
this.$message.error('You can only upload JPG file!');
|
||||
}
|
||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||
if (!isLt2M) {
|
||||
this.$message.error('Image must smaller than 2MB!');
|
||||
}
|
||||
return isJPG && isLt2M;
|
||||
return isJpgOrPng && isLt2M;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<cn>
|
||||
#### 点击上传
|
||||
经典款式,用户点击按钮弹出文件选择框。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Upload by clicking
|
||||
Classic mode. File selection dialog pops up when upload button is clicked.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<a-upload
|
||||
name="file"
|
||||
:multiple="true"
|
||||
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
|
||||
:headers="headers"
|
||||
@change="handleChange"
|
||||
>
|
||||
<a-button> <a-icon type="upload" /> Click to Upload </a-button>
|
||||
</a-upload>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
headers: {
|
||||
authorization: 'authorization-text',
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleChange(info) {
|
||||
if (info.file.status !== 'uploading') {
|
||||
console.log(info.file, info.fileList);
|
||||
}
|
||||
if (info.file.status === 'done') {
|
||||
this.$message.success(`${info.file.name} file uploaded successfully`);
|
||||
} else if (info.file.status === 'error') {
|
||||
this.$message.error(`${info.file.name} file upload failed.`);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
|
@ -3,7 +3,6 @@
|
|||
使用 `fileList` 对列表进行完全控制,可以实现各种自定义功能,以下演示三种情况:
|
||||
1) 上传列表数量的限制。
|
||||
2) 读取远程路径并显示链接。
|
||||
3) 按照服务器返回信息筛选成功上传的文件。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
|
@ -11,7 +10,6 @@
|
|||
You can gain full control over filelist by configuring `fileList`. You can accomplish all kinds of customed functions. The following shows three circumstances:
|
||||
1) limit the number of uploaded files.
|
||||
2) read from response and show file link.
|
||||
3) filter successfully uploaded files according to response from server.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
|
|
|
@ -8,6 +8,8 @@ import Drag from './drag.md';
|
|||
import PictureStyle from './picture-style.md';
|
||||
import UploadManually from './upload-manually.md';
|
||||
import Directory from './directory.md';
|
||||
import PreviewFile from './preview-file';
|
||||
import TransformFile from './transform-file';
|
||||
|
||||
import CN from '../index.zh-CN.md';
|
||||
import US from '../index.en-US.md';
|
||||
|
@ -52,6 +54,8 @@ export default {
|
|||
<PictureStyle />
|
||||
<UploadManually />
|
||||
<Directory />
|
||||
<PreviewFile />
|
||||
<TransformFile />
|
||||
<api>
|
||||
<template slot="cn">
|
||||
<CN />
|
||||
|
|
|
@ -18,7 +18,7 @@ After users upload picture, the thumbnail will be shown in list. The upload butt
|
|||
@preview="handlePreview"
|
||||
@change="handleChange"
|
||||
>
|
||||
<div v-if="fileList.length < 3">
|
||||
<div v-if="fileList.length < 8">
|
||||
<a-icon type="plus" />
|
||||
<div class="ant-upload-text">Upload</div>
|
||||
</div>
|
||||
|
@ -29,6 +29,14 @@ After users upload picture, the thumbnail will be shown in list. The upload butt
|
|||
</div>
|
||||
</template>
|
||||
<script>
|
||||
function getBase64(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 {
|
||||
data() {
|
||||
return {
|
||||
|
@ -37,10 +45,33 @@ After users upload picture, the thumbnail will be shown in list. The upload butt
|
|||
fileList: [
|
||||
{
|
||||
uid: '-1',
|
||||
name: 'xxx.png',
|
||||
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',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
|
@ -48,8 +79,11 @@ After users upload picture, the thumbnail will be shown in list. The upload butt
|
|||
handleCancel() {
|
||||
this.previewVisible = false;
|
||||
},
|
||||
handlePreview(file) {
|
||||
this.previewImage = file.url || file.thumbUrl;
|
||||
async handlePreview(file) {
|
||||
if (!file.url && !file.preview) {
|
||||
file.preview = await getBase64(file.originFileObj);
|
||||
}
|
||||
this.previewImage = file.url || file.preview;
|
||||
this.previewVisible = true;
|
||||
},
|
||||
handleChange({ fileList }) {
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
<cn>
|
||||
#### 自定义预览
|
||||
自定义本地预览,用于处理非图片格式文件(例如视频文件)。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Customize preview file
|
||||
Customize local preview. Can handle with non-image format files such as video.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div>
|
||||
<a-upload
|
||||
listType="picture"
|
||||
action="//jsonplaceholder.typicode.com/posts/"
|
||||
:previewFile="previewFile"
|
||||
>
|
||||
<a-button> <a-icon type="upload" /> Upload </a-button>
|
||||
</a-upload>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
previewFile(file) {
|
||||
console.log('Your upload file:', file);
|
||||
// Your process logic. Here we just mock to the same file
|
||||
return fetch('https://next.json-generator.com/api/json/get/4ytyBoLK8', {
|
||||
method: 'POST',
|
||||
body: file,
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(({ thumbnail }) => thumbnail);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
|
@ -0,0 +1,48 @@
|
|||
<cn>
|
||||
#### 上传前转换文件
|
||||
使用 `transformFile` 转换上传的文件(例如添加水印)。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Transform file before request
|
||||
Use `transformFile` for transform file before request such as add a watermark.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div>
|
||||
<a-upload
|
||||
listType="picture"
|
||||
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
|
||||
:transformFile="transformFile"
|
||||
>
|
||||
<a-button> <a-icon type="upload" /> Upload </a-button>
|
||||
</a-upload>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
transformFile(file) {
|
||||
return new Promise(resolve => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const img = document.createElement('img');
|
||||
img.src = reader.result;
|
||||
img.onload = () => {
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0);
|
||||
ctx.fillStyle = 'red';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText('Ant Design', 20, 20);
|
||||
canvas.toBlob(resolve);
|
||||
};
|
||||
};
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
|
@ -1,33 +1,37 @@
|
|||
## API
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| 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` | - |
|
||||
| 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) | - |
|
||||
| defaultFileList | Default list of files that have been uploaded. | object\[] | - |
|
||||
| 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' |
|
||||
| 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` | - |
|
||||
| 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) | - | |
|
||||
| defaultFileList | Default list of files that have been uploaded. | object\[] | - | |
|
||||
| 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 |
|
||||
| --- | --- | --- |
|
||||
| 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) | - |
|
||||
| reject | A callback function, will be executed when drop files is not accept. | Function(fileList) | - |
|
||||
| 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
|
||||
|
||||
|
@ -48,10 +52,11 @@ When uploading state change, it returns:
|
|||
```js
|
||||
{
|
||||
uid: 'uid', // unique identifier, negative is recommend, to prevent interference with internal generated id
|
||||
name: 'xx.png' // file name
|
||||
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
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -1,33 +1,37 @@
|
|||
## API
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| accept | 接受上传的文件类型, 详见 [input accept Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) | string | 无 |
|
||||
| action | 上传的地址 | string\|(file) => `Promise` | 无 |
|
||||
| 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 | 无 |
|
||||
| defaultFileList | 默认已经上传的文件列表 | object\[] | 无 |
|
||||
| disabled | 是否禁用 | boolean | false |
|
||||
| fileList | 已经上传的文件列表(受控) | object\[] | 无 |
|
||||
| headers | 设置上传的请求头部,IE10 以上有效 | object | 无 |
|
||||
| listType | 上传列表的内建样式,支持三种基本样式 `text`, `picture` 和 `picture-card` | string | 'text' |
|
||||
| multiple | 是否支持多选文件,`ie10+` 支持。开启后按住 ctrl 可选择多个文件。 | boolean | false |
|
||||
| name | 发到后台的文件参数名 | string | 'file' |
|
||||
| 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` | 无 |
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 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 | 无 | |
|
||||
| defaultFileList | 默认已经上传的文件列表 | 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) | 无 |
|
||||
| reject | 拖拽文件不符合 accept 类型时的回调 | Function(fileList) | 无 |
|
||||
| 事件名称 | 说明 | 回调参数 | 版本 |
|
||||
| --- | --- | --- | --- |
|
||||
| change | 上传文件改变时的状态,详见 [change](#change) | Function | 无 | |
|
||||
| preview | 点击文件链接或预览图标时的回调 | Function(file) | 无 | |
|
||||
| download | 点击下载文件时的回调,如果没有指定,则默认跳转到文件 url 对应的标签页。 | Function(file): void | 跳转新标签页 | 1.5.0 |
|
||||
| reject | 拖拽文件不符合 accept 类型时的回调 | Function(fileList) | 无 | |
|
||||
|
||||
### change
|
||||
|
||||
|
@ -48,10 +52,11 @@
|
|||
```js
|
||||
{
|
||||
uid: 'uid', // 文件唯一标识,建议设置为负数,防止和内部产生的 id 冲突
|
||||
name: 'xx.png' // 文件名
|
||||
name: 'xx.png', // 文件名
|
||||
status: 'done', // 状态有:uploading done error removed
|
||||
response: '{"status": "success"}', // 服务端响应内容
|
||||
linkProps: '{"download": "image"}', // 下载链接额外的 HTML 属性
|
||||
xhr: 'XMLHttpRequest{ ... }', // XMLHttpRequest Header
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ export const ShowUploadListInterface = PropsTypes.shape({
|
|||
export const UploadLocale = PropsTypes.shape({
|
||||
uploading: PropsTypes.string,
|
||||
removeFile: PropsTypes.string,
|
||||
downloadFile: PropsTypes.string,
|
||||
uploadError: PropsTypes.string,
|
||||
previewFile: PropsTypes.string,
|
||||
}).loose;
|
||||
|
@ -66,6 +67,7 @@ export const UploadProps = {
|
|||
action: PropsTypes.oneOfType([PropsTypes.string, PropsTypes.func]),
|
||||
directory: PropsTypes.bool,
|
||||
data: PropsTypes.oneOfType([PropsTypes.object, PropsTypes.func]),
|
||||
method: PropsTypes.oneOf(['POST', 'PUT', 'post', 'put']),
|
||||
headers: PropsTypes.object,
|
||||
showUploadList: PropsTypes.oneOfType([PropsTypes.bool, ShowUploadListInterface]),
|
||||
multiple: PropsTypes.bool,
|
||||
|
@ -86,6 +88,8 @@ export const UploadProps = {
|
|||
locale: UploadLocale,
|
||||
height: PropsTypes.number,
|
||||
id: PropsTypes.string,
|
||||
previewFile: PropsTypes.func,
|
||||
transformFile: PropsTypes.func,
|
||||
};
|
||||
|
||||
export const UploadState = {
|
||||
|
@ -103,6 +107,8 @@ export const UploadListProps = {
|
|||
progressAttr: PropsTypes.object,
|
||||
prefixCls: PropsTypes.string,
|
||||
showRemoveIcon: PropsTypes.bool,
|
||||
showDownloadIcon: PropsTypes.bool,
|
||||
showPreviewIcon: PropsTypes.bool,
|
||||
locale: UploadLocale,
|
||||
previewFile: PropsTypes.func,
|
||||
};
|
||||
|
|
|
@ -54,3 +54,77 @@ export function removeFileItem(file, fileList) {
|
|||
}
|
||||
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 => !!type && type.indexOf('image/') === 0;
|
||||
|
||||
export const isImageUrl = file => {
|
||||
if (isImageFileType(file.type)) {
|
||||
return true;
|
||||
}
|
||||
const url = file.thumbUrl || file.url;
|
||||
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) {
|
||||
return new Promise(resolve => {
|
||||
if (!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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// rc-upload 2.6.3
|
||||
// rc-upload 2.9.4
|
||||
import upload from './src';
|
||||
|
||||
export default upload;
|
||||
|
|
|
@ -28,6 +28,7 @@ const upLoadPropTypes = {
|
|||
// onProgress: PropTypes.func,
|
||||
withCredentials: PropTypes.bool,
|
||||
openFileDialogOnClick: PropTypes.bool,
|
||||
transformFile: PropTypes.func,
|
||||
};
|
||||
|
||||
const AjaxUploader = {
|
||||
|
@ -67,6 +68,7 @@ const AjaxUploader = {
|
|||
}
|
||||
},
|
||||
onFileDrop(e) {
|
||||
const { multiple } = this.$props;
|
||||
e.preventDefault();
|
||||
if (e.type === 'dragover') {
|
||||
return;
|
||||
|
@ -76,21 +78,31 @@ const AjaxUploader = {
|
|||
attrAccept(_file, this.accept),
|
||||
);
|
||||
} else {
|
||||
const files = partition(Array.prototype.slice.call(e.dataTransfer.files), file =>
|
||||
let files = partition(Array.prototype.slice.call(e.dataTransfer.files), file =>
|
||||
attrAccept(file, this.accept),
|
||||
);
|
||||
this.uploadFiles(files[0]);
|
||||
if (files[1].length) {
|
||||
this.$emit('reject', files[1]);
|
||||
let successFiles = files[0];
|
||||
const errorFiles = files[1];
|
||||
if (multiple === false) {
|
||||
successFiles = successFiles.slice(0, 1);
|
||||
}
|
||||
this.uploadFiles(successFiles);
|
||||
|
||||
if (errorFiles.length) {
|
||||
this.$emit('reject', errorFiles);
|
||||
}
|
||||
}
|
||||
},
|
||||
uploadFiles(files) {
|
||||
const postFiles = Array.prototype.slice.call(files);
|
||||
postFiles.forEach(file => {
|
||||
file.uid = getUid();
|
||||
this.upload(file, postFiles);
|
||||
});
|
||||
postFiles
|
||||
.map(file => {
|
||||
file.uid = getUid();
|
||||
return file;
|
||||
})
|
||||
.forEach(file => {
|
||||
this.upload(file, postFiles);
|
||||
});
|
||||
},
|
||||
upload(file, fileList) {
|
||||
if (!this.beforeUpload) {
|
||||
|
@ -119,10 +131,10 @@ const AjaxUploader = {
|
|||
if (!this._isMounted) {
|
||||
return;
|
||||
}
|
||||
let { data } = this.$props;
|
||||
if (typeof data === 'function') {
|
||||
data = data(file);
|
||||
}
|
||||
const { $props: props } = this;
|
||||
let { data } = props;
|
||||
const { transformFile = originFile => originFile } = props;
|
||||
|
||||
new Promise(resolve => {
|
||||
const { action } = this;
|
||||
if (typeof action === 'function') {
|
||||
|
@ -132,26 +144,37 @@ const AjaxUploader = {
|
|||
}).then(action => {
|
||||
const { uid } = file;
|
||||
const request = this.customRequest || defaultRequest;
|
||||
this.reqs[uid] = request({
|
||||
action,
|
||||
filename: this.name,
|
||||
file,
|
||||
data,
|
||||
headers: this.headers,
|
||||
withCredentials: this.withCredentials,
|
||||
onProgress: e => {
|
||||
this.$emit('progress', e, file);
|
||||
},
|
||||
onSuccess: (ret, xhr) => {
|
||||
delete this.reqs[uid];
|
||||
this.$emit('success', ret, file, xhr);
|
||||
},
|
||||
onError: (err, ret) => {
|
||||
delete this.reqs[uid];
|
||||
this.$emit('error', err, ret, file);
|
||||
},
|
||||
const transform = Promise.resolve(transformFile(file)).catch(e => {
|
||||
console.error(e); // eslint-disable-line no-console
|
||||
});
|
||||
transform.then(transformedFile => {
|
||||
if (typeof data === 'function') {
|
||||
data = data(file);
|
||||
}
|
||||
|
||||
const requestOption = {
|
||||
action,
|
||||
filename: this.name,
|
||||
data,
|
||||
file: transformedFile,
|
||||
headers: this.headers,
|
||||
withCredentials: this.withCredentials,
|
||||
method: props.method || 'post',
|
||||
onProgress: e => {
|
||||
this.$emit('progress', e, file);
|
||||
},
|
||||
onSuccess: (ret, xhr) => {
|
||||
delete this.reqs[uid];
|
||||
this.$emit('success', ret, file, xhr);
|
||||
},
|
||||
onError: (err, ret) => {
|
||||
delete this.reqs[uid];
|
||||
this.$emit('error', err, ret, file);
|
||||
},
|
||||
};
|
||||
this.reqs[uid] = request(requestOption);
|
||||
this.$emit('start', file);
|
||||
});
|
||||
this.$emit('start', file);
|
||||
});
|
||||
},
|
||||
reset() {
|
||||
|
@ -166,13 +189,13 @@ const AjaxUploader = {
|
|||
if (file && file.uid) {
|
||||
uid = file.uid;
|
||||
}
|
||||
if (reqs[uid]) {
|
||||
if (reqs[uid] && reqs[uid].abort) {
|
||||
reqs[uid].abort();
|
||||
delete reqs[uid];
|
||||
}
|
||||
delete reqs[uid];
|
||||
} else {
|
||||
Object.keys(reqs).forEach(uid => {
|
||||
if (reqs[uid]) {
|
||||
if (reqs[uid] && reqs[uid].abort) {
|
||||
reqs[uid].abort();
|
||||
}
|
||||
|
||||
|
@ -201,7 +224,7 @@ const AjaxUploader = {
|
|||
? {}
|
||||
: {
|
||||
click: openFileDialogOnClick ? this.onClick : () => {},
|
||||
keydown: this.onKeyDown,
|
||||
keydown: openFileDialogOnClick ? this.onKeyDown : () => {},
|
||||
drop: this.onFileDrop,
|
||||
dragover: this.onFileDrop,
|
||||
};
|
||||
|
@ -222,6 +245,7 @@ const AjaxUploader = {
|
|||
id={$attrs.id}
|
||||
type="file"
|
||||
ref="fileInputRef"
|
||||
onClick={e => e.stopPropagation()} // https://github.com/ant-design/ant-design/issues/19948
|
||||
key={this.uid}
|
||||
style={{ display: 'none' }}
|
||||
accept={accept}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
function getError(option, xhr) {
|
||||
const msg = `cannot post ${option.action} ${xhr.status}'`;
|
||||
const msg = `cannot ${option.method} ${option.action} ${xhr.status}'`;
|
||||
const err = new Error(msg);
|
||||
err.status = xhr.status;
|
||||
err.method = 'post';
|
||||
err.method = option.method;
|
||||
err.url = option.action;
|
||||
return err;
|
||||
}
|
||||
|
@ -46,7 +46,18 @@ export default function upload(option) {
|
|||
const formData = new window.FormData();
|
||||
|
||||
if (option.data) {
|
||||
Object.keys(option.data).map(key => {
|
||||
Object.keys(option.data).forEach(key => {
|
||||
const value = option.data[key];
|
||||
// support key-value array data
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach(item => {
|
||||
// { list: [ 11, 22 ] }
|
||||
// formData.append('list[]', 11);
|
||||
formData.append(`${key}[]`, item);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
formData.append(key, option.data[key]);
|
||||
});
|
||||
}
|
||||
|
@ -67,7 +78,7 @@ export default function upload(option) {
|
|||
option.onSuccess(getBody(xhr), xhr);
|
||||
};
|
||||
|
||||
xhr.open('post', option.action, true);
|
||||
xhr.open(option.method, option.action, true);
|
||||
|
||||
// Has to be after `.open()`. See https://github.com/enyo/dropzone/issues/179
|
||||
if (option.withCredentials && 'withCredentials' in xhr) {
|
||||
|
|
|
@ -27,6 +27,20 @@ const traverseFileTree = (files, callback, isAccepted) => {
|
|||
if (item.isFile) {
|
||||
item.file(file => {
|
||||
if (isAccepted(file)) {
|
||||
// https://github.com/ant-design/ant-design/issues/16426
|
||||
if (item.fullPath && !file.webkitRelativePath) {
|
||||
Object.defineProperties(file, {
|
||||
webkitRelativePath: {
|
||||
writable: true,
|
||||
},
|
||||
});
|
||||
file.webkitRelativePath = item.fullPath.replace(/^\//, '');
|
||||
Object.defineProperties(file, {
|
||||
webkitRelativePath: {
|
||||
writable: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
callback([file]);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -4,16 +4,54 @@
|
|||
|
||||
import { AntdComponent } from './component';
|
||||
|
||||
export interface UploadFile {
|
||||
uid: string | number;
|
||||
export interface VcFile extends File {
|
||||
uid: string;
|
||||
readonly lastModifiedDate: Date;
|
||||
readonly webkitRelativePath: 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?: File | Blob;
|
||||
response?: T;
|
||||
error?: any;
|
||||
linkProps?: any;
|
||||
type: string;
|
||||
xhr?: T;
|
||||
preview?: string;
|
||||
}
|
||||
|
||||
export interface ShowUploadList {
|
||||
showRemoveIcon?: boolean;
|
||||
showPreviewIcon?: boolean;
|
||||
showDownloadIcon?: boolean;
|
||||
}
|
||||
|
||||
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';
|
||||
|
||||
type PreviewFileHandler = (file: File | Blob) => PromiseLike<string>;
|
||||
type TransformFileHandler = (
|
||||
file: VcFile,
|
||||
) => string | Blob | File | PromiseLike<string | Blob | File>;
|
||||
|
||||
export declare class Upload extends AntdComponent {
|
||||
static Dragger: typeof Upload;
|
||||
|
||||
|
@ -56,6 +94,8 @@ export declare class Upload extends AntdComponent {
|
|||
*/
|
||||
data: object | Function;
|
||||
|
||||
method?: 'POST' | 'PUT' | 'post' | 'put';
|
||||
|
||||
/**
|
||||
* Default list of files that have been uploaded.
|
||||
* @type UploadFile[]
|
||||
|
@ -137,4 +177,9 @@ export declare class Upload extends AntdComponent {
|
|||
* @type Function
|
||||
*/
|
||||
remove: (file: any) => boolean | Promise<boolean>;
|
||||
|
||||
locale?: UploadLocale;
|
||||
id?: string;
|
||||
previewFile?: PreviewFileHandler;
|
||||
transformFile?: TransformFileHandler;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue