feat: improve view action schema initializer

This commit is contained in:
chenos 2022-03-03 23:02:55 +08:00
parent b4dc7f6199
commit 590ca267b2
14 changed files with 396 additions and 11 deletions

View File

@ -29,6 +29,7 @@ const useCollectionValues = (options) => {
name: `t_${uid()}`,
createdBy: true,
updatedBy: true,
sortable: true,
},
}),
{

View File

@ -10,6 +10,7 @@ export const checkboxGroup: IField = {
default: {
interface: 'checkboxGroup',
type: 'array',
defaultValue: [],
// name,
uiSchema: {
type: 'string',

View File

@ -9,6 +9,7 @@ export const multipleSelect: IField = {
title: '{{t("Multiple select")}}',
default: {
type: 'array',
defaultValue: [],
// name,
uiSchema: {
type: 'array',

View File

@ -0,0 +1,25 @@
import { useField } from '@formily/react';
import React from 'react';
import { GeneralSchemaDesigner, SchemaSettings } from '../../../';
export const TabsDesigner = () => {
const field = useField();
return (
<GeneralSchemaDesigner>
<SchemaSettings.Popup
schema={{
type: 'void',
'x-component': 'Action.Modal',
'x-decorator': 'Form',
'x-component-props': {
title: '标题',
},
}}
>
</SchemaSettings.Popup>
<SchemaSettings.Divider />
<SchemaSettings.Remove />
</GeneralSchemaDesigner>
);
};

View File

@ -1,12 +1,23 @@
import { css } from '@emotion/css';
import { observer, RecursionField, useField, useFieldSchema } from '@formily/react';
import { useSchemaInitializer } from '@nocobase/client';
import { TabPaneProps, Tabs as AntdTabs, TabsProps } from 'antd';
import classNames from 'classnames';
import React from 'react';
import { DndContext, SortableItem } from '../../common';
import { useDesigner } from '../../hooks/useDesigner';
import { TabsDesigner } from './Tabs.Designer';
export const Tabs: any = observer((props: TabsProps) => {
const fieldSchema = useFieldSchema();
const { render } = useSchemaInitializer(fieldSchema['x-initializer']);
return (
<div>
<AntdTabs>
<DndContext>
<AntdTabs
tabBarExtraContent={{
right: render(),
}}
>
{fieldSchema.mapProperties((schema, key) => {
return (
<AntdTabs.TabPane tab={<RecursionField name={key} schema={schema} onlyRenderSelf />} key={key}>
@ -15,11 +26,67 @@ export const Tabs: any = observer((props: TabsProps) => {
);
})}
</AntdTabs>
</div>
</DndContext>
);
});
const designerCss = css`
position: relative;
&:hover {
> .general-schema-designer {
display: block;
}
}
&.nb-action-link {
> .general-schema-designer {
top: -10px;
bottom: -10px;
left: -10px;
right: -10px;
}
}
> .general-schema-designer {
position: absolute;
z-index: 999;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: none;
background: rgba(241, 139, 98, 0.06);
border: 0;
top: 0;
bottom: 0;
left: 0;
right: 0;
pointer-events: none;
> .general-schema-designer-icons {
position: absolute;
right: 2px;
top: 2px;
line-height: 16px;
pointer-events: all;
.ant-space-item {
background-color: #f18b62;
color: #fff;
line-height: 16px;
width: 16px;
padding-left: 1px;
}
}
}
`;
Tabs.TabPane = observer((props: TabPaneProps) => {
const Designer = useDesigner();
const field = useField();
return <>{props.tab || field.title}</>;
console.log('Designer', Designer);
return (
<SortableItem className={classNames('nb-action-link', designerCss, props.className)}>
{props.tab || field.title}
<Designer />
</SortableItem>
);
});
Tabs.Designer = TabsDesigner;

View File

@ -2,8 +2,9 @@
* title: Tabs
*/
import { ISchema } from '@formily/react';
import { SchemaComponent, SchemaComponentProvider, Tabs } from '@nocobase/client';
import { Action, SchemaComponent, SchemaComponentProvider, Tabs } from '@nocobase/client';
import React from 'react';
import { AntdSchemaComponentProvider } from '../../AntdSchemaComponentProvider';
const schema: ISchema = {
type: 'object',
@ -46,8 +47,10 @@ const schema: ISchema = {
export default () => {
return (
<SchemaComponentProvider components={{ Tabs }}>
<SchemaComponent schema={schema} />
<SchemaComponentProvider designable components={{ Tabs, Action }}>
<AntdSchemaComponentProvider>
<SchemaComponent schema={schema} />
</AntdSchemaComponentProvider>
</SchemaComponentProvider>
);
};

View File

@ -0,0 +1,67 @@
import { FormOutlined } from '@ant-design/icons';
import { ISchema } from '@formily/react';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { SchemaInitializer } from '../..';
import { useCollectionManager } from '../../../collection-manager';
const createSchema = (collectionName) => {
const schema: ISchema = {
type: 'void',
'x-collection': 'collections',
'x-decorator': 'ResourceActionProvider',
'x-decorator-props': {
collection: collectionName,
request: {
resource: collectionName,
action: 'get',
params: {},
},
},
'x-designer': 'Form.Designer',
'x-component': 'CardItem',
properties: {
form: {
type: 'void',
'x-decorator': 'Form',
'x-decorator-props': {},
properties: {
grid: {
type: 'void',
'x-component': 'Grid',
'x-initializer': 'GridFormItemInitializers',
properties: {},
},
actions: {
type: 'void',
'x-initializer': 'FormActionInitializers',
'x-component': 'ActionBar',
'x-component-props': {
layout: 'one-column',
style: {
marginTop: 24,
},
},
properties: {},
},
},
},
},
};
return schema;
};
export const RecordFormBlockInitializer = (props) => {
const { insert } = props;
const { collections } = useCollectionManager();
const { t } = useTranslation();
return (
<SchemaInitializer.Item
{...props}
icon={<FormOutlined />}
onClick={({ item }) => {
insert(createSchema(item.name));
}}
/>
);
};

View File

@ -0,0 +1,67 @@
import { FormOutlined } from '@ant-design/icons';
import { ISchema } from '@formily/react';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { SchemaInitializer } from '../..';
import { useCollectionManager } from '../../../collection-manager';
const createSchema = (collectionName) => {
const schema: ISchema = {
type: 'void',
'x-collection': 'collections',
'x-decorator': 'ResourceActionProvider',
'x-decorator-props': {
collection: collectionName,
request: {
resource: collectionName,
action: 'get',
params: {},
},
},
'x-designer': 'Form.Designer',
'x-component': 'CardItem',
properties: {
form: {
type: 'void',
'x-decorator': 'Form',
'x-decorator-props': {},
properties: {
grid: {
type: 'void',
'x-component': 'Grid',
'x-initializer': 'GridFormItemInitializers',
properties: {},
},
actions: {
type: 'void',
'x-initializer': 'FormActionInitializers',
'x-component': 'ActionBar',
'x-component-props': {
layout: 'one-column',
style: {
marginTop: 24,
},
},
properties: {},
},
},
},
},
};
return schema;
};
export const RecordRelationBlockInitializer = (props) => {
const { insert } = props;
const { collections } = useCollectionManager();
const { t } = useTranslation();
return (
<SchemaInitializer.Item
{...props}
icon={<FormOutlined />}
onClick={({ item }) => {
insert(createSchema(item.name));
}}
/>
);
};

View File

@ -7,6 +7,8 @@ export * from './FormBlockInitializer';
export * from './GeneralInitializer';
export * from './LinkToCollectionFieldInitializer';
export * from './MarkdownBlockInitializer';
export * from './RecordFormBlockInitializer';
export * from './RecordRelationBlockInitializer';
export * from './SubTableFieldInitializer';
export * from './TableBlockInitializer';
export * from './UserFieldInitializer';

View File

@ -4,5 +4,44 @@ import { gridRowColWrap } from './utils';
export const RecordBlockInitializers = {
wrap: gridRowColWrap,
title: "{{t('Add block')}}",
items: [],
items: [
{
type: 'itemGroup',
title: 'Data blocks',
children: [
{
type: 'item',
title: 'Details',
component: 'RecordFormBlockInitializer',
},
{
type: 'item',
title: 'Form',
component: 'RecordFormBlockInitializer',
},
],
},
{
type: 'itemGroup',
title: '关系数据区块',
children: [
{
type: 'item',
title: '关系数据',
component: 'RecordRelationBlockInitializer',
},
],
},
{
type: 'itemGroup',
title: 'Media',
children: [
{
type: 'item',
title: 'Markdown',
component: 'MarkdownBlockInitializer',
},
],
},
],
};

View File

@ -0,0 +1,101 @@
import { useForm } from '@formily/react';
import React from 'react';
import { SchemaComponent, useActionContext, useDesignable } from '../..';
export const TabPaneInitializers = () => {
const { designable, insertBeforeEnd } = useDesignable();
if (!designable) {
return null;
}
const useSubmitAction = () => {
const form = useForm();
const ctx = useActionContext();
return {
async run() {
form.submit();
const { title } = form.values;
insertBeforeEnd({
type: 'void',
title,
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},
properties: {
grid: {
type: 'void',
'x-component': 'Grid',
'x-initializer': 'RecordBlockInitializers',
properties: {},
},
},
});
ctx.setVisible(false);
},
};
};
return (
<SchemaComponent
schema={{
type: 'void',
properties: {
action1: {
type: 'void',
'x-component': 'Action',
'x-component-props': {
style: {
borderColor: 'rgb(241, 139, 98)',
color: 'rgb(241, 139, 98)',
},
type: 'dashed',
},
title: '添加标签页',
properties: {
drawer1: {
'x-decorator': 'Form',
'x-component': 'Action.Modal',
type: 'void',
title: 'Drawer Title',
properties: {
title: {
title: '标题',
required: true,
'x-component': 'Input',
'x-decorator': 'FormItem',
},
footer: {
'x-component': 'Action.Modal.Footer',
type: 'void',
properties: {
cancel: {
title: 'Cancel',
'x-component': 'Action',
'x-component-props': {
useAction: () => {
const ctx = useActionContext();
return {
async run() {
ctx.setVisible(false);
},
};
},
},
},
submit: {
title: 'Submit',
'x-component': 'Action',
'x-component-props': {
type: 'primary',
useAction: useSubmitAction,
},
},
},
},
},
},
},
},
},
}}
/>
);
};

View File

@ -69,19 +69,19 @@ export const TableRecordActionInitializers = (props: any) => {
type: 'void',
'x-component': 'Tabs',
'x-component-props': {},
'x-initializer': 'TabPaneInitializers',
properties: {
tab1: {
type: 'void',
title: '详情',
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},
properties: {
grid: {
type: 'void',
'x-decorator': 'Form',
'x-component': 'Grid',
'x-read-pretty': true,
'x-item-initializer': 'RecordBlockInitializer',
'x-initializer': 'RecordBlockInitializers',
properties: {},
},
},

View File

@ -10,6 +10,8 @@ import { TableActionInitializers } from './TableActionInitializers';
import { TableColumnInitializers } from './TableColumnInitializers';
import { TableFieldRecordActionInitializers } from './TableFieldRecordActionInitializers';
import { TableRecordActionInitializers } from './TableRecordActionInitializers';
import { TabPaneInitializers } from './TabPaneInitializers';
export const items = { ...Items };
export const initializes = {
@ -35,4 +37,6 @@ export const initializes = {
TableRecordActionInitializers,
// 表格字段(子表格)场景的当前行记录的「操作配置」
TableFieldRecordActionInitializers,
// 添加标签页
TabPaneInitializers,
};

View File

@ -76,6 +76,10 @@ export const useTableColumnInitializerFields = () => {
},
} as SchemaInitializerItemOptions;
}
const componentProps = {};
if (field.interface === 'attachment') {
componentProps['size'] = 'small';
}
return {
type: 'item',
title: field?.uiSchema?.title || field.name,
@ -86,6 +90,9 @@ export const useTableColumnInitializerFields = () => {
name: field.name,
'x-collection-field': `${name}.${field.name}`,
'x-component': 'CollectionField',
'x-component-props': {
...componentProps,
},
},
} as SchemaInitializerItemOptions;
});