updates...

This commit is contained in:
chenos 2021-07-14 14:30:38 +08:00
parent 03da153052
commit 61796cbc27
30 changed files with 506 additions and 467 deletions

View File

@ -27,6 +27,7 @@
"beautiful-react-hooks": "^0.35.0",
"constate": "^3.3.0",
"lodash": "^4.17.21",
"monaco-editor": "^0.25.2",
"react-dnd": "^14.0.2",
"react-dnd-html5-backend": "^14.0.0",
"react-dnd-preview": "^6.0.2",

View File

@ -1,51 +0,0 @@
import React from 'react';
import { createFromIconfontCN } from '@ant-design/icons';
import * as antIcons from '@ant-design/icons';
export const IconFont = createFromIconfontCN({
scriptUrl: ['//at.alicdn.com/t/font_2261954_u9jzwc44ug.js'],
});
export const icons = new Map<string, any>();
export function registerIcon(type: string, icon: any = IconFont) {
icons.set(type.toLowerCase(), icon);
}
export function hasIcon(type: string) {
if (!type) {
return false;
}
return icons.has(type.toLowerCase());
}
export function registerIcons(components) {
Object.keys(components).forEach((type) => {
registerIcon(type, components[type]);
});
}
Object.keys(antIcons).forEach((name) => {
if (name.endsWith('Outlined')) {
registerIcon(name, antIcons[name]);
}
});
interface IconProps {
type: string;
[key: string]: any;
}
export function Icon(props: IconProps) {
const { type = '', ...restProps } = props;
if (type && icons.has(type.toLowerCase())) {
const IconComponent = icons.get(type.toLowerCase());
if (IconComponent === IconFont) {
return <IconFont type={type} />;
}
return <IconComponent {...restProps} />;
}
return <IconFont type={type} />;
}
export default Icon;

View File

@ -0,0 +1,50 @@
import { Button } from 'antd';
import React from 'react';
import { SchemaRenderer, useDesignable, useSchema } from '..';
import { uid } from '@formily/shared';
const Hello = () => {
return <div>Hello</div>;
};
const AddNew = () => {
const { schema, refresh, insertBefore } = useDesignable();
return (
<div>
<Button
onClick={() => {
insertBefore({
type: 'void',
'x-component': 'Hello',
});
refresh();
console.log({ schema });
}}
>
Add New
</Button>
</div>
);
};
export default () => {
return (
<SchemaRenderer
debug={true}
components={{ Hello, AddNew }}
schema={{
type: 'object',
properties: {
hello: {
type: 'void',
'x-component': 'Hello',
},
addnew: {
type: 'void',
'x-component': 'AddNew',
},
},
}}
/>
);
};

View File

@ -0,0 +1 @@
<code src="./demos/demo1.tsx"/>

View File

@ -1,46 +1,51 @@
import React, {
createContext,
useCallback,
useContext,
useMemo,
useState,
} from 'react';
import { createForm } from '@formily/core';
import {
Field,
ISchema,
observer,
Schema,
createSchemaField,
FormProvider,
useField,
ISchema,
Schema,
SchemaOptionsContext,
useFieldSchema,
useForm,
SchemaOptionsContext,
} from '@formily/react';
import { observable } from '@formily/reactive';
import { uid, clone } from '@formily/shared';
import { uid } from '@formily/shared';
import constate from 'constate';
import { get } from 'lodash';
import React, { createContext } from 'react';
import { useState } from 'react';
import { useContext } from 'react';
import { useMemo } from 'react';
import { CodeOutlined } from '@ant-design/icons';
import Editor from '@monaco-editor/react';
import { ArrayCollapse, ArrayTable, FormLayout } from '@formily/antd';
import { Space, Card, Modal, Spin } from 'antd';
import { Action, useLogin, useRegister, useSubmit, useDesignableValues } from '../action';
import { AddNew } from '../add-new';
import { Cascader } from '../cascader';
import { Checkbox } from '../checkbox';
import { ColorSelect } from '../color-select';
import { DatabaseField } from '../database-field';
import { DatePicker } from '../date-picker';
import { Filter } from '../filter';
import { Form } from '../form';
import { Grid } from '../grid';
import { IconPicker } from '../icon-picker';
import { Input } from '../input';
import { InputNumber } from '../input-number';
import { Markdown } from '../markdown';
import { Menu } from '../menu';
import { Password } from '../password';
import { Radio } from '../radio';
import { Select } from '../select';
import {
Action,
useLogin,
useRegister,
useSubmit,
useDesignableValues,
} from '../../schemas/action';
import { AddNew } from '../../schemas/add-new';
import { Cascader } from '../../schemas/cascader';
import { Checkbox } from '../../schemas/checkbox';
import { ColorSelect } from '../../schemas/color-select';
import { DatabaseField } from '../../schemas/database-field';
import { DatePicker } from '../../schemas/date-picker';
import { Filter } from '../../schemas/filter';
import { Form } from '../../schemas/form';
import { Grid } from '../../schemas/grid';
import { IconPicker } from '../../schemas/icon-picker';
import { Input } from '../../schemas/input';
import { InputNumber } from '../../schemas/input-number';
import { Markdown } from '../../schemas/markdown';
import { Menu } from '../../schemas/menu';
import { Password } from '../../schemas/password';
import { Radio } from '../../schemas/radio';
import { Select } from '../../schemas/select';
import {
Table,
useTableRow,
@ -48,23 +53,25 @@ import {
useTableUpdateAction,
useTableDestroyAction,
useTableFilterAction,
} from '../table';
import { Tabs } from '../tabs';
import { TimePicker } from '../time-picker';
import { Upload } from '../upload';
import { FormItem } from '../form-item';
import { BlockItem } from '../block-item';
import { DragAndDrop } from '../drag-and-drop';
import { CodeOutlined } from '@ant-design/icons';
import Editor from '@monaco-editor/react';
import { get } from 'lodash';
} from '../../schemas/table';
import { Tabs } from '../../schemas/tabs';
import { TimePicker } from '../../schemas/time-picker';
import { Upload } from '../../schemas/upload';
import { FormItem } from '../../schemas/form-item';
import { BlockItem } from '../../schemas/block-item';
import { DragAndDrop } from '../../schemas/drag-and-drop';
export const BlockContext = createContext({ dragRef: null });
const Div = (props) => <div {...props} />;
export const scope = {
interface DesignableContextProps {
schema?: Schema;
refresh?: any;
}
export const SchemaField = createSchemaField({
scope: {
useLogin,
useRegister,
useSubmit,
@ -74,9 +81,8 @@ export const scope = {
useTableRow,
useTableUpdateAction,
useDesignableValues,
};
export const components = {
},
components: {
Card,
Div,
Space,
@ -112,30 +118,16 @@ export const components = {
Tabs,
TimePicker,
Upload,
};
export function registerScope(scopes) {
Object.keys(scopes).forEach((key) => {
scope[key] = scopes[key];
});
}
export function registerComponents(values) {
Object.keys(values).forEach((key) => {
components[key] = values[key];
});
}
export interface DesignableContextProps {
schema: Schema;
refresh: () => void;
}
export const DesignableContext = createContext<DesignableContextProps>({
schema: null,
refresh: null,
},
});
export const DesignableContext = createContext<DesignableContextProps>(null);
export function useSchema(path?: any) {
const { schema, refresh } = useContext(DesignableContext);
return { schema, refresh };
}
export function pathToArray(path): string[] {
if (Array.isArray(path)) {
return [...path];
@ -188,13 +180,16 @@ export function addPropertyAfter(target: Schema, data: ISchema) {
export function useDesignable(path?: any) {
const { schema, refresh } = useContext(DesignableContext);
const schemaPath = path || useSchemaPath();
const currentSchema = findPropertyByPath(schema, schemaPath) || ({} as Schema);
console.log('useDesignable', { schema, schemaPath, currentSchema });
const fieldSchema = useFieldSchema();
const currentSchema =
findPropertyByPath(schema, schemaPath) || ({} as Schema);
// console.log('useDesignable', { schema, schemaPath, currentSchema });
const options = useContext(SchemaOptionsContext);
const DesignableBar = get(options.components, currentSchema['x-designable-bar']) || (() => null);
const DesignableBar =
get(options.components, currentSchema['x-designable-bar']) || (() => null);
return {
DesignableBar,
schema: currentSchema,
schema: currentSchema || fieldSchema,
refresh,
prepend: (property: ISchema, targetPath?: any): Schema => {
let target = currentSchema;
@ -218,7 +213,7 @@ export function useDesignable(path?: any) {
current.parent.removeProperty(current.name);
properties[current.name] = current.toJSON();
});
console.log({ properties }, target.properties);
// console.log({ properties }, target.properties);
target.setProperties(properties);
refresh();
return target.properties[property.name];
@ -331,7 +326,7 @@ export function getSchemaPath(schema: Schema) {
path.unshift(parent.name);
parent = parent.parent;
}
console.log('getSchemaPath', path, schema);
// console.log('getSchemaPath', path, schema);
return [...path];
}
@ -341,38 +336,6 @@ export function useSchemaPath() {
return [...path];
}
console.log({ scope, components });
export const createDesignableSchemaField = (options) => {
const SchemaField = createSchemaField(options);
const DesignableSchemaField = (props) => {
const schema = useMemo(() => new Schema(props.schema), [props.schema]);
const [, refresh] = useState(0);
if (props.designable === false) {
return <SchemaField schema={schema} />;
}
return (
<DesignableContext.Provider
value={{
schema,
refresh: () => {
refresh(Math.random());
props.onRefresh && props.onRefresh(schema);
},
}}
>
<SchemaField scope={props.scope} components={props.components} schema={schema} />
{props.debug && <CodePreview schema={schema} />}
{props.debug && <FormValues />}
</DesignableContext.Provider>
);
};
return DesignableSchemaField;
};
const FormValues = () => {
const form = useForm();
const [visible, setVisible] = useState(false);
@ -400,7 +363,10 @@ const CodePreview = ({ schema }) => {
const [visible, setVisible] = useState(false);
return (
<>
<CodeOutlined style={{position: 'relative', zIndex: 100}} onClick={() => setVisible(true)} />
<CodeOutlined
style={{ position: 'relative', zIndex: 100 }}
onClick={() => setVisible(true)}
/>
<Modal
width={'50%'}
onOk={() => setVisible(false)}
@ -418,31 +384,25 @@ const CodePreview = ({ schema }) => {
);
};
export const SchemaField = createSchemaField({
scope,
components,
});
export const DesignableSchemaField = createDesignableSchemaField({
scope,
components,
});
export interface SchemaRendererProps {
schema: ISchema;
schema: Schema | ISchema;
form?: any;
designable?: boolean;
onRefresh?: any;
onlyRenderProperties?: boolean;
scope?: any;
render?: any;
components?: any;
scope?: any;
debug?: boolean;
onlyRenderProperties?: boolean;
onRefresh?: any;
[key: string]: any;
}
export const SchemaRenderer = (props: SchemaRendererProps) => {
const form = useMemo(() => props.form || createForm({}), []);
const [, refresh] = useState(uid());
const form = useMemo(() => props.form || createForm(), []);
const schema = useMemo(() => {
if (Schema.isSchemaInstance(props.schema)) {
return schema;
}
let s = props.schema;
if (props.onlyRenderProperties) {
s = {
@ -457,21 +417,32 @@ export const SchemaRenderer = (props: SchemaRendererProps) => {
},
};
}
return s;
return new Schema(s);
}, []);
console.log('SchemaRenderer', schema, props.schema);
return (
const defaultRender = ({ schema }) => (
<FormProvider form={form}>
<DesignableSchemaField
debug={props.debug}
scope={props.scope}
<SchemaField
components={props.components}
onRefresh={props.onRefresh}
designable={props.designable}
scope={props.scope}
schema={schema}
/>
{props.debug && <CodePreview schema={schema} />}
{props.debug && <FormValues />}
</FormProvider>
);
return (
<DesignableContext.Provider
value={{
schema,
refresh: () => {
props.onRefresh && props.onRefresh(schema);
refresh(uid());
},
}}
>
<DesignableContext.Consumer>
{props.render || defaultRender}
</DesignableContext.Consumer>
</DesignableContext.Provider>
);
};

View File

@ -0,0 +1,30 @@
/**
* title: 按钮操作
* desc: 可以通过配置 `useAction`
*/
import React from 'react';
// @ts-ignore
import { SchemaRenderer } from '@nocobase/client';
function useAction() {
return {
run() {
alert('这是自定义的操作逻辑');
},
};
}
const schema = {
type: 'void',
name: 'action1',
title: '按钮',
'x-component': 'Action',
'x-designable-bar': 'Action.DesignableBar',
'x-component-props': {
useAction: '{{ useAction }}',
},
};
export default () => {
return <SchemaRenderer debug={true} scope={{ useAction }} schema={schema} />;
};

View File

@ -0,0 +1,16 @@
import React from 'react';
import { SchemaRenderer } from '@nocobase/client';
const schema = {
type: 'void',
name: 'action1',
title: '按钮',
'x-component': 'Action.Link',
'x-component-props': {
to: 'abc',
},
};
export default () => {
return <SchemaRenderer schema={schema} />;
};

View File

@ -0,0 +1,16 @@
import React from 'react';
import { SchemaRenderer } from '@nocobase/client';
const schema = {
type: 'void',
name: 'action1',
title: '按钮',
'x-component': 'Action.URL',
'x-component-props': {
url: 'https://www.nocobase.com/',
},
};
export default () => {
return <SchemaRenderer schema={schema} />;
};

View File

@ -0,0 +1,92 @@
import React from 'react';
import { SchemaRenderer } from '@nocobase/client';
function useAction() {
return {
run() {
alert('这是自定义的操作逻辑');
},
};
}
const schema = {
type: 'void',
name: 'action1',
title: '下拉菜单',
'x-component': 'Action.Dropdown',
properties: {
action1: {
type: 'void',
name: 'action1',
title: '按钮',
'x-component': 'Action',
'x-component-props': {
useAction,
},
},
action2: {
type: 'void',
title: 'Drawer 按钮',
'x-component': 'Action',
properties: {
drawer1: {
type: 'void',
title: '抽屉标题',
'x-component': 'Action.Drawer',
'x-component-props': {},
properties: {
input: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
action2: {
type: 'void',
title: '打开二级抽屉',
// 'x-decorator': 'FormItem',
'x-component': 'Action',
properties: {
drawer1: {
type: 'void',
title: '二级抽屉标题',
'x-component': 'Action.Drawer',
'x-component-props': {},
properties: {
input: {
type: 'string',
'x-component': 'Input',
},
},
},
},
},
},
},
},
},
action3: {
type: 'void',
name: 'action1',
title: 'Popover 按钮',
'x-component': 'Action',
properties: {
popover1: {
type: 'void',
title: '弹窗标题',
'x-component': 'Action.Popover',
'x-component-props': {},
properties: {
input: {
type: 'string',
'x-component': 'Input',
},
},
},
},
},
},
};
export default () => {
return <SchemaRenderer schema={schema} />;
};

View File

@ -77,180 +77,19 @@ group:
### Action - 常规操作
```tsx
/**
* title: 按钮操作
* desc: 可以通过配置 `useAction` 来处理操作逻辑
*/
import React from 'react';
import { SchemaRenderer, registerScope } from '../';
function useAction() {
return {
run() {
alert('这是自定义的操作逻辑');
}
}
}
const schema = {
type: 'void',
name: 'action1',
title: '按钮',
'x-component': 'Action',
'x-designable-bar': 'Action.DesignableBar',
'x-component-props': {
useAction: '{{ useAction }}',
},
};
export default () => {
return <SchemaRenderer debug={true} scope={{ useAction }} schema={schema} />
}
```
<code src="./demos/demo1.tsx">
### Action.Link - 内链跳转
```tsx
import React from 'react';
import { SchemaRenderer } from '../';
const schema = {
type: 'void',
name: 'action1',
title: '按钮',
'x-component': 'Action.Link',
'x-component-props': {
to: 'abc',
},
};
export default () => {
return <SchemaRenderer schema={schema} />
}
```
<code src="./demos/demo2.tsx">
### Action.URL - 外链跳转
```tsx
import React from 'react';
import { SchemaRenderer } from '../';
const schema = {
type: 'void',
name: 'action1',
title: '按钮',
'x-component': 'Action.URL',
'x-component-props': {
url: 'https://www.nocobase.com/',
},
};
export default () => {
return <SchemaRenderer schema={schema} />
}
```
<code src="./demos/demo3.tsx">
### Action.Dropdown - 下拉操作
```tsx
import React from 'react';
import { SchemaRenderer } from '../';
function useAction() {
return {
run() {
alert('这是自定义的操作逻辑');
}
}
}
const popoverSchema = {
type: 'void',
name: 'action1',
title: '按钮',
'x-component': 'Action',
properties: {
popover1: {
type: 'void',
title: '弹窗标题',
'x-component': 'Action.Popover',
'x-component-props': {},
properties: {
input: {
type: 'string',
'x-component': 'Input',
}
},
},
},
};
const drawerSchema = {
type: 'void',
title: '按钮',
'x-component': 'Action',
properties: {
drawer1: {
type: 'void',
title: '抽屉标题',
'x-component': 'Action.Drawer',
'x-component-props': {},
properties: {
input: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
action2: {
type: 'void',
title: '打开二级抽屉',
// 'x-decorator': 'FormItem',
'x-component': 'Action',
properties: {
drawer1: {
type: 'void',
title: '二级抽屉标题',
'x-component': 'Action.Drawer',
'x-component-props': {},
properties: {
input: {
type: 'string',
'x-component': 'Input',
},
},
},
},
}
},
},
},
};
const schema = {
type: 'void',
name: 'action1',
title: '下拉菜单',
'x-component': 'Action.Dropdown',
properties: {
action1: {
type: 'void',
name: 'action1',
title: '按钮',
'x-component': 'Action',
'x-component-props': {
useAction,
},
},
action2: drawerSchema,
action3: popoverSchema,
},
};
export default () => {
return <SchemaRenderer schema={schema} />
}
```
<code src="./demos/demo4.tsx">
### Action.Popover - 打开气泡
@ -288,9 +127,10 @@ export default () => {
```tsx
import React from 'react';
import { Space } from 'antd';
import { SchemaRenderer } from '../';
const schema = {
const schema1 = {
type: 'void',
name: 'action1',
title: '按钮',
@ -332,8 +172,40 @@ const schema = {
},
};
const schema2 = {
type: 'void',
name: 'action1',
title: 'x-decorator=Form',
'x-component': 'Action',
properties: {
drawer1: {
type: 'void',
title: '弹窗标题',
'x-component': 'Action.Drawer',
'x-component-props': {
// footer: null,
},
'x-decorator': 'Form',
properties: {
input: {
type: 'string',
title: '输入框',
'x-decorator': 'FormItem',
'x-component': 'Input',
required: true,
},
},
},
},
};
export default () => {
return <SchemaRenderer schema={schema} />
return (
<Space>
<SchemaRenderer schema={schema1} />
<SchemaRenderer schema={schema2} />
</Space>
)
}
```
@ -367,7 +239,9 @@ const schema = {
type: 'void',
title: '弹窗标题',
'x-component': 'Action.Modal',
'x-component-props': {},
'x-component-props': {
// footer: null,
},
properties: {
input: {
type: 'string',
@ -418,7 +292,9 @@ const schema2 = {
type: 'void',
title: '弹窗标题',
'x-component': 'Action.Modal',
'x-component-props': {},
'x-component-props': {
// footer: null,
},
'x-decorator': 'Form',
properties: {
input: {

View File

@ -86,24 +86,18 @@ export type ActionType = React.FC<ActionProps> & {
DesignableBar?: React.FC<any>;
};
function Blank() {
return null;
}
function useDesignableBar() {
const schema = useFieldSchema();
const options = useContext(SchemaOptionsContext);
const DesignableBar = get(options.components, schema['x-designable-bar']);
return {
DesignableBar: DesignableBar || Blank,
DesignableBar: DesignableBar || (() => null),
};
}
const [VisibleProvider, useVisibleContext] = constate((props: any = {}) => {
const { initialVisible = false, containerRef = null } = props;
const [visible, setVisible] = useState(initialVisible);
return { containerRef, visible, setVisible };
});
@ -123,6 +117,7 @@ const BaseAction = observer((props: any) => {
const { setVisible } = useVisibleContext();
const fieldSchema = useFieldSchema();
const { schema } = useDesignable();
useEffect(() => {
field.componentProps.setVisible = setVisible;
}, []);
@ -130,7 +125,7 @@ const BaseAction = observer((props: any) => {
const renderButton = () => (
<ButtonComponent
{...others}
icon={<Icon type={icon} />}
icon={icon ? <Icon type={icon} /> : null}
className={classNames(className, `name-${schema.name}`)}
onClick={async (e) => {
e.stopPropagation && e.stopPropagation();
@ -237,13 +232,29 @@ Action.Drawer = observer((props) => {
const { visible, setVisible } = useVisibleContext();
const { run } = useAction();
const form = useForm();
console.log(`schema['x-decorator']`, schema['x-decorator'])
console.log(`schema['x-decorator']`, schema['x-decorator']);
if (schema['x-decorator'] === 'Form.Decorator') {
Object.assign(others, {
footer: (
<Space>
<Button type={'primary'}>Ok</Button>
<Button>Cancel</Button>
<Button
onClick={async () => {
await form.submit();
await run();
setVisible(false);
}}
type={'primary'}
>
OK
</Button>
<Button
onClick={async () => {
form.clearErrors();
setVisible(false);
}}
>
Cancel
</Button>
</Space>
),
});
@ -258,8 +269,10 @@ Action.Drawer = observer((props) => {
width={'50%'}
{...others}
visible={visible}
destroyOnClose
onClose={(e) => {
e.stopPropagation();
form.clearErrors();
setVisible(false);
}}
>
@ -273,8 +286,6 @@ const [DesignableContextProvider, useDesignableContext] = constate(() => {
});
import { DesignableBar } from './designableBar';
import { cloneDeep } from 'lodash';
import { clone } from '@formily/shared';
export function useDesignableValues() {
const { schema } = useDesignableContext();
@ -313,7 +324,11 @@ Action.Modal = observer((props) => {
const { visible, setVisible } = useVisibleContext();
const { run } = useAction();
const form = useForm();
if (schema['x-decorator'] !== 'Form.Decorator') {
// console.log('Action.Modal', schema['x-component-props'], Object.keys(others).includes('footer'));
if (
!Object.keys(others).includes('footer') &&
schema['x-decorator'] !== 'Form.Decorator'
) {
Object.assign(others, { footer: null });
}
return (
@ -328,13 +343,13 @@ Action.Modal = observer((props) => {
onCancel={async (e) => {
e.stopPropagation();
setVisible(false);
// await form.reset();
setDropdownVisible && setDropdownVisible(false);
}}
onOk={async (e) => {
console.log('onOk', form.values);
// await form.submit();
await run();
// e.stopPropagation();
setVisible(false);
setDropdownVisible && setDropdownVisible(false);
}}
@ -348,12 +363,13 @@ Action.Modal = observer((props) => {
);
});
const [DropdownVisibleProvider, useDropdownVisibleContext] = constate((props: any = {}) => {
const [DropdownVisibleProvider, useDropdownVisibleContext] = constate(
(props: any = {}) => {
const { initialVisible = false } = props;
const [visible, setVisible] = useState(initialVisible);
return { visible, setVisible };
});
},
);
const ActionDropdown = observer((props: any) => {
const { icon, ...others } = props;
@ -371,7 +387,7 @@ const ActionDropdown = observer((props: any) => {
content={props.children}
placement={'bottomLeft'}
>
<Button icon={<Icon type={icon} />} {...others}>
<Button icon={icon ? <Icon type={icon} /> : null} {...others}>
{schema.title}
</Button>
</Popover>

View File

@ -46,7 +46,7 @@ import {
DownOutlined,
DatabaseOutlined,
} from '@ant-design/icons';
import { useDesignable, useSchemaPath } from '../DesignableSchemaField';
import { useDesignable, useSchemaPath } from '../';
import { uid } from '@formily/shared';
const generateGridBlock = (schema: ISchema) => {

View File

@ -16,7 +16,7 @@ import './style.less';
import get from 'lodash/get';
import { GridBlockContext } from '../grid';
import { uid } from '@formily/shared';
import { useDesignable, useSchemaPath } from '../DesignableSchemaField';
import { useDesignable, useSchemaPath } from '../';
import { AddNew } from '../add-new';
import { Card } from 'antd';
import {

View File

@ -12,7 +12,7 @@ import { ArrayCollapse } from '@formily/antd';
import { uid } from '@formily/shared';
import '@formily/antd/lib/form-tab/style';
import { Collapse, Button, Dropdown, Menu, Tag } from 'antd';
import { interfaces } from './interfaces';
import { options, interfaces } from './interfaces';
import { DeleteOutlined } from '@ant-design/icons';
export const DatabaseField: any = observer((props) => {
@ -33,24 +33,11 @@ export const DatabaseField: any = observer((props) => {
console.log({ schema });
return (
<Collapse.Panel
// extra={[
// <Button onClick={() => field.moveUp(index)}>Up</Button>,
// <Button
// onClick={() => {
// field.remove(index);
// field.insert(index, {
// ...item,
// interface: 'textarea',
// });
// }}
// >
// Update
// </Button>,
// ]}
header={
<>
{(item.ui && item.ui.title) || <i style={{color: 'rgba(0, 0, 0, 0.25)'}}></i>}{' '}
<Tag>{schema.title}</Tag>
<span style={{ color: 'rgba(0, 0, 0, 0.25)', fontSize: 14 }}>{item.name}</span>
</>
}
extra={[
@ -100,18 +87,24 @@ export const DatabaseField: any = observer((props) => {
return;
}
const data = {
...schema.default,
id: uid(),
name: uid(),
interface: info.key,
...schema.default,
};
field.push(data);
setActiveKey(data.id);
console.log('info.key', info.key, schema);
}}
>
{Array.from(interfaces).map(([key, schema]) => (
<Menu.Item key={key}>{schema.title}</Menu.Item>
{options.map(option => (
<Menu.ItemGroup title={option.label}>
{
option.children.map(item => (
<Menu.Item key={item.name}>{item.title}</Menu.Item>
))
}
</Menu.ItemGroup>
))}
</Menu>
}

View File

@ -1,4 +1,5 @@
import { ISchema } from '@formily/react';
import { set } from 'lodash';
import { select } from './select';
import { string } from './string';
@ -7,7 +8,33 @@ import { textarea } from './textarea';
export const interfaces = new Map<string, ISchema>();
interfaces.set('select', select);
interfaces.set('string', string);
interfaces.set('subTable', subTable);
interfaces.set('textarea', textarea);
const fields = {};
const groupLabels = {};
export function registerField(group: string, type: string, schema) {
fields[group] = fields[group] || {};
set(fields, [group, type], schema);
interfaces.set(type, schema);
}
export function registerGroupLabel(key: string, label: string) {
groupLabels[key] = label;
}
registerField('basic', 'string', string);
registerField('basic', 'textarea', textarea);
registerField('choices', 'select', select);
registerField('relation', 'subTable', subTable);
registerGroupLabel('basic', '基本类型');
registerGroupLabel('choices', '选择类型');
registerGroupLabel('relation', '关系类型');
export const options = Object.keys(fields).map((groupName) => {
return {
label: groupLabels[groupName],
children: Object.keys(fields[groupName]).map((type) => {
return fields[groupName][type];
}),
};
});

View File

@ -1,8 +1,10 @@
import { ISchema } from '@formily/react';
export const select: ISchema = {
name: 'select',
type: 'object',
title: '下拉选择',
group: 'choices',
default: {
dataType: 'string',
// name,

View File

@ -1,8 +1,10 @@
import { ISchema } from '@formily/react';
export const string: ISchema = {
name: 'string',
type: 'object',
title: '单行文本',
group: 'basic',
default: {
dataType: 'string',
// name,

View File

@ -1,8 +1,10 @@
import { ISchema } from '@formily/react';
export const subTable: ISchema = {
name: 'subTable',
type: 'object',
title: '子表格',
group: 'relation',
default: {
// name,
ui: {

View File

@ -1,8 +1,10 @@
import { ISchema } from '@formily/react';
export const textarea: ISchema = {
name: 'textarea',
type: 'object',
title: '多行文本',
group: 'basic',
default: {
dataType: 'text',
// name,

View File

@ -1,9 +1,5 @@
import React, { useEffect } from 'react';
import {
connect,
mapProps,
mapReadPretty,
} from '@formily/react';
import { connect, mapProps, mapReadPretty } from '@formily/react';
import { LoadingOutlined } from '@ant-design/icons';
import { useDynamicList } from 'ahooks';
import { Select } from 'antd';

View File

@ -16,7 +16,7 @@ import { MenuOutlined, DragOutlined } from '@ant-design/icons';
import get from 'lodash/get';
import { GridBlockContext } from '../grid';
import { uid } from '@formily/shared';
import { useDesignable } from '../DesignableSchemaField';
import { useDesignable } from '../';
import { AddNew } from '../add-new';
import { BlockItem } from '../block-item';
import { DraggableBlockContext } from '../../components/drag-and-drop';

View File

@ -9,7 +9,7 @@ import {
FormProvider,
ISchema,
} from '@formily/react';
import { SchemaRenderer, useDesignable } from '../DesignableSchemaField';
import { SchemaRenderer, useDesignable } from '../';
import get from 'lodash/get';
import { Dropdown, Menu } from 'antd';
import {
@ -56,19 +56,14 @@ export const Form: any = observer((props: any) => {
const schema = useFieldSchema();
const { schema: designableSchema, refresh } = useDesignable();
const formSchema: ISchema = schema['x-decorator'] === 'Form' ? {
type: 'void',
"x-component": 'Blank',
type: 'object',
properties: {
[schema.name]: {
...schema.toJSON(),
"x-decorator": 'Form.Decorator',
}
}
} : {
...schema.toJSON(),
'x-component': 'Form.Blank',
'x-component-props': {},
};
} : schema.toJSON();
return (
<SchemaRenderer
scope={scope}

View File

@ -21,7 +21,7 @@ import {
import './style.less';
import cls from 'classnames';
import { useDesignable, useSchemaPath } from '../DesignableSchemaField';
import { useDesignable, useSchemaPath } from '../';
import {
useDrag,
useDrop,

View File

@ -1 +1,2 @@
export * from './DesignableSchemaField';
// export * from './DesignableSchemaField';
export * from '../components/schema-renderer';

View File

@ -425,6 +425,7 @@ export default () => {
<Layout>
<Layout.Header>
<SchemaRenderer
debug={true}
schema={schema}
scope={{ onSelect, sideMenuRef }}
/>

View File

@ -3,7 +3,6 @@ import { connect, mapReadPretty } from '@formily/react';
import { Input } from 'antd';
import { PasswordProps } from 'antd/lib/input';
import { PasswordStrength } from './PasswordStrength';
import { Descriptions } from '../descriptions';
export interface IPasswordProps extends PasswordProps {
checkStrength: boolean;

View File

@ -14,7 +14,7 @@ import { LoadingOutlined } from '@ant-design/icons';
import { SelectProps } from 'antd/lib/select';
import { isArr, isValid, toArr } from '@formily/shared';
import { useState } from 'react';
import { useDesignable } from '../DesignableSchemaField';
import { useDesignable } from '../';
import { createContext } from 'react';
import { useContext } from 'react';
import { SelectedRowKeysContext, useTableContext } from '../table';

View File

@ -33,7 +33,7 @@ import {
SortableElement,
} from 'react-sortable-hoc';
import cls from 'classnames';
import { getSchemaPath, useDesignable, useSchemaPath } from '../DesignableSchemaField';
import { getSchemaPath, useDesignable, useSchemaPath } from '../';
import './style.less';
interface TableRowProps {

View File

@ -29,8 +29,8 @@ group:
import React from 'react';
import { Button } from 'antd'
import { UploadOutlined, InboxOutlined } from '@ant-design/icons'
import { SchemaRenderer } from '../';
import Upload from './';
import { SchemaRenderer, registerComponents } from '../';
const NormalUpload = (props) => {
return (
@ -46,10 +46,6 @@ const NormalUpload = (props) => {
)
}
registerComponents({
NormalUpload,
});
const schema = {
type: 'object',
properties: {
@ -79,7 +75,7 @@ const schema = {
export default () => {
return (
<SchemaRenderer schema={schema} />
<SchemaRenderer components={{ NormalUpload }} schema={schema} />
);
};
```

View File

@ -12661,6 +12661,11 @@ moment-timezone@^0.5.31:
resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
monaco-editor@^0.25.2:
version "0.25.2"
resolved "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.25.2.tgz#119e2b15bbd968a1a99c03cac9c329316d7c37e9"
integrity sha512-5iylzSJevCnzJn9UVsW8yOZ3yHjmAs4TfvH3zsbftKiFKmHG0xirGN6DK9Kk04VSWxYCZZAIafYJoNJJMAU1KA==
moo@^0.5.0:
version "0.5.1"
resolved "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4"