fix: Support cancel upload && show progress

1. Support cancel upload file
3. Show upload progress when upload image
4. Fix delete file after select file

Change-Id: I021fee23980e203be68252246d5eb1b5418774fc
This commit is contained in:
zhangjingwei 2021-07-30 09:34:00 +08:00 committed by Jingwei.Zhang
parent 4a8970a134
commit 1b122869f7
8 changed files with 111 additions and 35 deletions

View File

@ -15,13 +15,13 @@
import React from 'react'; import React from 'react';
import { isFunction, has, isObject, isEmpty } from 'lodash'; import { isFunction, has, isObject, isEmpty } from 'lodash';
import Notify from 'components/Notify'; import Notify from 'components/Notify';
import { Row, Col, Form, Button, Spin } from 'antd'; import { Row, Col, Form, Button, Spin, Progress } from 'antd';
import classnames from 'classnames'; import classnames from 'classnames';
import { InfoCircleOutlined } from '@ant-design/icons'; import { InfoCircleOutlined } from '@ant-design/icons';
import { isAdminPage, firstUpperCase, unescapeHtml } from 'utils/index'; import { isAdminPage, firstUpperCase, unescapeHtml } from 'utils/index';
import { parse } from 'qs'; import { parse } from 'qs';
import FormItem from 'components/FormItem'; import FormItem from 'components/FormItem';
import { CancelToken } from 'axios';
import styles from './index.less'; import styles from './index.less';
export default class BaseForm extends React.Component { export default class BaseForm extends React.Component {
@ -36,6 +36,7 @@ export default class BaseForm extends React.Component {
// eslint-disable-next-line react/no-unused-state // eslint-disable-next-line react/no-unused-state
formData: {}, formData: {},
isSubmitting: false, isSubmitting: false,
percent: '',
}; };
this.values = {}; this.values = {};
@ -45,6 +46,12 @@ export default class BaseForm extends React.Component {
this.tipRef = React.createRef(); this.tipRef = React.createRef();
this.codeError = false; this.codeError = false;
this.currentFormValue = {}; this.currentFormValue = {};
this.cancel = null;
this.cancelToken = this.hasRequestCancelCallback
? new CancelToken((c) => {
this.cancel = c;
})
: null;
this.init(); this.init();
} }
@ -231,6 +238,10 @@ export default class BaseForm extends React.Component {
.map((it) => it.name); .map((it) => it.name);
} }
get hasRequestCancelCallback() {
return false;
}
updateContext = (allFields) => { updateContext = (allFields) => {
const { updateContext } = this.props; const { updateContext } = this.props;
updateContext && updateContext(allFields); updateContext && updateContext(allFields);
@ -314,7 +325,12 @@ export default class BaseForm extends React.Component {
); );
}; };
onCancel = () => {}; onCancel = () => {
if (this.isSubmitting && this.cancel) {
this.cancel();
Notify.success(t('Cancel upload successfully.'));
}
};
getChangedFieldsValue = (changedFields, name) => { getChangedFieldsValue = (changedFields, name) => {
const value = changedFields[name]; const value = changedFields[name];
@ -379,7 +395,10 @@ export default class BaseForm extends React.Component {
}; };
onClickCancel = () => { onClickCancel = () => {
this.routing.push(this.listUrl); this.onCancel();
if (this.listUrl) {
this.routing.push(this.listUrl);
}
}; };
updateDefaultValue = () => { updateDefaultValue = () => {
@ -396,6 +415,31 @@ export default class BaseForm extends React.Component {
}); });
}; };
onUploadProgress = (progressEvent) => {
const { loaded, total } = progressEvent;
const percent = Math.floor((loaded / total) * 100);
this.setState({
percent,
});
};
getUploadRequestConf = () => {
return {
onUploadProgress: this.onUploadProgress,
canToken: this.cancelToken,
};
};
checkContextValue() {
const { context } = this.props;
const names = this.nameForStateUpdate;
if (isEmpty(context)) {
return false;
}
const item = names.find((name) => has(context, name));
return !!item;
}
updateState() { updateState() {
// save linkage data to state // save linkage data to state
const { context } = this.props; const { context } = this.props;
@ -418,16 +462,6 @@ export default class BaseForm extends React.Component {
}); });
} }
checkContextValue() {
const { context } = this.props;
const names = this.nameForStateUpdate;
if (isEmpty(context)) {
return false;
}
const item = names.find((name) => has(context, name));
return !!item;
}
init() { init() {
this.store = {}; this.store = {};
} }
@ -544,6 +578,21 @@ export default class BaseForm extends React.Component {
); );
} }
renderSubmittingTip() {
if (!this.hasRequestCancelCallback) {
return;
}
const { percent } = this.state;
return (
<div className={styles['submit-tip']}>
{t('Upload progress')}
<div className={styles['progress-wrapper']}>
<Progress percent={percent} size="small" />
</div>
</div>
);
}
render() { render() {
const wrapperPadding = const wrapperPadding =
this.listUrl || this.isStep || (this.isModal && this.tips) this.listUrl || this.isStep || (this.isModal && this.tips)
@ -564,7 +613,7 @@ export default class BaseForm extends React.Component {
<div <div
className={classnames(styles.wrapper, wrapperPadding, this.className)} className={classnames(styles.wrapper, wrapperPadding, this.className)}
> >
<Spin spinning={this.isSubmitting}> <Spin spinning={this.isSubmitting} tip={this.renderSubmittingTip()}>
{tips} {tips}
<div className={classnames(styles.form, 'sl-form')} style={formStyle}> <div className={classnames(styles.form, 'sl-form')} style={formStyle}>
{this.renderForms()} {this.renderForms()}

View File

@ -110,3 +110,12 @@
align-items: center; align-items: center;
justify-content: left; justify-content: left;
} }
.submit-tip {
font-size: 16px;
// margin-top: 16px;
display: inline-block;
}
.progress-wrapper {
width: 170px;
}

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Upload, Button, message } from 'antd'; import { Upload, Button } from 'antd';
import { UploadOutlined } from '@ant-design/icons'; import { UploadOutlined } from '@ant-design/icons';
import { isArray } from 'lodash'; import { isArray } from 'lodash';
@ -36,19 +36,7 @@ export default class index extends Component {
}; };
} }
onChange = (info) => { onChange = (file) => {
if (info.file.status !== 'uploading') {
// eslint-disable-next-line no-console
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.`);
}
};
beforeUpload = (file) => {
this.setState( this.setState(
{ {
file, file,
@ -60,6 +48,24 @@ export default class index extends Component {
} }
} }
); );
};
handleChange = (info) => {
const { file, fileList = [] } = info;
const { status } = file || {};
if (status === 'removed' && fileList.length === 0) {
this.onChange(null);
}
if (!status) {
this.onChange(file);
}
if (info.file.status !== 'uploading') {
// eslint-disable-next-line no-console
console.log(file, fileList);
}
};
beforeUpload = () => {
return false; return false;
}; };
@ -79,7 +85,7 @@ export default class index extends Component {
headers: { headers: {
authorization: 'authorization-text', authorization: 'authorization-text',
}, },
onChange: this.onChange, onChange: this.handleChange,
progress: this.progress, progress: this.progress,
beforeUpload: this.beforeUpload, beforeUpload: this.beforeUpload,
fileList, fileList,

View File

@ -15,7 +15,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { inject, observer } from 'mobx-react'; import { inject, observer } from 'mobx-react';
import { Modal, Button, Tooltip } from 'antd'; import { Modal, Button, Tooltip } from 'antd';
import { isArray, isFunction } from 'lodash'; import { isArray, isFunction, isBoolean } from 'lodash';
import Confirm from 'components/Confirm'; import Confirm from 'components/Confirm';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Notify from 'components/Notify'; import Notify from 'components/Notify';
@ -381,7 +381,7 @@ class ActionButton extends Component {
return this.formRef.current.wrappedInstance.onClickSubmit( return this.formRef.current.wrappedInstance.onClickSubmit(
(success, fail) => { (success, fail) => {
this.handleSubmitLoading(); this.handleSubmitLoading();
this.onClickModalActionCancel(); this.onClickModalActionCancel(true);
this.onCallback(success, fail); this.onCallback(success, fail);
}, },
() => { () => {
@ -391,7 +391,10 @@ class ActionButton extends Component {
); );
}; };
onClickModalActionCancel = () => { onClickModalActionCancel = (finish) => {
if (!isBoolean(finish)) {
this.formRef.current.wrappedInstance.onClickCancel();
}
const { onCancelAction } = this.props; const { onCancelAction } = this.props;
this.setState( this.setState(
{ {

View File

@ -161,7 +161,7 @@ export default class BaseTable extends React.Component {
} }
get itemActions() { get itemActions() {
const { itemActions } = this.props; const { itemActions = {} } = this.props;
return itemActions; return itemActions;
} }

View File

@ -214,6 +214,7 @@
"Cancel Download": "Cancel Download", "Cancel Download": "Cancel Download",
"Cancel Select": "Cancel Select", "Cancel Select": "Cancel Select",
"Cancel Transfer": "Cancel Transfer", "Cancel Transfer": "Cancel Transfer",
"Cancel upload successfully.": "Cancel upload successfully.",
"Capacity (GB)": "Capacity (GB)", "Capacity (GB)": "Capacity (GB)",
"Category": "Category", "Category": "Category",
"CentOS": "CentOS", "CentOS": "CentOS",
@ -1655,6 +1656,7 @@
"Updated At": "Updated At", "Updated At": "Updated At",
"Updating": "Updating", "Updating": "Updating",
"Updating Password": "Updating Password", "Updating Password": "Updating Password",
"Upload progress": "Upload progress",
"Uploading": "Uploading", "Uploading": "Uploading",
"Usage Type": "Usage Type", "Usage Type": "Usage Type",
"Usb Controller": "Usb Controller", "Usb Controller": "Usb Controller",

View File

@ -214,6 +214,7 @@
"Cancel Download": "取消下载", "Cancel Download": "取消下载",
"Cancel Select": "取消选择", "Cancel Select": "取消选择",
"Cancel Transfer": "取消云硬盘转让", "Cancel Transfer": "取消云硬盘转让",
"Cancel upload successfully.": "取消上传成功。",
"Capacity (GB)": "容量(GB)", "Capacity (GB)": "容量(GB)",
"Category": "类别", "Category": "类别",
"CentOS": "", "CentOS": "",
@ -1655,6 +1656,7 @@
"Updated At": "更新于", "Updated At": "更新于",
"Updating": "更新中", "Updating": "更新中",
"Updating Password": "更新密码中", "Updating Password": "更新密码中",
"Upload progress": "上传进度",
"Uploading": "上传中", "Uploading": "上传中",
"Usage Type": "使用类型", "Usage Type": "使用类型",
"Usb Controller": "USB控制器", "Usb Controller": "USB控制器",

View File

@ -61,6 +61,10 @@ class CreateForm extends FormAction {
}; };
} }
get hasRequestCancelCallback() {
return true;
}
static policy = ['add_image', 'upload_image']; static policy = ['add_image', 'upload_image'];
static allowed() { static allowed() {
@ -294,7 +298,8 @@ class CreateForm extends FormAction {
body.owner = owner.selectedRowKeys[0]; body.owner = owner.selectedRowKeys[0];
} }
const mems = visibility === 'shared' ? members.selectedRowKeys : []; const mems = visibility === 'shared' ? members.selectedRowKeys : [];
return this.store.create(body, file, mems); const config = this.getUploadRequestConf();
return this.store.create(body, file, mems, config);
}; };
} }