feat(client): FormItem.Designer & TableColumn. Designer

This commit is contained in:
chenos 2022-03-05 22:51:23 +08:00
parent 2c38bf139f
commit c9957dadab
10 changed files with 314 additions and 44 deletions

View File

@ -1,5 +1,6 @@
import { Field } from '@formily/core';
import { connect, useField, useFieldSchema } from '@formily/react';
import { merge } from '@formily/shared';
import React, { useEffect } from 'react';
import { useCompile, useComponent } from '..';
import { CollectionFieldProvider } from './CollectionFieldProvider';
@ -33,7 +34,9 @@ const InternalField: React.FC = (props) => {
setRequired();
// @ts-ignore
field.dataSource = uiSchema.enum;
field.component = [component, uiSchema['x-component-props']];
const originalProps = compile(uiSchema['x-component-props']);
const componentProps = field.componentProps;
field.component = [component, merge(originalProps, componentProps)];
}, [uiSchema?.title, uiSchema?.description, uiSchema?.required]);
if (!uiSchema) {
return null;

View File

@ -30,7 +30,7 @@ export const useCancelFilterAction = () => {
export const useCollectionFilterOptions = (collectionName: string) => {
const { getCollectionFields, getInterface } = useCollectionManager();
const fields = getCollectionFields(collectionName);
const field2option = (field) => {
const field2option = (field, nochildren) => {
if (!field.interface) {
return;
}
@ -45,21 +45,24 @@ export const useCollectionFilterOptions = (collectionName: string) => {
schema: field?.uiSchema,
operators: operators || [],
};
if (nochildren) {
return option;
}
if (children?.length) {
option['children'] = children;
}
if (nested) {
const targetFields = getCollectionFields(field.target);
const options = getOptions(targetFields);
const options = getOptions(targetFields, true);
option['children'] = option['children'] || [];
option['children'].push(...options);
}
return option;
};
const getOptions = (fields) => {
const getOptions = (fields, nochildren = false) => {
const options = [];
fields.forEach((field) => {
const option = field2option(field);
const option = field2option(field, nochildren);
if (option) {
options.push(option);
}

View File

@ -32,4 +32,6 @@ export interface CollectionFieldOptions {
collectionName?: string;
sourceKey?: string; // association field
uiSchema?: ISchema;
target?: string;
[key: string]: any;
}

View File

@ -26,7 +26,7 @@ export const Filter: any = observer((props: any) => {
<FilterContext.Provider value={{ field, fieldSchema, dynamicComponent, options: field.dataSource || [] }}>
<FilterGroup {...props} />
</FilterContext.Provider>
<pre>{JSON.stringify(field.value, null, 2)}</pre>
{/* <pre>{JSON.stringify(field.value, null, 2)}</pre> */}
</div>
);
});

View File

@ -1,5 +1,10 @@
import { FormItem as Item } from '@formily/antd';
import { Field } from '@formily/core';
import { ISchema, useField, useFieldSchema, useForm } from '@formily/react';
import React from 'react';
import { useActionContext } from '..';
import { useCompile, useDesignable } from '../..';
import { useCollection, useCollectionManager } from '../../../collection-manager';
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
import { BlockItem } from '../block-item';
@ -12,8 +17,144 @@ export const FormItem: any = (props) => {
};
FormItem.Designer = () => {
const { getCollectionFields } = useCollectionManager();
const { getField } = useCollection();
const field = useField<Field>();
const fieldSchema = useFieldSchema();
const { dn, reset, refresh } = useDesignable();
const compile = useCompile();
const collectionField = getField(fieldSchema['name']);
const originalTitle = collectionField?.uiSchema?.title;
const targetFields = collectionField.target ? getCollectionFields(collectionField.target) : [];
const initialValue = {
title: field.title === originalTitle ? undefined : field.title,
};
if (!field.readPretty) {
initialValue['required'] = field.required;
}
const options = targetFields
.filter((field) => !field?.target && field.type !== 'boolean')
.map((field) => ({
value: field?.name,
label: compile(field?.uiSchema?.title) || field?.name,
}));
return (
<GeneralSchemaDesigner>
<SchemaSettings.PopupItem
title={'编辑'}
schema={
{
title: '编辑字段',
'x-component': 'Action.Modal',
'x-decorator': 'Form',
'x-decorator-props': {
initialValue,
},
type: 'void',
properties: {
title: {
'x-decorator': 'FormItem',
'x-component': 'Input',
title: '字段标题',
'x-component-props': {},
description: `原字段标题:${collectionField?.uiSchema?.title}`,
},
required: {
'x-decorator': 'FormItem',
'x-component': 'Checkbox',
'x-content': '必填',
'x-hidden': field.readPretty,
},
footer: {
type: 'void',
'x-component': 'Action.Modal.Footer',
properties: {
cancel: {
type: 'void',
title: '{{t("Cancel")}}',
'x-component': 'Action',
'x-component-props': {
useAction() {
const ctx = useActionContext();
return {
async run() {
ctx.setVisible(false);
},
};
},
},
},
submit: {
type: 'void',
title: 'Submit',
'x-component': 'Action',
'x-component-props': {
type: 'primary',
useAction() {
const form = useForm();
const ctx = useActionContext();
return {
async run() {
const { title, required, label } = form.values;
const schema = {
['x-uid']: fieldSchema['x-uid'],
};
if (title) {
field.title = title;
fieldSchema['title'] = title;
schema['title'] = title;
}
field.required = required;
fieldSchema['required'] = required;
schema['required'] = required;
ctx.setVisible(false);
dn.emit('patch', {
schema,
});
// dn.refresh();
reset();
refresh();
},
};
},
},
},
},
},
},
} as ISchema
}
/>
{collectionField?.target && (
<SchemaSettings.SelectItem
title={'标题字段'}
options={options}
value={field?.componentProps?.fieldNames?.label}
onChange={(label) => {
const schema = {
['x-uid']: fieldSchema['x-uid'],
};
const fieldNames = {
...field.componentProps.fieldNames,
label,
};
fieldSchema['x-component-props']['fieldNames'] = fieldNames;
field.componentProps.fieldNames = fieldNames;
schema['x-component-props'] = {
fieldNames,
};
dn.emit('patch', {
schema,
});
dn.refresh();
}}
/>
)}
<SchemaSettings.Divider />
<SchemaSettings.Remove
removeParentsIfNoChildren
breakRemoveOn={{

View File

@ -16,7 +16,6 @@ import { toArr } from '@formily/shared';
import { Button, Drawer, Select, Space } from 'antd';
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useCollectionField } from '../../../collection-manager';
import { RecordProvider } from '../../../record-provider';
import { useAttach } from '../../hooks/useAttach';
import { ActionContext, useActionContext } from '../action';
@ -85,7 +84,7 @@ const InputRecordPicker: React.FC<any> = (props) => {
<Select
size={props.size}
mode={multiple ? 'multiple' : props.mode}
fieldNames={fieldNames}
// fieldNames={fieldNames}
onClick={() => {
setVisible(true);
}}
@ -193,7 +192,6 @@ RecordPicker.SelectedItem = () => {
const fieldSchema = useFieldSchema();
const [visible, setVisible] = useState(false);
const fieldNames = ctx.field.componentProps.fieldNames;
const { target } = useCollectionField();
return (
<ActionContext.Provider value={{ visible, setVisible }}>
<a style={{ cursor: 'pointer' }} onClick={() => setVisible(true)}>

View File

@ -17,13 +17,13 @@ export const useColumnSchema = () => {
return {};
}
const collectionField = getField(fieldSchema.name);
return { columnSchema, fieldSchema, uiSchema: compile(collectionField?.uiSchema) };
return { columnSchema, fieldSchema, collectionField, uiSchema: compile(collectionField?.uiSchema) };
};
export const TableColumnDecorator = (props) => {
const Designer = useDesigner();
const field = useField();
const { fieldSchema, uiSchema } = useColumnSchema();
const { fieldSchema, uiSchema, collectionField } = useColumnSchema();
const { refresh } = useDesignable();
const compile = useCompile();
useLayoutEffect(() => {
@ -37,10 +37,9 @@ export const TableColumnDecorator = (props) => {
field.title = uiSchema?.title;
}
}, [uiSchema?.title]);
console.log('field.title', uiSchema?.title, field.title);
return (
<SortableItem className={designerCss}>
<Designer />
<Designer fieldSchema={fieldSchema} uiSchema={uiSchema} collectionField={collectionField}/>
{/* <RecursionField name={columnSchema.name} schema={columnSchema}/> */}
{field.title || compile(uiSchema?.title)}
{/* <div

View File

@ -1,28 +1,137 @@
import { useField, useFieldSchema } from '@formily/react';
import { uid } from '@formily/shared';
import { ISchema, useField, useFieldSchema, useForm } from '@formily/react';
import React from 'react';
import { useCollectionManager } from '../../../collection-manager';
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
import { useDesignable } from '../../hooks';
import { useCompile, useDesignable } from '../../hooks';
import { useActionContext } from '../action';
export const TableColumnDeigner = () => {
const useLabelFields = (collectionName?: any) => {
if (!collectionName) {
return [];
}
const compile = useCompile();
const { getCollectionFields } = useCollectionManager();
const targetFields = getCollectionFields(collectionName);
return targetFields
?.filter((field) => !field?.target && field.type !== 'boolean')
?.map((field) => {
return {
value: field.name,
label: compile(field?.uiSchema?.title || field.name),
};
});
};
export const TableColumnDeigner = (props) => {
const { uiSchema, fieldSchema, collectionField } = props;
const field = useField();
const fieldSchema = useFieldSchema();
const columnSchema = useFieldSchema();
const { dn } = useDesignable();
const initialValue = {
title: columnSchema?.title,
};
const options = useLabelFields(collectionField.target);
return (
<GeneralSchemaDesigner>
<SchemaSettings.Item
<SchemaSettings.PopupItem
title={'编辑'}
onClick={() => {
field.title = uid();
fieldSchema.title = uid();
dn.emit('patch', {
schema: {
'x-uid': fieldSchema['x-uid'],
title: fieldSchema.title,
schema={
{
title: '编辑字段',
'x-component': 'Action.Modal',
'x-decorator': 'Form',
'x-decorator-props': {
initialValue,
},
});
}}
type: 'void',
properties: {
title: {
'x-decorator': 'FormItem',
'x-component': 'Input',
title: '字段标题',
'x-component-props': {},
description: `原字段标题:${collectionField?.uiSchema?.title}`,
},
footer: {
type: 'void',
'x-component': 'Action.Modal.Footer',
properties: {
cancel: {
type: 'void',
title: '{{t("Cancel")}}',
'x-component': 'Action',
'x-component-props': {
useAction() {
const ctx = useActionContext();
return {
async run() {
ctx.setVisible(false);
},
};
},
},
},
submit: {
type: 'void',
title: 'Submit',
'x-component': 'Action',
'x-component-props': {
type: 'primary',
useAction() {
const form = useForm();
const ctx = useActionContext();
return {
async run() {
const { title } = form.values;
if (title) {
field.title = title;
columnSchema.title = title;
dn.emit('patch', {
schema: {
'x-uid': columnSchema['x-uid'],
title: columnSchema.title,
},
});
}
ctx.setVisible(false);
dn.refresh();
},
};
},
},
},
},
},
},
} as ISchema
}
/>
{collectionField?.target && (
<SchemaSettings.SelectItem
title={'标题字段'}
options={options}
value={fieldSchema?.['x-component-props']?.['fieldNames']?.['label']}
onChange={(label) => {
const fieldNames = {
...fieldSchema['x-component-props']['fieldNames'],
label,
};
fieldSchema['x-component-props']['fieldNames'] = fieldNames;
field.query(`.*.${fieldSchema.name}`).take((f) => {
f.componentProps.fieldNames = fieldNames;
});
dn.emit('patch', {
schema: {
'x-uid': fieldSchema['x-uid'],
'x-component-props': {
fieldNames,
},
},
});
dn.refresh();
}}
/>
)}
<SchemaSettings.Divider />
<SchemaSettings.Remove
removeParentsIfNoChildren

View File

@ -17,6 +17,7 @@ export const AddNewActionInitializer = (props) => {
type: 'void',
title: '{{ t("Add new") }}',
'x-action': 'create',
'x-designer': 'Action.Designer',
'x-component': 'Action',
'x-component-props': {
type: 'primary',

View File

@ -1,7 +1,7 @@
import { GeneralField } from '@formily/core';
import { ISchema, Schema } from '@formily/react';
import { uid } from '@formily/shared';
import { Dropdown, Menu, MenuItemProps, Modal } from 'antd';
import { Dropdown, Menu, MenuItemProps, Modal, Select } from 'antd';
import React, { createContext, useContext, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ActionContext, Designable, SchemaComponent } from '..';
@ -17,6 +17,8 @@ interface SchemaSettingsContextProps {
dn?: Designable;
field?: GeneralField;
fieldSchema?: Schema;
setVisible?: any;
visible?: any;
}
const SchemaSettingsContext = createContext<SchemaSettingsContextProps>(null);
@ -42,23 +44,36 @@ interface SchemaSettingsProviderProps {
dn?: Designable;
field?: GeneralField;
fieldSchema?: Schema;
setVisible?: any;
visible?: any;
}
export const SchemaSettingsProvider: React.FC<SchemaSettingsProviderProps> = (props) => {
const { dn, field, fieldSchema, children } = props;
return <SchemaSettingsContext.Provider value={{ dn, field, fieldSchema }}>{children}</SchemaSettingsContext.Provider>;
const { visible, setVisible, dn, field, fieldSchema, children } = props;
return (
<SchemaSettingsContext.Provider value={{ visible, setVisible, dn, field, fieldSchema }}>
{children}
</SchemaSettingsContext.Provider>
);
};
export const SchemaSettings: React.FC<SchemaSettingsProps> & SchemaSettingsNested = (props) => {
const { title, dn, field, fieldSchema } = props;
const [visible, setVisible] = useState(false);
const DropdownMenu = (
<Dropdown overlay={<Menu>{props.children}</Menu>}>
<Dropdown
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={<Menu>{props.children}</Menu>}
>
{typeof title === 'string' ? <span>{title}</span> : title}
</Dropdown>
);
if (dn) {
return (
<SchemaSettingsProvider dn={dn} field={field} fieldSchema={fieldSchema}>
<SchemaSettingsProvider visible={visible} setVisible={setVisible} dn={dn} field={field} fieldSchema={fieldSchema}>
{DropdownMenu}
</SchemaSettingsProvider>
);
@ -121,21 +136,29 @@ SchemaSettings.Remove = (props: any) => {
};
SchemaSettings.SelectItem = (props) => {
return null;
const { title, options, value, onChange, ...others } = props;
return (
<SchemaSettings.Item {...others}>
{title}
<Select bordered={false} defaultValue={value} onChange={onChange} options={options} style={{ minWidth: 100 }} />
</SchemaSettings.Item>
);
};
SchemaSettings.SwitchItem = (props) => {
return null;
};
SchemaSettings.Popup = (props) => {
SchemaSettings.PopupItem = (props) => {
const { schema, ...others } = props;
const [visible, setVisible] = useState(false);
const ctx = useContext(SchemaSettingsContext);
return (
<ActionContext.Provider value={{ visible, setVisible }}>
<SchemaSettings.Item
{...others}
onClick={() => {
ctx.setVisible(false);
setVisible(true);
}}
>
@ -150,12 +173,3 @@ SchemaSettings.Popup = (props) => {
</ActionContext.Provider>
);
};
SchemaSettings.DrawerItem = (props) => {
const [visible, setVisible] = useState(false);
return (
<ActionContext.Provider value={{ visible, setVisible }}>
<SchemaSettings.Item {...props}>{props.children || props.title}</SchemaSettings.Item>
</ActionContext.Provider>
);
};