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:
parent
4e43e883ad
commit
4aa6de97e0
@ -285,4 +285,7 @@ export default {
|
|||||||
// workflows
|
// workflows
|
||||||
'Execution History': 'History',
|
'Execution History': 'History',
|
||||||
'Parallel branch': 'Branch',
|
'Parallel branch': 'Branch',
|
||||||
|
|
||||||
|
'Confirm': 'Confirm',
|
||||||
|
'Form value changed tip': 'Are you sure to exit that Data unsaved',
|
||||||
}
|
}
|
||||||
|
@ -477,5 +477,7 @@ export default {
|
|||||||
'Please select collection first': '请先选择数据表',
|
'Please select collection first': '请先选择数据表',
|
||||||
'Only update records matching conditions': '只更新满足条件的数据',
|
'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.': '未被赋值的字段将被设置为默认值,没有默认值的设置为空值。',
|
'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': '拖拽中',
|
'Dragging': '拖拽中',
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { observer, RecursionField, useField, useFieldSchema } from '@formily/react';
|
import { observer, RecursionField, useField, useFieldSchema } from '@formily/react';
|
||||||
import { Drawer } from 'antd';
|
import { Drawer, Modal as AntdModal } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useActionContext } from './hooks';
|
import { useActionContext } from './hooks';
|
||||||
import { ComposedActionDrawer } from './types';
|
import { ComposedActionDrawer } from './types';
|
||||||
|
|
||||||
export const ActionDrawer: ComposedActionDrawer = observer((props) => {
|
export const ActionDrawer: ComposedActionDrawer = observer((props) => {
|
||||||
const { footerNodeName = 'Action.Drawer.Footer', ...others } = 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 schema = useFieldSchema();
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const footerSchema = schema.reduceProperties((buf, s) => {
|
const footerSchema = schema.reduceProperties((buf, s) => {
|
||||||
@ -18,6 +20,21 @@ export const ActionDrawer: ComposedActionDrawer = observer((props) => {
|
|||||||
}
|
}
|
||||||
return buf;
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{createPortal(
|
{createPortal(
|
||||||
@ -32,7 +49,7 @@ export const ActionDrawer: ComposedActionDrawer = observer((props) => {
|
|||||||
{...others}
|
{...others}
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
visible={visible}
|
visible={visible}
|
||||||
onClose={() => setVisible(false)}
|
onClose={closeHandler}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
others.className,
|
others.className,
|
||||||
css`
|
css`
|
||||||
|
@ -80,6 +80,7 @@ export const Action: ComposedAction = observer((props: any) => {
|
|||||||
} = props;
|
} = props;
|
||||||
const { onClick } = useProps(props);
|
const { onClick } = useProps(props);
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [formValueChanged, setFormValueChanged] = useState(false);
|
||||||
const Designer = useDesigner();
|
const Designer = useDesigner();
|
||||||
const field = useField<any>();
|
const field = useField<any>();
|
||||||
const { run } = useAction();
|
const { run } = useAction();
|
||||||
@ -117,7 +118,17 @@ export const Action: ComposedAction = observer((props: any) => {
|
|||||||
</SortableItem>
|
</SortableItem>
|
||||||
);
|
);
|
||||||
return (
|
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 && <RecursionField basePath={field.address} onlyRenderProperties schema={fieldSchema} />}
|
||||||
{!popover && renderButton()}
|
{!popover && renderButton()}
|
||||||
{!popover && props.children}
|
{!popover && props.children}
|
||||||
|
@ -8,4 +8,6 @@ export interface ActionContextProps {
|
|||||||
setVisible?: (v: boolean) => void;
|
setVisible?: (v: boolean) => void;
|
||||||
openMode?: 'drawer' | 'modal' | 'page';
|
openMode?: 'drawer' | 'modal' | 'page';
|
||||||
containerRefKey?: string;
|
containerRefKey?: string;
|
||||||
|
formValueChanged?: boolean;
|
||||||
|
setFormValueChanged?: (v: boolean) => void;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { FormLayout } from '@formily/antd';
|
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 { FieldContext, FormContext, observer, RecursionField, useField, useFieldSchema } from '@formily/react';
|
||||||
|
import { uid } from '@nocobase/utils';
|
||||||
import { Spin } from 'antd';
|
import { Spin } from 'antd';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useEffect, useMemo } from 'react';
|
||||||
|
import { useActionContext } from '..';
|
||||||
import { useAttach, useComponent } from '../..';
|
import { useAttach, useComponent } from '../..';
|
||||||
import { useProps } from '../../hooks/useProps';
|
import { useProps } from '../../hooks/useProps';
|
||||||
|
|
||||||
@ -53,13 +55,37 @@ const FormDecorator: React.FC<FormProps> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const WithForm = (props) => {
|
const WithForm = (props) => {
|
||||||
|
const { form } = props;
|
||||||
const fieldSchema = useFieldSchema();
|
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} />;
|
return fieldSchema['x-decorator'] === 'Form' ? <FormDecorator {...props} /> : <FormComponent {...props} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const WithoutForm = (props) => {
|
const WithoutForm = (props) => {
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const form = useMemo(() => createForm(), []);
|
const { setFormValueChanged } = useActionContext();
|
||||||
|
const form = useMemo(
|
||||||
|
() =>
|
||||||
|
createForm({
|
||||||
|
effects() {
|
||||||
|
onFormInputChange((form) => {
|
||||||
|
setFormValueChanged(true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
return fieldSchema['x-decorator'] === 'Form' ? (
|
return fieldSchema['x-decorator'] === 'Form' ? (
|
||||||
<FormDecorator form={form} {...props} />
|
<FormDecorator form={form} {...props} />
|
||||||
) : (
|
) : (
|
||||||
|
Loading…
Reference in New Issue
Block a user