Browse Source
* docs: add demo to re-produce bug * fix: dragger should work with form decorator, close: #5334 * test: update snapshotpull/5215/merge
Benjy Cui
8 years ago
committed by
偏右
9 changed files with 421 additions and 351 deletions
@ -0,0 +1,12 @@ |
|||
import React from 'react'; |
|||
import Upload from './Upload'; |
|||
import { UploadProps } from './interface'; |
|||
|
|||
export type DraggerProps = UploadProps & { height?: number }; |
|||
|
|||
export default class Dragger extends React.Component<DraggerProps, any> { |
|||
render() { |
|||
const { props } = this; |
|||
return <Upload {...props} type="drag" style={{ height: props.height }}/>; |
|||
} |
|||
} |
@ -0,0 +1,287 @@ |
|||
import React from 'react'; |
|||
import RcUpload from 'rc-upload'; |
|||
import classNames from 'classnames'; |
|||
import assign from 'object-assign'; |
|||
import Dragger from './Dragger'; |
|||
import UploadList from './UploadList'; |
|||
import { UploadProps, UploadLocale } from './interface'; |
|||
import { T, fileToObject, genPercentAdd, getFileItem, removeFileItem } from './utils'; |
|||
|
|||
const defaultLocale: UploadLocale = { |
|||
uploading: '文件上传中', |
|||
removeFile: '删除文件', |
|||
uploadError: '上传错误', |
|||
previewFile: '预览文件', |
|||
}; |
|||
|
|||
export { UploadProps }; |
|||
|
|||
export default class Upload extends React.Component<UploadProps, any> { |
|||
static Dragger: typeof Dragger; |
|||
|
|||
static defaultProps = { |
|||
prefixCls: 'ant-upload', |
|||
type: 'select', |
|||
multiple: false, |
|||
action: '', |
|||
data: {}, |
|||
accept: '', |
|||
beforeUpload: T, |
|||
showUploadList: true, |
|||
listType: 'text', // or pictrue
|
|||
className: '', |
|||
disabled: false, |
|||
supportServerRender: true, |
|||
}; |
|||
|
|||
recentUploadStatus: boolean | PromiseLike<any>; |
|||
progressTimer: any; |
|||
refs: { |
|||
[key: string]: any; |
|||
upload: any; |
|||
}; |
|||
|
|||
constructor(props) { |
|||
super(props); |
|||
this.state = { |
|||
fileList: this.props.fileList || this.props.defaultFileList || [], |
|||
dragState: 'drop', |
|||
}; |
|||
} |
|||
|
|||
componentWillUnmount() { |
|||
this.clearProgressTimer(); |
|||
} |
|||
|
|||
getLocale() { |
|||
let locale = {}; |
|||
if (this.context.antLocale && this.context.antLocale.Upload) { |
|||
locale = this.context.antLocale.Upload; |
|||
} |
|||
return assign({}, defaultLocale, locale, this.props.locale); |
|||
} |
|||
|
|||
onStart = (file) => { |
|||
let targetItem; |
|||
let nextFileList = this.state.fileList.concat(); |
|||
if (file.length > 0) { |
|||
targetItem = file.map(f => { |
|||
const fileObject = fileToObject(f); |
|||
fileObject.status = 'uploading'; |
|||
return fileObject; |
|||
}); |
|||
nextFileList = nextFileList.concat(targetItem); |
|||
} else { |
|||
targetItem = fileToObject(file); |
|||
targetItem.status = 'uploading'; |
|||
nextFileList.push(targetItem); |
|||
} |
|||
this.onChange({ |
|||
file: targetItem, |
|||
fileList: nextFileList, |
|||
}); |
|||
// fix ie progress
|
|||
if (!(window as any).FormData) { |
|||
this.autoUpdateProgress(0, targetItem); |
|||
} |
|||
} |
|||
|
|||
autoUpdateProgress(_, file) { |
|||
const getPercent = genPercentAdd(); |
|||
let curPercent = 0; |
|||
this.clearProgressTimer(); |
|||
this.progressTimer = setInterval(() => { |
|||
curPercent = getPercent(curPercent); |
|||
this.onProgress({ |
|||
percent: curPercent, |
|||
}, file); |
|||
}, 200); |
|||
} |
|||
|
|||
onSuccess = (response, file) => { |
|||
this.clearProgressTimer(); |
|||
try { |
|||
if (typeof response === 'string') { |
|||
response = JSON.parse(response); |
|||
} |
|||
} catch (e) { /* do nothing */ |
|||
} |
|||
let fileList = this.state.fileList; |
|||
let targetItem = getFileItem(file, fileList); |
|||
// removed
|
|||
if (!targetItem) { |
|||
return; |
|||
} |
|||
targetItem.status = 'done'; |
|||
targetItem.response = response; |
|||
this.onChange({ |
|||
file: { ...targetItem }, |
|||
fileList, |
|||
}); |
|||
} |
|||
|
|||
onProgress = (e, file) => { |
|||
let fileList = this.state.fileList; |
|||
let targetItem = getFileItem(file, fileList); |
|||
// removed
|
|||
if (!targetItem) { |
|||
return; |
|||
} |
|||
targetItem.percent = e.percent; |
|||
this.onChange({ |
|||
event: e, |
|||
file: { ...targetItem }, |
|||
fileList: this.state.fileList, |
|||
}); |
|||
} |
|||
|
|||
onError = (error, response, file) => { |
|||
this.clearProgressTimer(); |
|||
let fileList = this.state.fileList; |
|||
let targetItem = getFileItem(file, fileList); |
|||
// removed
|
|||
if (!targetItem) { |
|||
return; |
|||
} |
|||
targetItem.error = error; |
|||
targetItem.response = response; |
|||
targetItem.status = 'error'; |
|||
this.onChange({ |
|||
file: { ...targetItem }, |
|||
fileList, |
|||
}); |
|||
} |
|||
|
|||
handleRemove(file) { |
|||
const { onRemove } = this.props; |
|||
// Prevent removing file
|
|||
const onRemoveReturnValue = onRemove && onRemove(file); |
|||
if (onRemoveReturnValue === false) { |
|||
return; |
|||
} |
|||
const removedFileList = removeFileItem(file, this.state.fileList); |
|||
if (removedFileList) { |
|||
this.onChange({ |
|||
file, |
|||
fileList: removedFileList, |
|||
}); |
|||
} |
|||
} |
|||
|
|||
handleManualRemove = (file) => { |
|||
this.refs.upload.abort(file); |
|||
file.status = 'removed'; // eslint-disable-line
|
|||
this.handleRemove(file); |
|||
} |
|||
|
|||
onChange = (info) => { |
|||
if (!('fileList' in this.props)) { |
|||
this.setState({ fileList: info.fileList }); |
|||
} |
|||
|
|||
const { onChange } = this.props; |
|||
if (onChange) { |
|||
onChange(info); |
|||
} |
|||
} |
|||
|
|||
componentWillReceiveProps(nextProps) { |
|||
if ('fileList' in nextProps) { |
|||
this.setState({ |
|||
fileList: nextProps.fileList || [], |
|||
}); |
|||
} |
|||
} |
|||
|
|||
onFileDrop = (e) => { |
|||
this.setState({ |
|||
dragState: e.type, |
|||
}); |
|||
} |
|||
|
|||
clearProgressTimer() { |
|||
clearInterval(this.progressTimer); |
|||
} |
|||
|
|||
render() { |
|||
const { |
|||
prefixCls = '', showUploadList, listType, onPreview, |
|||
type, disabled, children, className, |
|||
} = this.props; |
|||
|
|||
const rcUploadProps = assign({}, { |
|||
onStart: this.onStart, |
|||
onError: this.onError, |
|||
onProgress: this.onProgress, |
|||
onSuccess: this.onSuccess, |
|||
}, this.props); |
|||
|
|||
delete rcUploadProps.className; |
|||
|
|||
const { showRemoveIcon, showPreviewIcon } = showUploadList as any; |
|||
const uploadList = showUploadList ? ( |
|||
<UploadList |
|||
listType={listType} |
|||
items={this.state.fileList} |
|||
onPreview={onPreview} |
|||
onRemove={this.handleManualRemove} |
|||
showRemoveIcon={showRemoveIcon} |
|||
showPreviewIcon={showPreviewIcon} |
|||
locale={this.getLocale()} |
|||
/> |
|||
) : null; |
|||
|
|||
if (type === 'drag') { |
|||
const dragCls = classNames(prefixCls, { |
|||
[`${prefixCls}-drag`]: true, |
|||
[`${prefixCls}-drag-uploading`]: this.state.fileList.some(file => file.status === 'uploading'), |
|||
[`${prefixCls}-drag-hover`]: this.state.dragState === 'dragover', |
|||
[`${prefixCls}-disabled`]: disabled, |
|||
}); |
|||
return ( |
|||
<span className={className}> |
|||
<div |
|||
className={dragCls} |
|||
onDrop={this.onFileDrop} |
|||
onDragOver={this.onFileDrop} |
|||
onDragLeave={this.onFileDrop} |
|||
> |
|||
<RcUpload {...rcUploadProps} ref="upload" className={`${prefixCls}-btn`}> |
|||
<div className={`${prefixCls}-drag-container`}> |
|||
{children} |
|||
</div> |
|||
</RcUpload> |
|||
</div> |
|||
{uploadList} |
|||
</span> |
|||
); |
|||
} |
|||
|
|||
const uploadButtonCls = classNames(prefixCls, { |
|||
[`${prefixCls}-select`]: true, |
|||
[`${prefixCls}-select-${listType}`]: true, |
|||
[`${prefixCls}-disabled`]: disabled, |
|||
}); |
|||
|
|||
const uploadButton = ( |
|||
<div className={uploadButtonCls} style={{ display: children ? '' : 'none'}}> |
|||
<RcUpload {...rcUploadProps} ref="upload" /> |
|||
</div> |
|||
); |
|||
|
|||
if (listType === 'picture-card') { |
|||
return ( |
|||
<span className={className}> |
|||
{uploadList} |
|||
{uploadButton} |
|||
</span> |
|||
); |
|||
} |
|||
return ( |
|||
<span className={className}> |
|||
{uploadButton} |
|||
{uploadList} |
|||
</span> |
|||
); |
|||
} |
|||
} |
@ -1,13 +0,0 @@ |
|||
export function getFileItem(file, fileList) { |
|||
const matchKey = file.uid ? 'uid' : 'name'; |
|||
return fileList.filter(item => item[matchKey] === file[matchKey])[0]; |
|||
} |
|||
|
|||
export function removeFileItem(file, fileList) { |
|||
const matchKey = file.uid ? 'uid' : 'name'; |
|||
const removed = fileList.filter(item => item[matchKey] !== file[matchKey]); |
|||
if (removed.length === fileList.length) { |
|||
return null; |
|||
} |
|||
return removed; |
|||
} |
@ -1,335 +1,5 @@ |
|||
import React from 'react'; |
|||
import RcUpload from 'rc-upload'; |
|||
import UploadList from './uploadList'; |
|||
import { getFileItem, removeFileItem } from './getFileItem'; |
|||
import classNames from 'classnames'; |
|||
import assign from 'object-assign'; |
|||
import { UploadProps, UploadLocale } from './interface'; |
|||
import Upload from './Upload'; |
|||
import Dragger from './Dragger'; |
|||
|
|||
function T() { |
|||
return true; |
|||
} |
|||
|
|||
// Fix IE file.status problem
|
|||
// via coping a new Object
|
|||
function fileToObject(file): any { |
|||
return { |
|||
lastModified: file.lastModified, |
|||
lastModifiedDate: file.lastModifiedDate, |
|||
name: file.filename || file.name, |
|||
size: file.size, |
|||
type: file.type, |
|||
uid: file.uid, |
|||
response: file.response, |
|||
error: file.error, |
|||
percent: 0, |
|||
originFileObj: file, |
|||
status: null, |
|||
}; |
|||
} |
|||
|
|||
/** |
|||
* 生成Progress percent: 0.1 -> 0.98 |
|||
* - for ie |
|||
*/ |
|||
function genPercentAdd() { |
|||
let k = 0.1; |
|||
const i = 0.01; |
|||
const end = 0.98; |
|||
return function (s) { |
|||
let start = s; |
|||
if (start >= end) { |
|||
return start; |
|||
} |
|||
|
|||
start += k; |
|||
k = k - i; |
|||
if (k < 0.001) { |
|||
k = 0.001; |
|||
} |
|||
return start * 100; |
|||
}; |
|||
} |
|||
|
|||
const defaultLocale: UploadLocale = { |
|||
uploading: '文件上传中', |
|||
removeFile: '删除文件', |
|||
uploadError: '上传错误', |
|||
previewFile: '预览文件', |
|||
}; |
|||
|
|||
export { UploadProps }; |
|||
|
|||
export function Dragger(props) { |
|||
return <Upload {...props} type="drag" style={{ height: props.height }}/>; |
|||
} |
|||
|
|||
export default class Upload extends React.Component<UploadProps, any> { |
|||
static Dragger = Dragger; |
|||
|
|||
static defaultProps = { |
|||
prefixCls: 'ant-upload', |
|||
type: 'select', |
|||
multiple: false, |
|||
action: '', |
|||
data: {}, |
|||
accept: '', |
|||
beforeUpload: T, |
|||
showUploadList: true, |
|||
listType: 'text', // or pictrue
|
|||
className: '', |
|||
disabled: false, |
|||
supportServerRender: true, |
|||
}; |
|||
|
|||
recentUploadStatus: boolean | PromiseLike<any>; |
|||
progressTimer: any; |
|||
refs: { |
|||
[key: string]: any; |
|||
upload: any; |
|||
}; |
|||
|
|||
constructor(props) { |
|||
super(props); |
|||
this.state = { |
|||
fileList: this.props.fileList || this.props.defaultFileList || [], |
|||
dragState: 'drop', |
|||
}; |
|||
} |
|||
|
|||
componentWillUnmount() { |
|||
this.clearProgressTimer(); |
|||
} |
|||
|
|||
getLocale() { |
|||
let locale = {}; |
|||
if (this.context.antLocale && this.context.antLocale.Upload) { |
|||
locale = this.context.antLocale.Upload; |
|||
} |
|||
return assign({}, defaultLocale, locale, this.props.locale); |
|||
} |
|||
|
|||
onStart = (file) => { |
|||
let targetItem; |
|||
let nextFileList = this.state.fileList.concat(); |
|||
if (file.length > 0) { |
|||
targetItem = file.map(f => { |
|||
const fileObject = fileToObject(f); |
|||
fileObject.status = 'uploading'; |
|||
return fileObject; |
|||
}); |
|||
nextFileList = nextFileList.concat(targetItem); |
|||
} else { |
|||
targetItem = fileToObject(file); |
|||
targetItem.status = 'uploading'; |
|||
nextFileList.push(targetItem); |
|||
} |
|||
this.onChange({ |
|||
file: targetItem, |
|||
fileList: nextFileList, |
|||
}); |
|||
// fix ie progress
|
|||
if (!(window as any).FormData) { |
|||
this.autoUpdateProgress(0, targetItem); |
|||
} |
|||
} |
|||
|
|||
autoUpdateProgress(_, file) { |
|||
const getPercent = genPercentAdd(); |
|||
let curPercent = 0; |
|||
this.clearProgressTimer(); |
|||
this.progressTimer = setInterval(() => { |
|||
curPercent = getPercent(curPercent); |
|||
this.onProgress({ |
|||
percent: curPercent, |
|||
}, file); |
|||
}, 200); |
|||
} |
|||
|
|||
onSuccess = (response, file) => { |
|||
this.clearProgressTimer(); |
|||
try { |
|||
if (typeof response === 'string') { |
|||
response = JSON.parse(response); |
|||
} |
|||
} catch (e) { /* do nothing */ |
|||
} |
|||
let fileList = this.state.fileList; |
|||
let targetItem = getFileItem(file, fileList); |
|||
// removed
|
|||
if (!targetItem) { |
|||
return; |
|||
} |
|||
targetItem.status = 'done'; |
|||
targetItem.response = response; |
|||
this.onChange({ |
|||
file: { ...targetItem }, |
|||
fileList, |
|||
}); |
|||
} |
|||
|
|||
onProgress = (e, file) => { |
|||
let fileList = this.state.fileList; |
|||
let targetItem = getFileItem(file, fileList); |
|||
// removed
|
|||
if (!targetItem) { |
|||
return; |
|||
} |
|||
targetItem.percent = e.percent; |
|||
this.onChange({ |
|||
event: e, |
|||
file: { ...targetItem }, |
|||
fileList: this.state.fileList, |
|||
}); |
|||
} |
|||
|
|||
onError = (error, response, file) => { |
|||
this.clearProgressTimer(); |
|||
let fileList = this.state.fileList; |
|||
let targetItem = getFileItem(file, fileList); |
|||
// removed
|
|||
if (!targetItem) { |
|||
return; |
|||
} |
|||
targetItem.error = error; |
|||
targetItem.response = response; |
|||
targetItem.status = 'error'; |
|||
this.onChange({ |
|||
file: { ...targetItem }, |
|||
fileList, |
|||
}); |
|||
} |
|||
|
|||
handleRemove(file) { |
|||
const { onRemove } = this.props; |
|||
// Prevent removing file
|
|||
const onRemoveReturnValue = onRemove && onRemove(file); |
|||
if (onRemoveReturnValue === false) { |
|||
return; |
|||
} |
|||
const removedFileList = removeFileItem(file, this.state.fileList); |
|||
if (removedFileList) { |
|||
this.onChange({ |
|||
file, |
|||
fileList: removedFileList, |
|||
}); |
|||
} |
|||
} |
|||
|
|||
handleManualRemove = (file) => { |
|||
this.refs.upload.abort(file); |
|||
file.status = 'removed'; // eslint-disable-line
|
|||
this.handleRemove(file); |
|||
} |
|||
|
|||
onChange = (info) => { |
|||
if (!('fileList' in this.props)) { |
|||
this.setState({ fileList: info.fileList }); |
|||
} |
|||
|
|||
const { onChange } = this.props; |
|||
if (onChange) { |
|||
onChange(info); |
|||
} |
|||
} |
|||
|
|||
componentWillReceiveProps(nextProps) { |
|||
if ('fileList' in nextProps) { |
|||
this.setState({ |
|||
fileList: nextProps.fileList || [], |
|||
}); |
|||
} |
|||
} |
|||
|
|||
onFileDrop = (e) => { |
|||
this.setState({ |
|||
dragState: e.type, |
|||
}); |
|||
} |
|||
|
|||
clearProgressTimer() { |
|||
clearInterval(this.progressTimer); |
|||
} |
|||
|
|||
render() { |
|||
const { |
|||
prefixCls = '', showUploadList, listType, onPreview, |
|||
type, disabled, children, className, |
|||
} = this.props; |
|||
|
|||
const rcUploadProps = assign({}, { |
|||
onStart: this.onStart, |
|||
onError: this.onError, |
|||
onProgress: this.onProgress, |
|||
onSuccess: this.onSuccess, |
|||
}, this.props); |
|||
|
|||
delete rcUploadProps.className; |
|||
|
|||
const { showRemoveIcon, showPreviewIcon } = showUploadList as any; |
|||
const uploadList = showUploadList ? ( |
|||
<UploadList |
|||
listType={listType} |
|||
items={this.state.fileList} |
|||
onPreview={onPreview} |
|||
onRemove={this.handleManualRemove} |
|||
showRemoveIcon={showRemoveIcon} |
|||
showPreviewIcon={showPreviewIcon} |
|||
locale={this.getLocale()} |
|||
/> |
|||
) : null; |
|||
|
|||
if (type === 'drag') { |
|||
const dragCls = classNames(prefixCls, { |
|||
[`${prefixCls}-drag`]: true, |
|||
[`${prefixCls}-drag-uploading`]: this.state.fileList.some(file => file.status === 'uploading'), |
|||
[`${prefixCls}-drag-hover`]: this.state.dragState === 'dragover', |
|||
[`${prefixCls}-disabled`]: disabled, |
|||
}); |
|||
return ( |
|||
<span className={className}> |
|||
<div |
|||
className={dragCls} |
|||
onDrop={this.onFileDrop} |
|||
onDragOver={this.onFileDrop} |
|||
onDragLeave={this.onFileDrop} |
|||
> |
|||
<RcUpload {...rcUploadProps} ref="upload" className={`${prefixCls}-btn`}> |
|||
<div className={`${prefixCls}-drag-container`}> |
|||
{children} |
|||
</div> |
|||
</RcUpload> |
|||
</div> |
|||
{uploadList} |
|||
</span> |
|||
); |
|||
} |
|||
|
|||
const uploadButtonCls = classNames(prefixCls, { |
|||
[`${prefixCls}-select`]: true, |
|||
[`${prefixCls}-select-${listType}`]: true, |
|||
[`${prefixCls}-disabled`]: disabled, |
|||
}); |
|||
|
|||
const uploadButton = ( |
|||
<div className={uploadButtonCls} style={{ display: children ? '' : 'none'}}> |
|||
<RcUpload {...rcUploadProps} ref="upload" /> |
|||
</div> |
|||
); |
|||
|
|||
if (listType === 'picture-card') { |
|||
return ( |
|||
<span className={className}> |
|||
{uploadList} |
|||
{uploadButton} |
|||
</span> |
|||
); |
|||
} |
|||
return ( |
|||
<span className={className}> |
|||
{uploadButton} |
|||
{uploadList} |
|||
</span> |
|||
); |
|||
} |
|||
} |
|||
Upload.Dragger = Dragger; |
|||
export default Upload; |
|||
|
@ -0,0 +1,58 @@ |
|||
export function T() { |
|||
return true; |
|||
} |
|||
|
|||
// Fix IE file.status problem
|
|||
// via coping a new Object
|
|||
export function fileToObject(file): any { |
|||
return { |
|||
lastModified: file.lastModified, |
|||
lastModifiedDate: file.lastModifiedDate, |
|||
name: file.filename || file.name, |
|||
size: file.size, |
|||
type: file.type, |
|||
uid: file.uid, |
|||
response: file.response, |
|||
error: file.error, |
|||
percent: 0, |
|||
originFileObj: file, |
|||
status: null, |
|||
}; |
|||
} |
|||
|
|||
/** |
|||
* 生成Progress percent: 0.1 -> 0.98 |
|||
* - for ie |
|||
*/ |
|||
export function genPercentAdd() { |
|||
let k = 0.1; |
|||
const i = 0.01; |
|||
const end = 0.98; |
|||
return function (s) { |
|||
let start = s; |
|||
if (start >= end) { |
|||
return start; |
|||
} |
|||
|
|||
start += k; |
|||
k = k - i; |
|||
if (k < 0.001) { |
|||
k = 0.001; |
|||
} |
|||
return start * 100; |
|||
}; |
|||
} |
|||
|
|||
export function getFileItem(file, fileList) { |
|||
const matchKey = file.uid ? 'uid' : 'name'; |
|||
return fileList.filter(item => item[matchKey] === file[matchKey])[0]; |
|||
} |
|||
|
|||
export function removeFileItem(file, fileList) { |
|||
const matchKey = file.uid ? 'uid' : 'name'; |
|||
const removed = fileList.filter(item => item[matchKey] !== file[matchKey]); |
|||
if (removed.length === fileList.length) { |
|||
return null; |
|||
} |
|||
return removed; |
|||
} |
Loading…
Reference in new issue