From 1b122869f7eb26ecf768967235b0ed32ed13ba9a Mon Sep 17 00:00:00 2001 From: zhangjingwei Date: Fri, 30 Jul 2021 09:34:00 +0800 Subject: [PATCH] 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 --- src/components/Form/index.jsx | 79 +++++++++++++++---- src/components/Form/index.less | 9 +++ src/components/FormItem/Upload/index.jsx | 36 +++++---- .../Tables/Base/ActionButton/index.jsx | 9 ++- src/components/Tables/Base/index.jsx | 2 +- src/locales/en.json | 2 + src/locales/zh.json | 2 + .../containers/Image/actions/Create.jsx | 7 +- 8 files changed, 111 insertions(+), 35 deletions(-) diff --git a/src/components/Form/index.jsx b/src/components/Form/index.jsx index 8802e542..0d99e23b 100644 --- a/src/components/Form/index.jsx +++ b/src/components/Form/index.jsx @@ -15,13 +15,13 @@ import React from 'react'; import { isFunction, has, isObject, isEmpty } from 'lodash'; 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 { InfoCircleOutlined } from '@ant-design/icons'; import { isAdminPage, firstUpperCase, unescapeHtml } from 'utils/index'; - import { parse } from 'qs'; import FormItem from 'components/FormItem'; +import { CancelToken } from 'axios'; import styles from './index.less'; 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 formData: {}, isSubmitting: false, + percent: '', }; this.values = {}; @@ -45,6 +46,12 @@ export default class BaseForm extends React.Component { this.tipRef = React.createRef(); this.codeError = false; this.currentFormValue = {}; + this.cancel = null; + this.cancelToken = this.hasRequestCancelCallback + ? new CancelToken((c) => { + this.cancel = c; + }) + : null; this.init(); } @@ -231,6 +238,10 @@ export default class BaseForm extends React.Component { .map((it) => it.name); } + get hasRequestCancelCallback() { + return false; + } + updateContext = (allFields) => { const { updateContext } = this.props; 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) => { const value = changedFields[name]; @@ -379,7 +395,10 @@ export default class BaseForm extends React.Component { }; onClickCancel = () => { - this.routing.push(this.listUrl); + this.onCancel(); + if (this.listUrl) { + this.routing.push(this.listUrl); + } }; 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() { // save linkage data to state 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() { this.store = {}; } @@ -544,6 +578,21 @@ export default class BaseForm extends React.Component { ); } + renderSubmittingTip() { + if (!this.hasRequestCancelCallback) { + return; + } + const { percent } = this.state; + return ( +
+ {t('Upload progress')} +
+ +
+
+ ); + } + render() { const wrapperPadding = this.listUrl || this.isStep || (this.isModal && this.tips) @@ -564,7 +613,7 @@ export default class BaseForm extends React.Component {
- + {tips}
{this.renderForms()} diff --git a/src/components/Form/index.less b/src/components/Form/index.less index 4994a9fd..f7293443 100644 --- a/src/components/Form/index.less +++ b/src/components/Form/index.less @@ -109,4 +109,13 @@ display: flex; align-items: center; justify-content: left; +} + +.submit-tip { + font-size: 16px; + // margin-top: 16px; + display: inline-block; +} +.progress-wrapper { + width: 170px; } \ No newline at end of file diff --git a/src/components/FormItem/Upload/index.jsx b/src/components/FormItem/Upload/index.jsx index 1de416d7..ed081b02 100644 --- a/src/components/FormItem/Upload/index.jsx +++ b/src/components/FormItem/Upload/index.jsx @@ -13,7 +13,7 @@ // limitations under the License. import React, { Component } from 'react'; -import { Upload, Button, message } from 'antd'; +import { Upload, Button } from 'antd'; import { UploadOutlined } from '@ant-design/icons'; import { isArray } from 'lodash'; @@ -36,19 +36,7 @@ export default class index extends Component { }; } - onChange = (info) => { - 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) => { + onChange = (file) => { this.setState( { 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; }; @@ -79,7 +85,7 @@ export default class index extends Component { headers: { authorization: 'authorization-text', }, - onChange: this.onChange, + onChange: this.handleChange, progress: this.progress, beforeUpload: this.beforeUpload, fileList, diff --git a/src/components/Tables/Base/ActionButton/index.jsx b/src/components/Tables/Base/ActionButton/index.jsx index 5cccc668..48beb7d2 100644 --- a/src/components/Tables/Base/ActionButton/index.jsx +++ b/src/components/Tables/Base/ActionButton/index.jsx @@ -15,7 +15,7 @@ import React, { Component } from 'react'; import { inject, observer } from 'mobx-react'; import { Modal, Button, Tooltip } from 'antd'; -import { isArray, isFunction } from 'lodash'; +import { isArray, isFunction, isBoolean } from 'lodash'; import Confirm from 'components/Confirm'; import PropTypes from 'prop-types'; import Notify from 'components/Notify'; @@ -381,7 +381,7 @@ class ActionButton extends Component { return this.formRef.current.wrappedInstance.onClickSubmit( (success, fail) => { this.handleSubmitLoading(); - this.onClickModalActionCancel(); + this.onClickModalActionCancel(true); 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; this.setState( { diff --git a/src/components/Tables/Base/index.jsx b/src/components/Tables/Base/index.jsx index 4dcc1a5b..e0fc6f23 100644 --- a/src/components/Tables/Base/index.jsx +++ b/src/components/Tables/Base/index.jsx @@ -161,7 +161,7 @@ export default class BaseTable extends React.Component { } get itemActions() { - const { itemActions } = this.props; + const { itemActions = {} } = this.props; return itemActions; } diff --git a/src/locales/en.json b/src/locales/en.json index 61a07d27..c7fb5c6f 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -214,6 +214,7 @@ "Cancel Download": "Cancel Download", "Cancel Select": "Cancel Select", "Cancel Transfer": "Cancel Transfer", + "Cancel upload successfully.": "Cancel upload successfully.", "Capacity (GB)": "Capacity (GB)", "Category": "Category", "CentOS": "CentOS", @@ -1655,6 +1656,7 @@ "Updated At": "Updated At", "Updating": "Updating", "Updating Password": "Updating Password", + "Upload progress": "Upload progress", "Uploading": "Uploading", "Usage Type": "Usage Type", "Usb Controller": "Usb Controller", diff --git a/src/locales/zh.json b/src/locales/zh.json index f812a89b..4b5ea1db 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -214,6 +214,7 @@ "Cancel Download": "取消下载", "Cancel Select": "取消选择", "Cancel Transfer": "取消云硬盘转让", + "Cancel upload successfully.": "取消上传成功。", "Capacity (GB)": "容量(GB)", "Category": "类别", "CentOS": "", @@ -1655,6 +1656,7 @@ "Updated At": "更新于", "Updating": "更新中", "Updating Password": "更新密码中", + "Upload progress": "上传进度", "Uploading": "上传中", "Usage Type": "使用类型", "Usb Controller": "USB控制器", diff --git a/src/pages/compute/containers/Image/actions/Create.jsx b/src/pages/compute/containers/Image/actions/Create.jsx index 97cce01d..43740fca 100644 --- a/src/pages/compute/containers/Image/actions/Create.jsx +++ b/src/pages/compute/containers/Image/actions/Create.jsx @@ -61,6 +61,10 @@ class CreateForm extends FormAction { }; } + get hasRequestCancelCallback() { + return true; + } + static policy = ['add_image', 'upload_image']; static allowed() { @@ -294,7 +298,8 @@ class CreateForm extends FormAction { body.owner = owner.selectedRowKeys[0]; } 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); }; }