提交 b6ec8b9e 编写于 作者: Y yiminghe

Merge pull request #226 from ant-design/upload-filelist

重构 Upload API
## 0.8.1 (not ready)
## 0.9.0 (not ready)
### Upload
**重构了 API 接口,不向下兼容**,支持自定义的功能扩展。
* 新增 `onChange(file) {}` 接口,移出原来的 `onSuccess``onProgess``onError` 等接口。
* 新增 `fileList``defaultFileList` 属性,以满足更多的自定义功能,具体见演示。
* 设置 fileList 数组项的 url 属性可以作为链接展示在文件列表中方便下载。
### Table
......@@ -7,12 +16,6 @@
* 修复远程模式 loading 失效的问题。[9b8abb2](https://github.com/ant-design/ant-design/commit/9b8abb219934c246970a84200818aa8f85974bdf)
*[reqwest-without-xhr2](http://npmjs.com/reqwest-without-xhr2) 代替了 reqwest,解决某些开发环境下 xhr2 依赖的问题。
### Upload
* 新增 `onRemove(file) {}` 接口,作为移除上传文件的回调。
* 新增 `urlResolver(res) {}` 接口,可以拿到请求回调数据里的远程文件地址,展示在文件列表中方便下载。
* 新增 `limit` 属性,用于限制文件上传列表的数量。
### Notification
* 修复不会自动消失的问题。[23fce55](https://github.com/ant-design/ant-design/commit/23fce559b0b2faf4e0b686a92dbcdd045727a464)
......
......@@ -35,6 +35,7 @@
| allowClear | 显示清除按钮 | | false |
| placeholder | 选择框默认文字 | string | 无 |
| searchPlaceholder | 搜索框默认文字 | string | 无 |
| optionFilterProp | 输入项过滤对应的 option 属性 | string | value |
| combobox | 输入框自动提示模式 | | false |
| size | 选择框大小 | String | 无 |
......
......@@ -10,13 +10,12 @@
var Upload = antd.Upload;
var props = {
description: '支持扩展名为: .rar .zip ...',
action: '/upload.do',
data: {},
accept: '',
uploadTip: '',
onStart(file){
console.log(file.uid);
onChange(info) {
if (info.file.status !== 'uploading') {
console.log(info.file);
console.log(info.fileList);
}
}
};
......@@ -25,7 +24,6 @@ React.render(
<button className="ant-btn ant-btn-ghost">
<i className="anticon anticon-upload"></i> 点击上传
</button>
</Upload>,
document.getElementById('components-upload-demo-basic')
);
</Upload>
, document.getElementById('components-upload-demo-basic'));
````
# 传入已上传的文件
- order: 1
对已上传的文件进行编辑。
---
````jsx
var Upload = antd.Upload;
var props = {
action: '/upload.do',
onChange(info) {
if (info.file.status !== 'uploading') {
console.log(info.file);
console.log(info.fileList);
}
},
defaultFileList: [{
uid: -1,
name: 'xxx.png',
status: 'done',
url: 'http://www.baidu.com/xxx.png'
}, {
uid: -2,
name: 'yyy.png',
status: 'done',
url: 'http://www.baidu.com/yyy.png'
}]
};
React.render(
<Upload {...props}>
<button className="ant-btn ant-btn-ghost">
<i className="anticon anticon-upload"></i> 点击上传
</button>
</Upload>
, document.getElementById('components-upload-demo-defaultfilelist'));
````
# 拖拽上传
- order: 2
- order: 4
样式简单一些。
......@@ -11,10 +11,7 @@ var Dragger = antd.Upload.Dragger;
var props = {
name: 'file',
action: '/upload.do',
data: {},
accept: '',
uploadTip: ''
action: '/upload.do'
};
React.render(
......
# 拖拽上传
- order: 1
- order: 3
可以把文件拖入指定区域,完成上传,同样支持点击上传。
......@@ -11,10 +11,7 @@ var Dragger = antd.Upload.Dragger;
var props = {
name: 'file',
action: '/upload.do',
data: {},
accept: 'i',
uploadTip: ''
action: '/upload.do'
};
React.render(
......
# 完全控制的上传列表
- order: 2
使用 `fileList` 对列表进行完全控制,可以实现各种自定义功能,以下演示三种情况:
1) 上传列表数量的限制。
2) 读取远程路径并显示链接。
3) 按照服务器返回信息筛选成功上传的文件。
---
````jsx
var Upload = antd.Upload;
var fileList = [{
uid: -1,
name: 'xxx.png',
status: 'done',
url: 'http://www.baidu.com/xxx.png'
}];
var MyUpload = React.createClass({
getInitialState() {
return {
fileList: fileList
};
},
handleChange(info) {
let fileList = info.fileList;
// 1. 上传列表数量的限制
// 只显示最近上传的一个,旧的会被新的顶掉
fileList = fileList.slice(-2);
// 2. 读取远程路径并显示链接
fileList = fileList.map(function(file) {
if (file.response) {
// 组件会将 file.url 作为链接进行展示
file.url = JSON.parse(file.response).url;
}
return file;
});
// 3. 按照服务器返回信息筛选成功上传的文件
fileList = fileList.filter(function(file) {
if (file.response) {
return JSON.parse(file.response).status === 'success';
}
return true;
});
this.setState({
fileList: fileList
});
},
render() {
var props = {
action: '/upload.do',
onChange: this.handleChange
};
return <Upload {...props} fileList={this.state.fileList}>
<button className="ant-btn ant-btn-ghost">
<i className="anticon anticon-upload"></i> 点击上传
</button>
</Upload>;
}
});
React.render(<MyUpload />, document.getElementById('components-upload-demo-filelist'));
````
# 文件列表限制
- order: 3
`limit` 属性控制文件列表数的上限。如设为 1 时,表示只能上传一个文件,新文件会顶掉旧文件。
---
````jsx
var Upload = antd.Upload;
var props = {
description: '支持扩展名为: .rar .zip ...',
action: '/upload.do',
data: {},
accept: '',
uploadTip: '',
limit: 1,
onStart(file){
console.log(file.uid);
}
};
React.render(
<Upload {...props}>
<button className="ant-btn ant-btn-ghost">
<i className="anticon anticon-upload"></i> 点击上传,只支持一个文件
</button>
</Upload>,
document.getElementById('components-upload-demo-limit')
);
````
......@@ -2,7 +2,7 @@ export default function getFileItem(file, fileList) {
let matchWay = (!file.uid) ? 'byName' : 'byUid';
let target = fileList.filter((item) => {
if (matchWay === 'byName') {
return item.filename === file.filename;
return item.name === file.name;
} else {
return item.uid === file.uid;
}
......
......@@ -5,7 +5,6 @@ import Message from '../message';
import UploadList from './uploadList';
import getFileItem from './getFileItem';
const prefixCls = 'ant-upload';
let fileIndex = 0;
function noop() {
}
......@@ -13,65 +12,78 @@ function noop() {
const AntUpload = React.createClass({
getInitialState() {
return {
downloadList: []
fileList: this.props.fileList || this.props.defaultFileList || []
};
},
onStart(file) {
let nextDownloadList = this.state.downloadList;
nextDownloadList.push({
index: fileIndex++,
uid: file.uid || '',
filename: file.name,
let nextFileList = this.state.fileList.concat();
file.status = 'uploading';
nextFileList.push(file);
this.onChange({
file: file,
status: 'downloading'
fileList: nextFileList
});
if (nextDownloadList.length === this.props.limit + 1) {
nextDownloadList = nextDownloadList.slice(1);
}
this.setState({
downloadList: nextDownloadList
});
this.props.onStart(file);
},
removeFile(file){
var downloadList = this.state.downloadList.concat();
let targetItem = getFileItem(file, downloadList);
var index = downloadList.indexOf(targetItem);
removeFile(file) {
file.status = 'removed';
let fileList = this.state.fileList.concat();
let targetItem = getFileItem(file, fileList);
let index = fileList.indexOf(targetItem);
if (index !== -1) {
downloadList.splice(index, 1);
fileList.splice(index, 1);
return fileList;
}
this.setState({
downloadList: downloadList
});
return null;
},
onSuccess(ret, file) {
var res = this.props.onSuccess(ret, file);
if (res !== false) {
var downloadList = this.state.downloadList.concat();
Message.success(file.name + '上传完成');
let targetItem = getFileItem(file, downloadList);
onSuccess(response, file) {
let fileList = this.state.fileList.concat();
Message.success(file.name + '上传完成。');
let targetItem = getFileItem(file, fileList);
// 之前已经删除
if (targetItem) {
targetItem.status = 'done';
// 解析出文件上传后的远程地址
if (typeof this.props.urlResolver === 'function') {
targetItem.url = this.props.urlResolver(ret);
}
this.setState({
downloadList: downloadList
targetItem.response = response;
this.onChange({
file: targetItem,
fileList: this.state.fileList
});
} else {
this.removeFile(file);
}
},
onProgress(e, file) {
this.props.onProgress(e, file);
let fileList = this.state.fileList;
let targetItem = getFileItem(file, fileList);
if (targetItem) {
this.onChange({
event: e,
file: file,
fileList: this.state.fileList
});
}
},
onError(err, responce, file) {
Message.error(file.name + ' 上传失败');
this.removeFile(file);
this.props.onError(err, responce, file);
onError(error, response, file) {
Message.error(file.name + ' 上传失败。');
file.error = error;
file.response = response;
this.handleRemove(file);
},
handleRemove(file) {
let fileList = this.removeFile(file);
if (fileList) {
this.onChange({
file: file,
fileList: fileList
});
}
},
onRemove(file){
this.props.onRemove(file);
onChange(info) {
// 1. 有设置外部属性时不改变 fileList
// 2. 上传中状态(info.event)不改变 fileList
if (!('fileList' in this.props) && !info.event) {
this.setState({
fileList: info.fileList
});
}
this.props.onChange(info);
},
getDefaultProps() {
return {
......@@ -81,20 +93,16 @@ const AntUpload = React.createClass({
action: '',
data: {},
accept: '',
uploadTip: '',
onStart: noop,
onError: noop,
onSuccess: noop,
onProgress: noop,
onRemove: noop,
limit: Number.MAX_VALUE,
urlResolver: function(ret) {
try {
return JSON.parse(ret).url;
} catch(e) {}
}
onChange: noop,
};
},
componentWillReceiveProps(nextProps) {
if ('fileList' in nextProps) {
this.setState({
fileList: nextProps.fileList
});
}
},
render() {
let type = this.props.type || 'select';
let props = assign({}, this.props, {
......@@ -121,9 +129,8 @@ const AntUpload = React.createClass({
{this.props.children}
</Upload>
</div>
<UploadList items={this.state.downloadList}
onRemove={this.onRemove}
limit={props.limit} />
<UploadList items={this.state.fileList}
onRemove={this.handleRemove}/>
</div>
);
}
......
......@@ -18,30 +18,41 @@
## API
| 参数 | 说明 | 类型 | 默认值 |
|----------- |--------------------------------------------------------- | ---------- |-------|
| name | 可选参数, 上传的文件 | String | file |
| 参数 | 说明 | 类型 | 默认值|
|------------|--------------------------------------------------------------| ----------- |-------|
| name | 可选参数, 上传的文件 | String | file |
| action | 必选参数, 上传的地址 | String | 无 |
| data | 可选参数, 上传所需参数 | Object | 无 |
| multiple | 可选参数, 是否支持多选文件,支持ie10+ | Boolean | false |
| accept | 可选参数, 接受上传的文件类型, 详见input accept Attribute | String | 无 |
| onError | 可选参数, error callback |Function | 无 |
| onSuccess | 可选参数, success callback | Function | 无 |
| onProgress | 可选参数, progress callback, 现代浏览器有效 | Function | 无 |
| urlResolver| 通过解析请求返回数据,获得文件上传的远程地址 | Function | `function() { return JSON.parse(ret).url }` |
| limit | 文件上传数量的限制 | Number | Number.MAX_VALUE |
| multiple | 可选参数, 是否支持多选文件,支持 `ie10+` | Boolean | false |
| accept | 可选参数, 接受上传的文件类型, 详见 input accept Attribute | String | 无 |
| onChange | 可选参数, 上传文件改变时的状态,详见 onChange | Function | 无 |
### onError
### onChange
错误回调,有三个参数返回
文件状态改变的回调,返回为
1. `err` 请求返回错误信息
2. `responce` 请求响应,包括错误状态码等信息
3. `file` 错误的文件对象
```js
{
file: { ... },
fileList: [ ... ],
event: { ... }
}
```
### onSuccess
1. `file` 当前操作的文件对象。
成功回调,返回两个参数
```js
{
uid: 'uid', // 文件唯一标识,建议设置为负数,防止和内部产生的 id 冲突
name: 'xx.png' // 文件名
status: 'done', // 状态:uploading done
response: '{"status":"success"}' // 服务端响应内容
}
```
1. `result` 上传图片返回结果
2. `file` 文件对象
2. `fileList` 当前的文件列表。
3. `event` 上传中的服务端响应内容,包含了上传进度等信息,高级浏览器支持。
## 显示下载链接
请使用 fileList 属性设置数组项的 url 属性进行展示控制。
......@@ -22,36 +22,29 @@ export default React.createClass({
}
},
handleClose(file) {
let items = this.state.items;
let removeItem = getFileItem(file, items);
if (removeItem) {
items.splice(items.indexOf(removeItem), 1);
}
this.setState({
items: items
});
this.props.onRemove(file.file);
this.props.onRemove(file);
},
render() {
let items = this.state.items;
let downloadItem = (file) => {
let statusIcon = file.status === 'done' ? <i className={'anticon anticon-check ' + prefixCls + '-success-icon'}></i> :
let list = this.state.items.map((file) => {
let statusIcon = file.status === 'done' ?
<i className={'anticon anticon-check ' + prefixCls + '-success-icon'}></i> :
<i className="anticon anticon-loading"></i>;
let filename = file.url ? <a className={prefixCls + '-item-name'} href={file.url} _target="_blank">{file.filename}</a> :
<b className={prefixCls + '-item-name'}>{file.filename}</b>;
let filename = file.url ?
<a className={prefixCls + '-item-name'} href={file.url} _target="_blank">{file.name}</a> :
<b className={prefixCls + '-item-name'}>{file.name}</b>;
return (
<div className={prefixCls + '-list-item'} key={file.index}>
<div className={prefixCls + '-list-item'} key={file.uid}>
{statusIcon}
{filename}
<i className="anticon anticon-cross" ref="theCloseBtn"
onClick={this.handleClose.bind(this, file)}></i>
</div>
);
};
return (<div className={prefixCls + '-list'}>
});
return <div className={prefixCls + '-list'}>
<Animate transitionName={prefixCls + '-margin-top'}>
{items.map(downloadItem)}
{list}
</Animate>
</div>);
</div>;
}
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册