plupload-antd-react
Version:
Fragment upload component integrating plupload and antd
289 lines (268 loc) • 10.6 kB
JSX
import _ from 'lodash';
import React from 'react';
import {Button, Space, List, Row, Col, Progress, message} from 'antd'
import {DeleteOutlined, UploadOutlined, PaperClipOutlined, LoadingOutlined, FileAddOutlined} from '@ant-design/icons'
import style from './index.less'
const plUploadJs = require('plupload/js/plupload.full.min')
const EVENTS = [
'PostInit', 'Browse', 'Refresh', 'StateChanged', 'QueueChanged', 'OptionChanged',
'BeforeUpload', 'UploadProgress', 'FileFiltered', 'FilesAdded', 'FilesRemoved', 'FileUploaded', 'ChunkUploaded',
'UploadComplete', 'Destroy', 'Error'
];
class PlUpload extends React.Component {
id = new Date().valueOf();
container = null
state = {
files: [], uploadState: false, progress: {}
}
checkUploader() {
return plUploadJs !== undefined;
}
runUploader = () => {
this.initUploader();
this.uploader.init();
EVENTS.forEach((event) => {
const handler = this.props['on' + event];
if (typeof handler === 'function') {
this.uploader.bind(event, handler);
}
});
// 当文件添加到上传队列后触发监听函数
this.uploader.bind('FilesAdded', (up, files) => {
if (_.get(this.props, 'multi_selection') === false) {
this.clearAllFiles();
} else {
this.clearFailedFiles();
}
const f = this.state.files;
_.map(files, (file) => {
if (up.files.length > this.props.maxLength || f.length == this.props.maxLength) {
message.error(`最多上传${this.props.maxLength}个文件`);
this.uploader.stop()
this.removeFile(file.id)
} else {
f.push(file);
}
});
this.setState({files: f}, () => {
if (this.props.autoUpload === true) {
this.uploader.start();
}
});
});
// 当文件从上传队列移除后
this.uploader.bind('FilesRemoved', (up, rmFiles) => {
const stateFiles = this.state.files;
const files = _.filter(stateFiles, (file) => {
return -1 !== _.find(rmFiles, {id: file.id});
});
this.setState({files: files});
});
//当上传队列的状态发生改变时
this.uploader.bind('StateChanged', (up) => {
if (up.state === plUploadJs.STARTED && this.state.uploadState === false) {
this.setState({uploadState: true});
}
if (up.state !== plUploadJs.STARTED && this.state.uploadState === true) {
this.setState({uploadState: false});
}
});
//队列中的某一个文件上传完成后
this.uploader.bind('FileUploaded', (up, file, responseObject) => {
const stateFiles = JSON.parse(JSON.stringify(this.state.files));
const response = JSON.parse(responseObject.response)
_.map(stateFiles, (val, key) => {
if (val.id === file.id) {
val.uploaded = true;
val.response = response
val.url = response.realUrl
stateFiles[key] = val;
}
});
this.setState({files: stateFiles});
});
// 当发生错误时触发监听函数
this.uploader.bind('Error', (up, err) => {
switch (err.code) {
case -600:
return message.error(`上传文件最大为${this.props.maxSize}`)
case -601:
return message.error(`上传文件类型为${this.props.accept}`);
}
if (_.isUndefined(err.file) !== true) {
const stateFiles = this.state.files;
_.map(stateFiles, (val, key) => {
if (val.id === err.file.id) {
val.error = err;
stateFiles[key] = val;
}
});
this.setState({files: stateFiles});
}
});
// 会在文件上传过程中不断触发,可以用此事件来显示上传进度监听函数
this.uploader.bind('UploadProgress', (up, file) => {
const stateProgress = this.state.progress;
stateProgress[file.id] = file.percent;
this.setState({progress: stateProgress});
});
}
componentDidMount() {
if (this.checkUploader()) {
this.runUploader();
} else {
setTimeout(() => {
if (this.checkUploader()) {
this.runUploader();
} else {
console.warn('Plupload has not initialized');
}
}, 500);
}
}
shouldComponentUpdate(nextProps, nextState) {
if (this.props != nextProps) {
return false
}
return true
}
componentDidUpdate(prevProps, prevState) {
if (this.checkUploader()) {
this.uploader.refresh();
}
if(this.state.files != prevState.files){
if(this.state?.files?.length && this.state.files.every(item => item.uploaded) ||
this.state?.files?.length == 0
){
this.props.getFileList(this.state.files);
}
}
}
getComponentId = () => {
return this.props.id || 'react_plupload_' + this.id;
}
initUploader = () => {
if(this.props.defaultFileList){
this.setState({files: this.props.defaultFileList})
}
this.uploader = new plUploadJs.Uploader(_.extend({
container: `plupload_${this.props.id}`,
runtimes: 'html5',
multipart: true,
chunk_size: '1mb',
browse_button: this.getComponentId(),
url: '/upload',
filters: {
mime_types: this.props.accept ? [
{ title : "files filters", extensions : this.props.accept}
]: [],
max_file_size: this.props.maxSize
}
}, this.props));
}
// 选择文件列-
list = () => {
return _.map(this.state.files, (val) => {
const removeFile = (e) => {
e.preventDefault();
this.removeFile(val.id);
};
return (
<React.Fragment key={val.id}>
<Row gutter={10} wrap={false} className={style.listItem} style={{color: '#8c8c8c'}}>
<Col flex='15px'>{this.state.uploadState && val.uploaded !== true ? <LoadingOutlined/> : <PaperClipOutlined/>}</Col>
<Col flex='auto' className={[style.fileLink, !val.uploaded && style.fileAdd]}>
<a target='_blank' title={val.url} href={val.url}>{val.name}</a>
</Col>
<Col flex="15px" onClick={removeFile}><DeleteOutlined className={style.deleteBtn}/></Col>
</Row>
{
(() => {
if (this.state.uploadState === true && val.uploaded !== true && _.isUndefined(val.error)) {
const percent = this.state.progress[val.id] || 0;
return (
<Row style={{width: '100%'}}>
<Progress size='small' percent={percent} showInfo={false}/>
</Row>
)
}
})()
}
{
(() => {
if (!_.isUndefined(val.error)) {
return (
<div className={'alert alert-danger'}>
{'Error: ' + val.error.code + ', Message: ' + val.error.message}
</div>
)
}
})()
}
</React.Fragment>
)
});
}
clearAllFiles = () => {
const state = _.filter(this.state.files, (file) => {
this.uploader.removeFile(file.id);
});
this.setState({files: state});
}
clearFailedFiles = () => {
const state = _.filter(this.state.files, (file) => {
if (file.error) {
this.uploader.removeFile(file.id);
}
return !file.error;
});
this.setState({files: state});
}
removeFile = (id) => {
this.uploader.removeFile(id);
const state = _.filter(this.state.files, (file) => {
return file.id !== id;
});
this.setState({files: state});
}
doUpload = (e) => {
e.preventDefault();
this.uploader.start();
}
render() {
const list = this.list();
const {files} = this.state
return (
<div id={`plupload_${this.props.id}`} className={'my-list'} ref={ref => (this.container = ref)}>
<Space>
{/*选择文件*/}
<Button
id={this.getComponentId()}
icon={this.props.autoUpload ? <UploadOutlined/> : <FileAddOutlined /> }
>
{this.props.buttonSelect || 'select'}
</Button>
{/*上传文件*/}
{
!this.props.autoUpload &&
<Button
type='primary'
icon={<UploadOutlined/>}
onClick={this.doUpload}
disabled={files.length === 0 || files.every((listItem) => listItem.uploaded) ? 'disabled' : false}
>
{this.props.buttonUpload || 'upload'}
</Button>
}
</Space>
{list.length > 0 &&
<List
size={'small'}
dataSource={list}
renderItem={item => item}
/>}
</div>
);
}
}
export default PlUpload;