Feat: from values changed when unsaved will prompt (#351)

* fix: thumbnail image in kanban card (#338)

* fix: thumbnail image in kanban card

* Update attachment.ts

* Update Kanban.Card.Designer.tsx

Co-authored-by: chenos <chenlinxh@gmail.com>

* feat: data unsaved will be prompt when close drawer

* feat: data unsaved will be prompt when close drawer

Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
SemmyWong 2022-05-03 21:02:11 +08:00 committed by GitHub
parent 4e43e883ad
commit 4aa6de97e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 68 additions and 7 deletions

View File

@ -285,4 +285,7 @@ export default {
// workflows
'Execution History': 'History',
'Parallel branch': 'Branch',
'Confirm': 'Confirm',
'Form value changed tip': 'Are you sure to exit that Data unsaved',
}

View File

@ -477,5 +477,7 @@ export default {
'Please select collection first': '请先选择数据表',
'Only update records matching conditions': '只更新满足条件的数据',
'Fields that are not assigned a value will be set to the default value, and those that do not have a default value are set to null.': '未被赋值的字段将被设置为默认值,没有默认值的设置为空值。',
'Confirm': '确认',
'Form value changed tip': '数据更新未保存,确定要退出吗?',
'Dragging': '拖拽中',
}

View File

@ -1,15 +1,17 @@
import { css } from '@emotion/css';
import { observer, RecursionField, useField, useFieldSchema } from '@formily/react';
import { Drawer } from 'antd';
import { Drawer, Modal as AntdModal } from 'antd';
import classNames from 'classnames';
import React from 'react';
import { createPortal } from 'react-dom';
import { useTranslation } from 'react-i18next';
import { useActionContext } from './hooks';
import { ComposedActionDrawer } from './types';
export const ActionDrawer: ComposedActionDrawer = observer((props) => {
const { footerNodeName = 'Action.Drawer.Footer', ...others } = props;
const { visible, setVisible } = useActionContext();
const { t } = useTranslation();
const { visible, setVisible, formValueChanged, setFormValueChanged } = useActionContext();
const schema = useFieldSchema();
const field = useField();
const footerSchema = schema.reduceProperties((buf, s) => {
@ -18,6 +20,21 @@ export const ActionDrawer: ComposedActionDrawer = observer((props) => {
}
return buf;
});
const closeHandler = () => {
if (!formValueChanged) {
setVisible(false);
return;
}
AntdModal.confirm({
title: t('Confirm'),
content: t('Form value changed tip'),
async onOk() {
setFormValueChanged(false);
setVisible(false);
},
});
};
return (
<>
{createPortal(
@ -32,7 +49,7 @@ export const ActionDrawer: ComposedActionDrawer = observer((props) => {
{...others}
destroyOnClose
visible={visible}
onClose={() => setVisible(false)}
onClose={closeHandler}
className={classNames(
others.className,
css`

View File

@ -80,6 +80,7 @@ export const Action: ComposedAction = observer((props: any) => {
} = props;
const { onClick } = useProps(props);
const [visible, setVisible] = useState(false);
const [formValueChanged, setFormValueChanged] = useState(false);
const Designer = useDesigner();
const field = useField<any>();
const { run } = useAction();
@ -117,7 +118,17 @@ export const Action: ComposedAction = observer((props: any) => {
</SortableItem>
);
return (
<ActionContext.Provider value={{ button: renderButton(), visible, setVisible, openMode, containerRefKey }}>
<ActionContext.Provider
value={{
button: renderButton(),
visible,
setVisible,
formValueChanged,
setFormValueChanged,
openMode,
containerRefKey,
}}
>
{popover && <RecursionField basePath={field.address} onlyRenderProperties schema={fieldSchema} />}
{!popover && renderButton()}
{!popover && props.children}

View File

@ -8,4 +8,6 @@ export interface ActionContextProps {
setVisible?: (v: boolean) => void;
openMode?: 'drawer' | 'modal' | 'page';
containerRefKey?: string;
formValueChanged?: boolean;
setFormValueChanged?: (v: boolean) => void;
}

View File

@ -1,8 +1,10 @@
import { FormLayout } from '@formily/antd';
import { createForm, Field } from '@formily/core';
import { createForm, Field, onFormInputChange } from '@formily/core';
import { FieldContext, FormContext, observer, RecursionField, useField, useFieldSchema } from '@formily/react';
import { uid } from '@nocobase/utils';
import { Spin } from 'antd';
import React, { useMemo } from 'react';
import React, { useEffect, useMemo } from 'react';
import { useActionContext } from '..';
import { useAttach, useComponent } from '../..';
import { useProps } from '../../hooks/useProps';
@ -53,13 +55,37 @@ const FormDecorator: React.FC<FormProps> = (props) => {
};
const WithForm = (props) => {
const { form } = props;
const fieldSchema = useFieldSchema();
const { setFormValueChanged } = useActionContext();
useEffect(() => {
const id = uid();
form.addEffects(id, () => {
onFormInputChange((form) => {
setFormValueChanged(true);
});
});
return () => {
form.removeEffects(id);
};
}, []);
return fieldSchema['x-decorator'] === 'Form' ? <FormDecorator {...props} /> : <FormComponent {...props} />;
};
const WithoutForm = (props) => {
const fieldSchema = useFieldSchema();
const form = useMemo(() => createForm(), []);
const { setFormValueChanged } = useActionContext();
const form = useMemo(
() =>
createForm({
effects() {
onFormInputChange((form) => {
setFormValueChanged(true);
});
},
}),
[],
);
return fieldSchema['x-decorator'] === 'Form' ? (
<FormDecorator form={form} {...props} />
) : (