// Copyright 2021 99cloud // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import React from 'react'; import { isFunction, has, isObject, isEmpty } from 'lodash'; import Notify from 'components/Notify'; 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 { getPath, getLinkRender } from 'utils/route-map'; import InfoButton from 'components/InfoButton'; import QuotaChart from 'components/QuotaChart'; import styles from './index.less'; export default class BaseForm extends React.Component { constructor(props, options = {}) { super(props); this.options = options; this.state = { // eslint-disable-next-line react/no-unused-state defaultValue: {}, // eslint-disable-next-line react/no-unused-state formData: {}, isSubmitting: false, percent: '', }; this.values = {}; this.response = null; this.responseError = null; this.formRef = React.createRef(); 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(); } componentDidMount() { try { // this.updateDefaultValue(); this.updateState(); } catch (e) { // eslint-disable-next-line no-console console.log(e); } } componentWillUnmount() { this.unsubscribe && this.unsubscribe(); this.disposer && this.disposer(); this.unMountActions && this.unMountActions(); } get path() { const { location: { pathname = '' } = {} } = this.props; return pathname || ''; } get disableSubmit() { return false; } get name() { return ''; } get title() { return ''; } get className() { return ''; } get prefix() { return this.props.match.url; } get routing() { return this.props.rootStore.routing; } get params() { return this.props.match.params || {}; } get location() { return this.props.location || {}; } get locationParams() { return parse(this.location.search.slice(1)); } get listUrl() { return ''; } get currentUser() { const { user } = this.props.rootStore || {}; return user || {}; } get isAdminPage() { const { pathname = '' } = this.props.location || {}; return isAdminPage(pathname); } get hasAdminRole() { return this.props.rootStore.hasAdminRole; } get currentProjectId() { return this.props.rootStore.projectId; } get currentProjectName() { return this.props.rootStore.projectName; } getRouteName(routeName) { return this.isAdminPage ? `${routeName}Admin` : routeName; } getRoutePath(routeName, params = {}, query = {}) { const realName = this.getRouteName(routeName); return getPath({ key: realName, params, query }); } getLinkRender(routeName, value, params = {}, query = {}) { const realName = this.getRouteName(routeName); return getLinkRender({ key: realName, params, query, value }); } get isStep() { return false; } get isModal() { return false; } get labelCol() { return { xs: { span: 5 }, sm: { span: 3 }, }; } get wrapperCol() { return { xs: { span: 10 }, sm: { span: 8 }, }; } get defaultValue() { return null; } get formDefaultValue() { const { context = {} } = this.props; const { defaultValue } = this; return { ...defaultValue, ...context, }; } get okBtnText() { return t('Confirm'); } get instanceName() { const { name } = this.values || {}; return name; } get successText() { if (this.instanceName) { return firstUpperCase( t('{action} successfully, instance: {name}.', { action: this.name.toLowerCase(), name: this.instanceName, }) ); } return firstUpperCase( t('{action} successfully.', { action: this.name.toLowerCase(), }) ); } get errorText() { if (this.instanceName) { return t('Unable to {action}, instance: {name}.', { action: this.name.toLowerCase(), name: this.instanceName, }); } return t('Unable to {action}.', { action: this.name.toLowerCase(), }); } get isSubmitting() { const { isSubmitting = false } = this.state; return isSubmitting; // return (this.store && this.store.isSubmitting) || false; } get formItems() { return []; } get validateMessages() { return []; } get tips() { return ''; } get showNotice() { return true; } get nameForStateUpdate() { const typeList = ['radio', 'more']; return this.formItems .filter((it) => typeList.indexOf(it.type) >= 0) .map((it) => it.name); } get hasRequestCancelCallback() { return false; } get showQuota() { return false; } get quotaInfo() { return null; } getRightExtraSpan() { return { left: 18, right: 6, }; } getSubmitData(data) { return { ...data }; } updateContext = (allFields) => { const { updateContext } = this.props; updateContext && updateContext(allFields); }; unescape = (message) => unescapeHtml(message); getFormInstance = () => this.formRef.current; // eslint-disable-next-line no-unused-vars onSubmit = (values) => Promise.resolve(); updateSubmitting = (value) => { this.setState({ isSubmitting: value || false, }); }; onOk = (values, containerProps, callback) => { // eslint-disable-next-line no-console console.log('onOk', values); this.values = values; if (this.codeError) { return; } this.updateSubmitting(true); if (!this.onSubmit) { return callback(true, false); } const submitData = this.getSubmitData(values); return this.onSubmit(submitData, containerProps).then( (response) => { this.updateSubmitting(false); !this.isModal && this.routing.push(this.listUrl); this.response = response; if (callback && isFunction(callback)) { callback(true, false); } if (response instanceof Array) { const instanceNameArr = this.instanceName ? this.instanceName.split(', ') : null; const failedNames = response .map((it, idx) => { if (it.status === 'rejected') { return { reason: it.reason, name: instanceNameArr ? instanceNameArr[idx] : '', }; } return null; }) .filter((it) => !!it); if (failedNames.length !== 0) { failedNames.forEach((it) => { const { response: { data } = {} } = it.reason; this.showNotice && Notify.errorWithDetail( data, t('Unable to {action}, instance: {name}.', { action: this.name.toLowerCase(), name: it.name, }) ); }); } else { this.showNotice && Notify.success(this.successText); } } else { this.showNotice && Notify.success(this.successText); } }, (err = {}) => { this.updateSubmitting(false); this.responseError = err; const { response: { data } = {} } = err; this.showNotice && Notify.errorWithDetail(data, this.errorText); // eslint-disable-next-line no-console console.log('err', err, data); if (callback && isFunction(callback)) { callback(false, true); } } ); }; onCancel = () => { if (this.isSubmitting && this.cancel) { this.cancel(); Notify.success(t('Cancel upload successfully.')); } }; getChangedFieldsValue = (changedFields, name) => { const value = changedFields[name]; if (isObject(value) && value.value) { return value.value; } if (isObject(value) && value.selectedRows) { return value.selectedRows[0]; } return value; }; // eslint-disable-next-line no-unused-vars onValuesChange = (changedFields, allFields) => {}; // eslint-disable-next-line no-unused-vars onValuesChangeForm = (changedFields, allFields) => { // save linkage data to state const newState = {}; this.currentFormValue = allFields; this.nameForStateUpdate.forEach((name) => { if (has(changedFields, name)) { const value = this.getChangedFieldsValue(changedFields, name); newState[name] = value; } }); if (!isEmpty(newState)) { this.setState({ ...newState, }); } this.onValuesChange(changedFields, allFields); }; checkFormInput = (callback, failCallback) => { this.formRef.current && this.formRef.current.validateFields().then( (values) => { callback && callback(values); this.updateContext(values); }, ({ values, errorFields }) => { if (errorFields && errorFields.length) { failCallback && failCallback(values, errorFields); } else { // eslint-disable-next-line no-console console.log('checkFormInput-catch', values, errorFields); // callback && callback(values); } } ); }; onClickSubmit = (callback, checkCallback, containerProps) => { if (this.codeError) { return; } this.checkFormInput((values) => { checkCallback && checkCallback(values); this.onOk(values, containerProps, callback); }); }; onClickCancel = () => { this.onCancel(); if (this.listUrl) { this.routing.push(this.listUrl); } }; updateDefaultValue = () => { this.resetFormValue(); this.updateContext(this.defaultValue); }; resetFormValue = (fields) => { if (this.formRef.current && this.formRef.current.resetFields) { if (!fields) { this.formRef.current.resetFields(); } else { this.formRef.current.resetFields(fields); } } }; updateFormValue = (key, value) => { this.formRef.current && this.formRef.current.setFieldsValue({ [key]: value, }); }; onUploadProgress = (progressEvent) => { const { loaded, total } = progressEvent; const percent = Math.floor((loaded / total) * 100); this.setState({ percent, }); }; getUploadRequestConf = () => { return { onUploadProgress: this.onUploadProgress, cancelToken: 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; const names = this.nameForStateUpdate; if (names.length === 0) { return; } const newState = {}; if (this.checkContextValue()) { names.forEach((name) => { newState[name] = this.getChangedFieldsValue(context, name); }); } else { names.forEach((name) => { newState[name] = this.getChangedFieldsValue(this.defaultValue, name); }); } this.setState({ ...newState, }); } init() { this.store = {}; } renderTips() { if (this.tips) { return (