feat(client): add form disabled context (#1374)

* feat(client): add form disabled context

* fix: improve code

* fix: improve code

* fix: form-v2 disabled

Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
Junyi 2023-01-16 23:01:28 +08:00 committed by GitHub
parent 246ee04154
commit 00efb38438
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 147 additions and 115 deletions

View File

@ -1,7 +1,7 @@
import { CloseCircleOutlined } from '@ant-design/icons'; import { CloseCircleOutlined } from '@ant-design/icons';
import { ObjectField as ObjectFieldModel } from '@formily/core'; import { ObjectField as ObjectFieldModel } from '@formily/core';
import { ArrayField, connect, useField } from '@formily/react'; import { ArrayField, connect, useField } from '@formily/react';
import { Select, Space } from 'antd'; import { ConfigProvider, Select, Space } from 'antd';
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { Trans, useTranslation } from 'react-i18next'; import { Trans, useTranslation } from 'react-i18next';
import { FilterLogicContext, RemoveConditionContext } from './context'; import { FilterLogicContext, RemoveConditionContext } from './context';
@ -20,84 +20,89 @@ export const FilterGroup = connect((props) => {
[value]: [...(obj[logic] || [])], [value]: [...(obj[logic] || [])],
}; };
}; };
const mergedDisabled = field.disabled;
return ( return (
<FilterLogicContext.Provider value={logic}> <ConfigProvider componentDisabled={mergedDisabled}>
<div <FilterLogicContext.Provider value={logic}>
style={ <div
bordered style={
? { bordered
position: 'relative', ? {
border: '1px dashed #dedede', position: 'relative',
padding: 14, border: '1px dashed #dedede',
marginBottom: 8, padding: 14,
} marginBottom: 8,
: { }
position: 'relative', : {
marginBottom: 8, position: 'relative',
} marginBottom: 8,
} }
> }
{remove && ( >
<a> {remove && !mergedDisabled && (
<CloseCircleOutlined <a>
style={{ <CloseCircleOutlined
position: 'absolute', style={{
right: 10, position: 'absolute',
top: 10, right: 10,
color: '#bfbfbf', top: 10,
}} color: '#bfbfbf',
onClick={() => remove()} }}
/> onClick={() => remove()}
</a> />
)} </a>
<div style={{ marginBottom: 8 }}> )}
<Trans> <div style={{ marginBottom: 8 }}>
{'Meet '} <Trans>
<Select {'Meet '}
style={{ width: 'auto' }} <Select
value={logic} style={{ width: 'auto' }}
onChange={(value) => { value={logic}
setLogic(value); onChange={(value) => {
}} setLogic(value);
> }}
<Select.Option value={'$and'}>All</Select.Option> >
<Select.Option value={'$or'}>Any</Select.Option> <Select.Option value={'$and'}>All</Select.Option>
</Select> <Select.Option value={'$or'}>Any</Select.Option>
{' conditions in the group'} </Select>
</Trans> {' conditions in the group'}
</Trans>
</div>
<div>
<ArrayField name={logic} component={[FilterItems]} disabled={mergedDisabled} />
</div>
{!mergedDisabled && (
<Space size={16} style={{ marginTop: 8, marginBottom: 8 }}>
<a
onClick={() => {
const value = field.value || {};
const items = value[logic] || [];
items.push({});
field.value = {
[logic]: items,
};
}}
>
{t('Add condition')}
</a>
<a
onClick={() => {
const value = field.value || {};
const items = value[logic] || [];
items.push({
$and: [{}],
});
field.value = {
[logic]: items,
};
}}
>
{t('Add condition group')}
</a>
</Space>
)}
</div> </div>
<div> </FilterLogicContext.Provider>
<ArrayField name={logic} component={[FilterItems]} /> </ConfigProvider>
</div>
<Space size={16} style={{ marginTop: 8, marginBottom: 8 }}>
<a
onClick={() => {
const value = field.value || {};
const items = value[logic] || [];
items.push({});
field.value = {
[logic]: items,
};
}}
>
{t('Add condition')}
</a>
<a
onClick={() => {
const value = field.value || {};
const items = value[logic] || [];
items.push({
$and: [{}],
});
field.value = {
[logic]: items,
};
}}
>
{t('Add condition group')}
</a>
</Space>
</div>
</FilterLogicContext.Provider>
); );
}); });

View File

@ -50,9 +50,11 @@ export const FilterItem = observer((props: any) => {
setValue(value); setValue(value);
}, },
})} })}
<a> {!props.disabled && (
<CloseCircleOutlined onClick={() => remove()} style={{ color: '#bfbfbf' }} /> <a>
</a> <CloseCircleOutlined onClick={() => remove()} style={{ color: '#bfbfbf' }} />
</a>
)}
</Space> </Space>
</div> </div>
); );

View File

@ -2,7 +2,7 @@ import { FormLayout } from '@formily/antd';
import { createForm, Field, onFormInputChange } 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 '@formily/shared'; import { uid } from '@formily/shared';
import { Spin } from 'antd'; import { ConfigProvider, Spin } from 'antd';
import React, { useEffect, useMemo } from 'react'; import React, { useEffect, useMemo } from 'react';
import { useActionContext } from '..'; import { useActionContext } from '..';
import { useAttach, useComponent } from '../..'; import { useAttach, useComponent } from '../..';
@ -32,7 +32,7 @@ const FormComponent: React.FC<FormProps> = (props) => {
const Def = (props: any) => props.children; const Def = (props: any) => props.children;
const FormDecorator: React.FC<FormProps> = (props) => { const FormDecorator: React.FC<FormProps> = (props) => {
const { form, children, ...others } = props; const { form, children, disabled, ...others } = props;
const field = useField(); const field = useField();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
// TODO: component 里 useField 会与当前 field 存在偏差 // TODO: component 里 useField 会与当前 field 存在偏差
@ -65,6 +65,7 @@ const WithForm = (props) => {
setFormValueChanged?.(true); setFormValueChanged?.(true);
}); });
}); });
form.disabled = props.disabled;
return () => { return () => {
form.removeEffects(id); form.removeEffects(id);
}; };
@ -78,6 +79,7 @@ const WithoutForm = (props) => {
const form = useMemo( const form = useMemo(
() => () =>
createForm({ createForm({
disabled: props.disabled,
effects() { effects() {
onFormInputChange((form) => { onFormInputChange((form) => {
setFormValueChanged?.(true); setFormValueChanged?.(true);
@ -95,12 +97,19 @@ const WithoutForm = (props) => {
export const Form: React.FC<FormProps> & { Designer?: any; ReadPrettyDesigner?: any } = observer((props) => { export const Form: React.FC<FormProps> & { Designer?: any; ReadPrettyDesigner?: any } = observer((props) => {
const field = useField<Field>(); const field = useField<Field>();
const { form, ...others } = useProps(props); const { form, disabled, ...others } = useProps(props);
const formDisabled = disabled || field.disabled;
return ( return (
<form> <ConfigProvider componentDisabled={formDisabled}>
<Spin spinning={field.loading || false}> <form>
{form ? <WithForm form={form} {...others} /> : <WithoutForm {...others} />} <Spin spinning={field.loading || false}>
</Spin> {form ? (
</form> <WithForm form={form} {...others} disabled={formDisabled} />
) : (
<WithoutForm {...others} disabled={formDisabled} />
)}
</Spin>
</form>
</ConfigProvider>
); );
}); });

View File

@ -2,8 +2,8 @@ import { FormLayout } from '@formily/antd';
import { createForm } from '@formily/core'; import { createForm } from '@formily/core';
import { FieldContext, FormContext, observer, RecursionField, useField, useFieldSchema } from '@formily/react'; import { FieldContext, FormContext, observer, RecursionField, useField, useFieldSchema } from '@formily/react';
import { Options, Result } from 'ahooks/lib/useRequest/src/types'; import { Options, Result } from 'ahooks/lib/useRequest/src/types';
import { Spin } from 'antd'; import { ConfigProvider, Spin } from 'antd';
import React, { createContext, useContext, useMemo } from 'react'; import React, { createContext, useContext, useEffect, useMemo } from 'react';
import { useAttach, useComponent } from '../..'; import { useAttach, useComponent } from '../..';
import { useRequest } from '../../../api-client'; import { useRequest } from '../../../api-client';
import { useCollection } from '../../../collection-manager'; import { useCollection } from '../../../collection-manager';
@ -38,25 +38,30 @@ const FormComponent: React.FC<FormProps> = (props) => {
const Def = (props: any) => props.children; const Def = (props: any) => props.children;
const FormDecorator: React.FC<FormProps> = (props) => { const FormDecorator: React.FC<FormProps> = (props) => {
const { form, children, ...others } = props; const { form, children, disabled, ...others } = props;
const field = useField(); const field = useField();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
// TODO: component 里 useField 会与当前 field 存在偏差 // TODO: component 里 useField 会与当前 field 存在偏差
const f = useAttach(form.createVoidField({ ...field.props, basePath: '' })); const f = useAttach(form.createVoidField({ ...field.props, basePath: '' }));
const Component = useComponent(fieldSchema['x-component'], Def); const Component = useComponent(fieldSchema['x-component'], Def);
useEffect(() => {
form.disabled = disabled || field.disabled;
}, [disabled, field.disabled]);
return ( return (
<FieldContext.Provider value={undefined}> <ConfigProvider componentDisabled={disabled}>
<FormContext.Provider value={form}> <FieldContext.Provider value={undefined}>
<FormLayout layout={'vertical'} {...others}> <FormContext.Provider value={form}>
<FieldContext.Provider value={f}> <FormLayout layout={'vertical'} {...others}>
<Component {...field.componentProps}> <FieldContext.Provider value={f}>
<RecursionField basePath={f.address} schema={fieldSchema} onlyRenderProperties /> <Component {...field.componentProps}>
</Component> <RecursionField basePath={f.address} schema={fieldSchema} onlyRenderProperties />
</FieldContext.Provider> </Component>
{/* <FieldContext.Provider value={f}>{children}</FieldContext.Provider> */} </FieldContext.Provider>
</FormLayout> {/* <FieldContext.Provider value={f}>{children}</FieldContext.Provider> */}
</FormContext.Provider> </FormLayout>
</FieldContext.Provider> </FormContext.Provider>
</FieldContext.Provider>
</ConfigProvider>
); );
}; };

View File

@ -5,7 +5,7 @@ import {
} from '@ant-design/icons'; } from '@ant-design/icons';
import { css, cx } from '@emotion/css'; import { css, cx } from '@emotion/css';
import { ISchema, useForm } from '@formily/react'; import { ISchema, useForm } from '@formily/react';
import { Button, message, Modal, Tag } from 'antd'; import { Button, message, Modal, Tag, Alert } from 'antd';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import parse from 'json-templates'; import parse from 'json-templates';
@ -334,6 +334,7 @@ export function NodeDefaultView(props) {
'x-component': 'Action.Drawer', 'x-component': 'Action.Drawer',
'x-decorator': 'Form', 'x-decorator': 'Form',
'x-decorator-props': { 'x-decorator-props': {
disabled: workflow.executed,
useValues(options) { useValues(options) {
const d = useNodeContext(); const d = useNodeContext();
return useRequest(() => { return useRequest(() => {
@ -354,7 +355,6 @@ export function NodeDefaultView(props) {
name: 'config', name: 'config',
'x-component': 'fieldset', 'x-component': 'fieldset',
'x-component-props': { 'x-component-props': {
disabled: workflow.executed,
className: css` className: css`
.ant-select, .ant-select,
.ant-cascader-picker, .ant-cascader-picker,
@ -373,11 +373,16 @@ export function NodeDefaultView(props) {
'x-component': 'Action.Drawer.Footer', 'x-component': 'Action.Drawer.Footer',
properties: workflow.executed properties: workflow.executed
? { ? {
close: { alert: {
title: '{{t("Close")}}', 'x-component': Alert,
'x-component': 'Action',
'x-component-props': { 'x-component-props': {
useAction: '{{ cm.useCancelAction }}', type: 'warning',
showIcon: true,
message: `{{t("Node in executed workflow cannot be modified", { ns: "${NAMESPACE}" })}}`,
className: css`
width: 100%;
font-size: 85%;
`
}, },
} }
} }

View File

@ -1,7 +1,7 @@
import { css, cx } from "@emotion/css"; import { css, cx } from "@emotion/css";
import { ISchema, useForm } from "@formily/react"; import { ISchema, useForm } from "@formily/react";
import { Registry } from "@nocobase/utils/client"; import { Registry } from "@nocobase/utils/client";
import { message, Tag } from "antd"; import { message, Tag, Alert } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { InfoOutlined } from '@ant-design/icons'; import { InfoOutlined } from '@ant-design/icons';
@ -159,6 +159,7 @@ export const TriggerConfig = () => {
'x-component': 'Action.Drawer', 'x-component': 'Action.Drawer',
'x-decorator': 'Form', 'x-decorator': 'Form',
'x-decorator-props': { 'x-decorator-props': {
disabled: workflow.executed,
useValues(options) { useValues(options) {
return useRequest(() => Promise.resolve({ return useRequest(() => Promise.resolve({
data: { config }, data: { config },
@ -185,11 +186,16 @@ export const TriggerConfig = () => {
'x-component': 'Action.Drawer.Footer', 'x-component': 'Action.Drawer.Footer',
properties: executed properties: executed
? { ? {
close: { alert: {
title: '{{t("Close")}}', 'x-component': Alert,
'x-component': 'Action',
'x-component-props': { 'x-component-props': {
useAction: '{{ cm.useCancelAction }}', type: 'warning',
showIcon: true,
message: `{{t("Trigger in executed workflow cannot be modified", { ns: "${NAMESPACE}" })}}`,
className: css`
width: 100%;
font-size: 85%;
`
}, },
} }
} }