refactor: action/menu/schema-renderer...
This commit is contained in:
		
							parent
							
								
									61796cbc27
								
							
						
					
					
						commit
						c9aa381045
					
				
							
								
								
									
										54
									
								
								packages/client/src/components/icon-picker/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								packages/client/src/components/icon-picker/index.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					import React, { useState } from 'react';
 | 
				
			||||||
 | 
					import { connect, mapProps, mapReadPretty } from '@formily/react';
 | 
				
			||||||
 | 
					import { Input, Popover, Button } from 'antd';
 | 
				
			||||||
 | 
					import { LoadingOutlined, CloseOutlined } from '@ant-design/icons';
 | 
				
			||||||
 | 
					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 IconPicker(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 null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default IconPicker;
 | 
				
			||||||
@ -3,6 +3,30 @@ import { Spin } from 'antd';
 | 
				
			|||||||
import { Helmet } from 'react-helmet';
 | 
					import { Helmet } from 'react-helmet';
 | 
				
			||||||
import { useRequest } from 'ahooks';
 | 
					import { useRequest } from 'ahooks';
 | 
				
			||||||
import { SchemaRenderer } from '../../schemas';
 | 
					import { SchemaRenderer } from '../../schemas';
 | 
				
			||||||
 | 
					import { useForm } from '@formily/react';
 | 
				
			||||||
 | 
					import { useHistory } from 'react-router-dom';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function useLogin() {
 | 
				
			||||||
 | 
					  const form = useForm();
 | 
				
			||||||
 | 
					  const history = useHistory();
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    async run() {
 | 
				
			||||||
 | 
					      await form.submit();
 | 
				
			||||||
 | 
					      history.push('/admin');
 | 
				
			||||||
 | 
					      console.log(form.values);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function useRegister() {
 | 
				
			||||||
 | 
					  const form = useForm();
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    async run() {
 | 
				
			||||||
 | 
					      await form.submit();
 | 
				
			||||||
 | 
					      console.log(form.values);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function RouteSchemaRenderer({ route }) {
 | 
					export function RouteSchemaRenderer({ route }) {
 | 
				
			||||||
  const { data = {}, loading } = useRequest(
 | 
					  const { data = {}, loading } = useRequest(
 | 
				
			||||||
@ -17,7 +41,7 @@ export function RouteSchemaRenderer({ route }) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
      <SchemaRenderer schema={data} />
 | 
					      <SchemaRenderer scope={{ useLogin, useRegister }} schema={data} />
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -22,13 +22,7 @@ import Editor from '@monaco-editor/react';
 | 
				
			|||||||
import { ArrayCollapse, ArrayTable, FormLayout } from '@formily/antd';
 | 
					import { ArrayCollapse, ArrayTable, FormLayout } from '@formily/antd';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Space, Card, Modal, Spin } from 'antd';
 | 
					import { Space, Card, Modal, Spin } from 'antd';
 | 
				
			||||||
import {
 | 
					import { Action } from '../../schemas/action';
 | 
				
			||||||
  Action,
 | 
					 | 
				
			||||||
  useLogin,
 | 
					 | 
				
			||||||
  useRegister,
 | 
					 | 
				
			||||||
  useSubmit,
 | 
					 | 
				
			||||||
  useDesignableValues,
 | 
					 | 
				
			||||||
} from '../../schemas/action';
 | 
					 | 
				
			||||||
import { AddNew } from '../../schemas/add-new';
 | 
					import { AddNew } from '../../schemas/add-new';
 | 
				
			||||||
import { Cascader } from '../../schemas/cascader';
 | 
					import { Cascader } from '../../schemas/cascader';
 | 
				
			||||||
import { Checkbox } from '../../schemas/checkbox';
 | 
					import { Checkbox } from '../../schemas/checkbox';
 | 
				
			||||||
@ -46,14 +40,7 @@ import { Menu } from '../../schemas/menu';
 | 
				
			|||||||
import { Password } from '../../schemas/password';
 | 
					import { Password } from '../../schemas/password';
 | 
				
			||||||
import { Radio } from '../../schemas/radio';
 | 
					import { Radio } from '../../schemas/radio';
 | 
				
			||||||
import { Select } from '../../schemas/select';
 | 
					import { Select } from '../../schemas/select';
 | 
				
			||||||
import {
 | 
					import { Table } from '../../schemas/table';
 | 
				
			||||||
  Table,
 | 
					 | 
				
			||||||
  useTableRow,
 | 
					 | 
				
			||||||
  useTableCreateAction,
 | 
					 | 
				
			||||||
  useTableUpdateAction,
 | 
					 | 
				
			||||||
  useTableDestroyAction,
 | 
					 | 
				
			||||||
  useTableFilterAction,
 | 
					 | 
				
			||||||
} from '../../schemas/table';
 | 
					 | 
				
			||||||
import { Tabs } from '../../schemas/tabs';
 | 
					import { Tabs } from '../../schemas/tabs';
 | 
				
			||||||
import { TimePicker } from '../../schemas/time-picker';
 | 
					import { TimePicker } from '../../schemas/time-picker';
 | 
				
			||||||
import { Upload } from '../../schemas/upload';
 | 
					import { Upload } from '../../schemas/upload';
 | 
				
			||||||
@ -71,17 +58,7 @@ interface DesignableContextProps {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const SchemaField = createSchemaField({
 | 
					export const SchemaField = createSchemaField({
 | 
				
			||||||
  scope: {
 | 
					  scope: {},
 | 
				
			||||||
    useLogin,
 | 
					 | 
				
			||||||
    useRegister,
 | 
					 | 
				
			||||||
    useSubmit,
 | 
					 | 
				
			||||||
    useTableCreateAction,
 | 
					 | 
				
			||||||
    useTableDestroyAction,
 | 
					 | 
				
			||||||
    useTableFilterAction,
 | 
					 | 
				
			||||||
    useTableRow,
 | 
					 | 
				
			||||||
    useTableUpdateAction,
 | 
					 | 
				
			||||||
    useDesignableValues,
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  components: {
 | 
					  components: {
 | 
				
			||||||
    Card,
 | 
					    Card,
 | 
				
			||||||
    Div,
 | 
					    Div,
 | 
				
			||||||
@ -180,9 +157,11 @@ export function addPropertyAfter(target: Schema, data: ISchema) {
 | 
				
			|||||||
export function useDesignable(path?: any) {
 | 
					export function useDesignable(path?: any) {
 | 
				
			||||||
  const { schema, refresh } = useContext(DesignableContext);
 | 
					  const { schema, refresh } = useContext(DesignableContext);
 | 
				
			||||||
  const schemaPath = path || useSchemaPath();
 | 
					  const schemaPath = path || useSchemaPath();
 | 
				
			||||||
 | 
					  console.log({ schemaPath });
 | 
				
			||||||
  const fieldSchema = useFieldSchema();
 | 
					  const fieldSchema = useFieldSchema();
 | 
				
			||||||
  const currentSchema =
 | 
					  const currentSchema =
 | 
				
			||||||
    findPropertyByPath(schema, schemaPath) || ({} as Schema);
 | 
					    findPropertyByPath(schema, schemaPath) || ({} as Schema);
 | 
				
			||||||
 | 
					  console.log({ currentSchema })
 | 
				
			||||||
    // console.log('useDesignable', { schema, schemaPath, currentSchema });
 | 
					    // console.log('useDesignable', { schema, schemaPath, currentSchema });
 | 
				
			||||||
  const options = useContext(SchemaOptionsContext);
 | 
					  const options = useContext(SchemaOptionsContext);
 | 
				
			||||||
  const DesignableBar =
 | 
					  const DesignableBar =
 | 
				
			||||||
@ -317,13 +296,17 @@ export function useDesignable(path?: any) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function getSchemaPath(schema: Schema) {
 | 
					export function getSchemaPath(schema: Schema) {
 | 
				
			||||||
  const path = [schema.name];
 | 
					  const path = schema['x-path'] || [schema.name];
 | 
				
			||||||
  let parent = schema.parent;
 | 
					  let parent = schema.parent;
 | 
				
			||||||
  while (parent) {
 | 
					  while (parent) {
 | 
				
			||||||
    if (!parent.name) {
 | 
					    if (!parent.name) {
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (parent['x-path']) {
 | 
				
			||||||
 | 
					      path.unshift(...parent['x-path']);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
      path.unshift(parent.name);
 | 
					      path.unshift(parent.name);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    parent = parent.parent;
 | 
					    parent = parent.parent;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  // console.log('getSchemaPath', path, schema);
 | 
					  // console.log('getSchemaPath', path, schema);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,30 +0,0 @@
 | 
				
			|||||||
/**
 | 
					 | 
				
			||||||
 * 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} />;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,16 +0,0 @@
 | 
				
			|||||||
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} />;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,16 +0,0 @@
 | 
				
			|||||||
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} />;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,92 +0,0 @@
 | 
				
			|||||||
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} />;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,61 +0,0 @@
 | 
				
			|||||||
import { ISchema } from '@formily/react';
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  useDesignableValues,
 | 
					 | 
				
			||||||
  useDesignableUpdate,
 | 
					 | 
				
			||||||
  useDesignableSchemaRemove,
 | 
					 | 
				
			||||||
} from '.';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const DesignableBar: { [key: string]: ISchema } = {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DesignableBar.Action = {
 | 
					 | 
				
			||||||
  name: 'bar',
 | 
					 | 
				
			||||||
  type: 'void',
 | 
					 | 
				
			||||||
  // title: '操作栏2',
 | 
					 | 
				
			||||||
  'x-component': 'Action.Dropdown',
 | 
					 | 
				
			||||||
  'x-component-props': {
 | 
					 | 
				
			||||||
    icon: 'MenuOutlined',
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  properties: {
 | 
					 | 
				
			||||||
    edit: {
 | 
					 | 
				
			||||||
      type: 'void',
 | 
					 | 
				
			||||||
      title: '编辑按钮',
 | 
					 | 
				
			||||||
      'x-component': 'Action',
 | 
					 | 
				
			||||||
      'x-component-props': {
 | 
					 | 
				
			||||||
        // icon,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      properties: {
 | 
					 | 
				
			||||||
        modal: {
 | 
					 | 
				
			||||||
          type: 'void',
 | 
					 | 
				
			||||||
          title: '修改按钮配置',
 | 
					 | 
				
			||||||
          'x-decorator': 'Form',
 | 
					 | 
				
			||||||
          'x-decorator-props': {
 | 
					 | 
				
			||||||
            useValues: useDesignableValues,
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          'x-component': 'Action.Modal',
 | 
					 | 
				
			||||||
          'x-component-props': {
 | 
					 | 
				
			||||||
            width: '500px',
 | 
					 | 
				
			||||||
            useAction: useDesignableUpdate,
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          properties: {
 | 
					 | 
				
			||||||
            title: {
 | 
					 | 
				
			||||||
              type: 'string',
 | 
					 | 
				
			||||||
              title: '按钮标题',
 | 
					 | 
				
			||||||
              required: true,
 | 
					 | 
				
			||||||
              'x-component': 'Input',
 | 
					 | 
				
			||||||
              'x-decorator': 'FormItem',
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    remove: {
 | 
					 | 
				
			||||||
      type: 'void',
 | 
					 | 
				
			||||||
      title: '删除按钮',
 | 
					 | 
				
			||||||
      'x-component': 'Action',
 | 
					 | 
				
			||||||
      'x-component-props': {
 | 
					 | 
				
			||||||
        useAction: useDesignableSchemaRemove,
 | 
					 | 
				
			||||||
        // icon,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -75,208 +75,75 @@ group:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Examples
 | 
					## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Action - 常规操作
 | 
					### Action
 | 
				
			||||||
 | 
					 | 
				
			||||||
<code src="./demos/demo1.tsx">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Action.Link - 内链跳转
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<code src="./demos/demo2.tsx">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Action.URL - 外链跳转
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<code src="./demos/demo3.tsx">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Action.Dropdown - 下拉操作
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<code src="./demos/demo4.tsx">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Action.Popover - 打开气泡
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
```tsx
 | 
					```tsx
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * title: 按钮操作
 | 
				
			||||||
 | 
					 * desc: 可以通过配置 `useAction` 来处理操作逻辑
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import { SchemaRenderer } from '../';
 | 
					// @ts-ignore
 | 
				
			||||||
 | 
					import { SchemaRenderer } from '@nocobase/client';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function useAction() {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    run() {
 | 
				
			||||||
 | 
					      alert('这是自定义的操作逻辑');
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const schema = {
 | 
					const schema = {
 | 
				
			||||||
  type: 'void',
 | 
					  type: 'void',
 | 
				
			||||||
  name: 'action1',
 | 
					  name: 'action1',
 | 
				
			||||||
  title: '按钮',
 | 
					  title: '按钮',
 | 
				
			||||||
  'x-component': 'Action',
 | 
					  'x-component': 'Action',
 | 
				
			||||||
  properties: {
 | 
					  'x-designable-bar': 'Action.DesignableBar',
 | 
				
			||||||
    popover1: {
 | 
					  'x-component-props': {
 | 
				
			||||||
      type: 'void',
 | 
					    useAction: '{{ useAction }}',
 | 
				
			||||||
      title: '弹窗标题',
 | 
					 | 
				
			||||||
      'x-component': 'Action.Popover',
 | 
					 | 
				
			||||||
      'x-component-props': {},
 | 
					 | 
				
			||||||
      properties: {
 | 
					 | 
				
			||||||
        input: {
 | 
					 | 
				
			||||||
          type: 'string',
 | 
					 | 
				
			||||||
          'x-component': 'Input',
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default () => {
 | 
					export default () => {
 | 
				
			||||||
  return <SchemaRenderer schema={schema} />
 | 
					  return (
 | 
				
			||||||
}
 | 
					    <SchemaRenderer 
 | 
				
			||||||
 | 
					      debug={true} 
 | 
				
			||||||
 | 
					      scope={{ useAction }} 
 | 
				
			||||||
 | 
					      schema={schema} 
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Action.Drawer - 打开抽屉
 | 
					### Action.Modal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```tsx
 | 
					```tsx
 | 
				
			||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { SchemaRenderer } from '@nocobase/client';
 | 
				
			||||||
import { Space } from 'antd';
 | 
					import { Space } from 'antd';
 | 
				
			||||||
import { SchemaRenderer } from '../';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const schema1 = {
 | 
					const schema = {
 | 
				
			||||||
  type: 'void',
 | 
					  type: 'void',
 | 
				
			||||||
  name: 'action1',
 | 
					  name: 'action1',
 | 
				
			||||||
  title: '按钮',
 | 
					  title: '按钮',
 | 
				
			||||||
  'x-component': 'Action',
 | 
					  'x-component': 'Action',
 | 
				
			||||||
  properties: {
 | 
					  'x-designable-bar': 'Action.DesignableBar',
 | 
				
			||||||
    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 schema2 = {
 | 
					 | 
				
			||||||
  type: 'void',
 | 
					 | 
				
			||||||
  name: 'action1',
 | 
					 | 
				
			||||||
  title: 'x-decorator=Form',
 | 
					 | 
				
			||||||
  'x-component': 'Action',
 | 
					 | 
				
			||||||
  properties: {
 | 
					 | 
				
			||||||
    drawer1: {
 | 
					 | 
				
			||||||
      type: 'void',
 | 
					 | 
				
			||||||
      title: '弹窗标题',
 | 
					 | 
				
			||||||
      'x-component': 'Action.Drawer',
 | 
					 | 
				
			||||||
  'x-component-props': {
 | 
					  'x-component-props': {
 | 
				
			||||||
        // footer: null,
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
      'x-decorator': 'Form',
 | 
					  properties: {
 | 
				
			||||||
 | 
					    modal1: {
 | 
				
			||||||
 | 
					      type: 'void',
 | 
				
			||||||
 | 
					      title: '对话框标题',
 | 
				
			||||||
 | 
					      'x-component': 'Action.Modal',
 | 
				
			||||||
      properties: {
 | 
					      properties: {
 | 
				
			||||||
        input: {
 | 
					        input: {
 | 
				
			||||||
          type: 'string',
 | 
					          type: 'string',
 | 
				
			||||||
          title: '输入框',
 | 
					          title: '输入框',
 | 
				
			||||||
          'x-decorator': 'FormItem',
 | 
					 | 
				
			||||||
          'x-component': 'Input',
 | 
					 | 
				
			||||||
          required: true,
 | 
					          required: true,
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default () => {
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <Space>
 | 
					 | 
				
			||||||
      <SchemaRenderer schema={schema1} />
 | 
					 | 
				
			||||||
      <SchemaRenderer schema={schema2} />
 | 
					 | 
				
			||||||
    </Space>
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Action.Modal - 打开对话框
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
`x-decorator='Form'` 时,Model 为 Form,并自带按钮
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```tsx
 | 
					 | 
				
			||||||
import React from 'react';
 | 
					 | 
				
			||||||
import { Space } from 'antd';
 | 
					 | 
				
			||||||
import { SchemaRenderer } from '../';
 | 
					 | 
				
			||||||
import { useVisibleContext } from './';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function useAction() {
 | 
					 | 
				
			||||||
  const { visible, setVisible } = useVisibleContext();
 | 
					 | 
				
			||||||
  return {
 | 
					 | 
				
			||||||
    async run() {
 | 
					 | 
				
			||||||
      console.log({ visible })
 | 
					 | 
				
			||||||
      setVisible(false);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const schema = {
 | 
					 | 
				
			||||||
  type: 'void',
 | 
					 | 
				
			||||||
  name: 'action1',
 | 
					 | 
				
			||||||
  title: '按钮',
 | 
					 | 
				
			||||||
  'x-component': 'Action',
 | 
					 | 
				
			||||||
  properties: {
 | 
					 | 
				
			||||||
    drawer1: {
 | 
					 | 
				
			||||||
      type: 'void',
 | 
					 | 
				
			||||||
      title: '弹窗标题',
 | 
					 | 
				
			||||||
      'x-component': 'Action.Modal',
 | 
					 | 
				
			||||||
      'x-component-props': {
 | 
					 | 
				
			||||||
        // footer: null,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      properties: {
 | 
					 | 
				
			||||||
        input: {
 | 
					 | 
				
			||||||
          type: 'string',
 | 
					 | 
				
			||||||
          'x-decorator': 'FormItem',
 | 
					          'x-decorator': 'FormItem',
 | 
				
			||||||
          'x-component': 'Input',
 | 
					          'x-component': 'Input',
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        close: {
 | 
					 | 
				
			||||||
          type: 'void',
 | 
					 | 
				
			||||||
          name: 'action1',
 | 
					 | 
				
			||||||
          title: '关闭',
 | 
					 | 
				
			||||||
          'x-component': 'Action',
 | 
					 | 
				
			||||||
          'x-component-props': {
 | 
					 | 
				
			||||||
            useAction,
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        action2: {
 | 
					 | 
				
			||||||
          type: 'void',
 | 
					 | 
				
			||||||
          title: '打开二级抽屉',
 | 
					 | 
				
			||||||
          // 'x-decorator': 'FormItem',
 | 
					 | 
				
			||||||
          'x-component': 'Action',
 | 
					 | 
				
			||||||
          properties: {
 | 
					 | 
				
			||||||
            drawer1: {
 | 
					 | 
				
			||||||
              type: 'void',
 | 
					 | 
				
			||||||
              title: '二级抽屉标题',
 | 
					 | 
				
			||||||
              'x-component': 'Action.Modal',
 | 
					 | 
				
			||||||
              'x-component-props': {},
 | 
					 | 
				
			||||||
              properties: {
 | 
					 | 
				
			||||||
                input: {
 | 
					 | 
				
			||||||
                  type: 'string',
 | 
					 | 
				
			||||||
                  'x-component': 'Input',
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
              },
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
@ -285,20 +152,22 @@ const schema = {
 | 
				
			|||||||
const schema2 = {
 | 
					const schema2 = {
 | 
				
			||||||
  type: 'void',
 | 
					  type: 'void',
 | 
				
			||||||
  name: 'action1',
 | 
					  name: 'action1',
 | 
				
			||||||
  title: 'x-decorator=Form',
 | 
					  title: 'ModalForm',
 | 
				
			||||||
  'x-component': 'Action',
 | 
					  'x-component': 'Action',
 | 
				
			||||||
  properties: {
 | 
					  'x-designable-bar': 'Action.DesignableBar',
 | 
				
			||||||
    drawer1: {
 | 
					 | 
				
			||||||
      type: 'void',
 | 
					 | 
				
			||||||
      title: '弹窗标题',
 | 
					 | 
				
			||||||
      'x-component': 'Action.Modal',
 | 
					 | 
				
			||||||
  'x-component-props': {
 | 
					  'x-component-props': {
 | 
				
			||||||
        // footer: null,
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  properties: {
 | 
				
			||||||
 | 
					    modal1: {
 | 
				
			||||||
 | 
					      type: 'void',
 | 
				
			||||||
 | 
					      title: '对话框标题',
 | 
				
			||||||
      'x-decorator': 'Form',
 | 
					      'x-decorator': 'Form',
 | 
				
			||||||
 | 
					      'x-component': 'Action.Modal',
 | 
				
			||||||
      properties: {
 | 
					      properties: {
 | 
				
			||||||
        input: {
 | 
					        input: {
 | 
				
			||||||
          type: 'string',
 | 
					          type: 'string',
 | 
				
			||||||
 | 
					          title: '输入框',
 | 
				
			||||||
 | 
					          required: true,
 | 
				
			||||||
          'x-decorator': 'FormItem',
 | 
					          'x-decorator': 'FormItem',
 | 
				
			||||||
          'x-component': 'Input',
 | 
					          'x-component': 'Input',
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -310,51 +179,226 @@ const schema2 = {
 | 
				
			|||||||
export default () => {
 | 
					export default () => {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Space>
 | 
					    <Space>
 | 
				
			||||||
      <SchemaRenderer schema={schema} />
 | 
					      <SchemaRenderer 
 | 
				
			||||||
      <SchemaRenderer schema={schema2} />
 | 
					        schema={schema} 
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <SchemaRenderer 
 | 
				
			||||||
 | 
					        schema={schema2} 
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
    </Space>
 | 
					    </Space>
 | 
				
			||||||
  )
 | 
					  );
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Action.Container - 指定容器内打开
 | 
					### Action.Drawer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```tsx
 | 
					```tsx
 | 
				
			||||||
import React, { useRef } from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import { SchemaRenderer } from '../';
 | 
					import { SchemaRenderer } from '@nocobase/client';
 | 
				
			||||||
import { ActionContext } from './';
 | 
					import { Space } from 'antd';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default () => {
 | 
					const schema = {
 | 
				
			||||||
  const containerRef = useRef();
 | 
					 | 
				
			||||||
  const schema = {
 | 
					 | 
				
			||||||
  type: 'void',
 | 
					  type: 'void',
 | 
				
			||||||
  name: 'action1',
 | 
					  name: 'action1',
 | 
				
			||||||
  title: '按钮',
 | 
					  title: '按钮',
 | 
				
			||||||
  'x-component': 'Action',
 | 
					  'x-component': 'Action',
 | 
				
			||||||
 | 
					  'x-designable-bar': 'Action.DesignableBar',
 | 
				
			||||||
  'x-component-props': {
 | 
					  'x-component-props': {
 | 
				
			||||||
      containerRef
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  properties: {
 | 
					  properties: {
 | 
				
			||||||
      container1: {
 | 
					    modal1: {
 | 
				
			||||||
      type: 'void',
 | 
					      type: 'void',
 | 
				
			||||||
        title: '页面标题',
 | 
					      title: '抽屉标题',
 | 
				
			||||||
        'x-component': 'Action.Container',
 | 
					      'x-component': 'Action.Drawer',
 | 
				
			||||||
      properties: {
 | 
					      properties: {
 | 
				
			||||||
        input: {
 | 
					        input: {
 | 
				
			||||||
          type: 'string',
 | 
					          type: 'string',
 | 
				
			||||||
            title: '字段',
 | 
					          title: '输入框',
 | 
				
			||||||
            'x-designable-bar': 'FormItem.DesignableBar',
 | 
					          required: true,
 | 
				
			||||||
          'x-decorator': 'FormItem',
 | 
					          'x-decorator': 'FormItem',
 | 
				
			||||||
          'x-component': 'Input',
 | 
					          'x-component': 'Input',
 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  };
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const schema2 = {
 | 
				
			||||||
 | 
					  type: 'void',
 | 
				
			||||||
 | 
					  name: 'action1',
 | 
				
			||||||
 | 
					  title: 'DrawerForm',
 | 
				
			||||||
 | 
					  'x-component': 'Action',
 | 
				
			||||||
 | 
					  'x-designable-bar': 'Action.DesignableBar',
 | 
				
			||||||
 | 
					  'x-component-props': {
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  properties: {
 | 
				
			||||||
 | 
					    modal1: {
 | 
				
			||||||
 | 
					      type: 'void',
 | 
				
			||||||
 | 
					      title: '抽屉标题',
 | 
				
			||||||
 | 
					      'x-decorator': 'Form',
 | 
				
			||||||
 | 
					      'x-component': 'Action.Drawer',
 | 
				
			||||||
 | 
					      properties: {
 | 
				
			||||||
 | 
					        input: {
 | 
				
			||||||
 | 
					          type: 'string',
 | 
				
			||||||
 | 
					          title: '输入框',
 | 
				
			||||||
 | 
					          required: true,
 | 
				
			||||||
 | 
					          'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					          'x-component': 'Input',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default () => {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div>
 | 
					    <Space>
 | 
				
			||||||
      <SchemaRenderer schema={schema} />
 | 
					      <SchemaRenderer 
 | 
				
			||||||
    </div>
 | 
					        schema={schema} 
 | 
				
			||||||
  )
 | 
					      />
 | 
				
			||||||
}
 | 
					      <SchemaRenderer 
 | 
				
			||||||
 | 
					        schema={schema2} 
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </Space>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Action.Dropdown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```tsx
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { SchemaRenderer } from '@nocobase/client';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const schema = {
 | 
				
			||||||
 | 
					  type: 'void',
 | 
				
			||||||
 | 
					  name: 'dropdown1',
 | 
				
			||||||
 | 
					  title: '下拉菜单',
 | 
				
			||||||
 | 
					  'x-component': 'Action.Dropdown',
 | 
				
			||||||
 | 
					  'x-designable-bar': 'Action.DesignableBar',
 | 
				
			||||||
 | 
					  'x-component-props': {},
 | 
				
			||||||
 | 
					  properties: {
 | 
				
			||||||
 | 
					    item1: {
 | 
				
			||||||
 | 
					      type: 'void',
 | 
				
			||||||
 | 
					      title: `操作1`,
 | 
				
			||||||
 | 
					      'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					      'x-component': 'Menu.Action',
 | 
				
			||||||
 | 
					      properties: {
 | 
				
			||||||
 | 
					        modal1: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          title: '对话框标题',
 | 
				
			||||||
 | 
					          'x-component': 'Action.Modal',
 | 
				
			||||||
 | 
					          properties: {
 | 
				
			||||||
 | 
					            input: {
 | 
				
			||||||
 | 
					              type: 'string',
 | 
				
			||||||
 | 
					              'x-component': 'Input',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    item2: {
 | 
				
			||||||
 | 
					      type: 'void',
 | 
				
			||||||
 | 
					      title: `操作2`,
 | 
				
			||||||
 | 
					      'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					      'x-component': 'Menu.Action',
 | 
				
			||||||
 | 
					      properties: {
 | 
				
			||||||
 | 
					        modal2: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          title: '对话框标题',
 | 
				
			||||||
 | 
					          'x-component': 'Action.Modal',
 | 
				
			||||||
 | 
					          properties: {
 | 
				
			||||||
 | 
					            input: {
 | 
				
			||||||
 | 
					              type: 'string',
 | 
				
			||||||
 | 
					              'x-component': 'Input',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default () => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <SchemaRenderer 
 | 
				
			||||||
 | 
					      debug={true} 
 | 
				
			||||||
 | 
					      schema={schema} 
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Action.Popover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```tsx
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { SchemaRenderer } from '@nocobase/client';
 | 
				
			||||||
 | 
					import { Space } from 'antd';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const schema = {
 | 
				
			||||||
 | 
					  type: 'void',
 | 
				
			||||||
 | 
					  name: 'action1',
 | 
				
			||||||
 | 
					  title: '按钮',
 | 
				
			||||||
 | 
					  'x-component': 'Action',
 | 
				
			||||||
 | 
					  'x-designable-bar': 'Action.DesignableBar',
 | 
				
			||||||
 | 
					  'x-component-props': {
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  properties: {
 | 
				
			||||||
 | 
					    modal1: {
 | 
				
			||||||
 | 
					      type: 'void',
 | 
				
			||||||
 | 
					      title: '抽屉标题',
 | 
				
			||||||
 | 
					      'x-component': 'Action.Popover',
 | 
				
			||||||
 | 
					      properties: {
 | 
				
			||||||
 | 
					        input: {
 | 
				
			||||||
 | 
					          type: 'string',
 | 
				
			||||||
 | 
					          title: '输入框',
 | 
				
			||||||
 | 
					          required: true,
 | 
				
			||||||
 | 
					          'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					          'x-component': 'Input',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const schema2 = {
 | 
				
			||||||
 | 
					  type: 'void',
 | 
				
			||||||
 | 
					  name: 'action1',
 | 
				
			||||||
 | 
					  title: 'PopoverForm',
 | 
				
			||||||
 | 
					  'x-component': 'Action',
 | 
				
			||||||
 | 
					  'x-designable-bar': 'Action.DesignableBar',
 | 
				
			||||||
 | 
					  'x-component-props': {
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  properties: {
 | 
				
			||||||
 | 
					    modal1: {
 | 
				
			||||||
 | 
					      type: 'void',
 | 
				
			||||||
 | 
					      title: '抽屉标题',
 | 
				
			||||||
 | 
					      'x-decorator': 'Form',
 | 
				
			||||||
 | 
					      'x-component': 'Action.Popover',
 | 
				
			||||||
 | 
					      properties: {
 | 
				
			||||||
 | 
					        input: {
 | 
				
			||||||
 | 
					          type: 'string',
 | 
				
			||||||
 | 
					          title: '输入框',
 | 
				
			||||||
 | 
					          required: true,
 | 
				
			||||||
 | 
					          'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					          'x-component': 'Input',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default () => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Space>
 | 
				
			||||||
 | 
					      <SchemaRenderer 
 | 
				
			||||||
 | 
					        schema={schema} 
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <SchemaRenderer 
 | 
				
			||||||
 | 
					        schema={schema2} 
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </Space>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
				
			|||||||
@ -13,266 +13,132 @@ import {
 | 
				
			|||||||
} from '@formily/react';
 | 
					} from '@formily/react';
 | 
				
			||||||
import { Button, Dropdown, Menu, Popover, Space, Drawer, Modal } from 'antd';
 | 
					import { Button, Dropdown, Menu, Popover, Space, Drawer, Modal } from 'antd';
 | 
				
			||||||
import { Link, useHistory, LinkProps } from 'react-router-dom';
 | 
					import { Link, useHistory, LinkProps } from 'react-router-dom';
 | 
				
			||||||
import { SchemaRenderer, useDesignable } from '..';
 | 
					import { useDesignable, VisibleContext, useDefaultAction } from '..';
 | 
				
			||||||
import ReactDOM from 'react-dom';
 | 
					 | 
				
			||||||
import get from 'lodash/get';
 | 
					 | 
				
			||||||
import { MenuOutlined, DownOutlined } from '@ant-design/icons';
 | 
					 | 
				
			||||||
import classNames from 'classnames';
 | 
					 | 
				
			||||||
import { useMount } from 'ahooks';
 | 
					 | 
				
			||||||
import { uid } from '@formily/shared';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import './style.less';
 | 
					import './style.less';
 | 
				
			||||||
import constate from 'constate';
 | 
					import { uid } from '@formily/shared';
 | 
				
			||||||
import { Icon } from '../icon-picker';
 | 
					import cls from 'classnames';
 | 
				
			||||||
import { useMemo } from 'react';
 | 
					import { MenuOutlined } from '@ant-design/icons';
 | 
				
			||||||
import { createForm } from '@formily/core';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function useDefaultAction() {
 | 
					export const Action: any = observer((props: any) => {
 | 
				
			||||||
  return {
 | 
					  const { useAction = useDefaultAction, ...others } = props;
 | 
				
			||||||
    run() {},
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function useSubmit() {
 | 
					 | 
				
			||||||
  const form = useForm();
 | 
					 | 
				
			||||||
  const run = async () => {
 | 
					 | 
				
			||||||
    await form.submit();
 | 
					 | 
				
			||||||
    console.log(form.values);
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  return {
 | 
					 | 
				
			||||||
    run,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function useLogin() {
 | 
					 | 
				
			||||||
  const form = useForm();
 | 
					 | 
				
			||||||
  const history = useHistory();
 | 
					 | 
				
			||||||
  const run = async () => {
 | 
					 | 
				
			||||||
    await form.submit();
 | 
					 | 
				
			||||||
    history.push('/admin');
 | 
					 | 
				
			||||||
    console.log(form.values);
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  return {
 | 
					 | 
				
			||||||
    run,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function useRegister() {
 | 
					 | 
				
			||||||
  const form = useForm();
 | 
					 | 
				
			||||||
  const run = async () => {
 | 
					 | 
				
			||||||
    await form.submit();
 | 
					 | 
				
			||||||
    console.log(form.values);
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  return {
 | 
					 | 
				
			||||||
    run,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface ActionProps {
 | 
					 | 
				
			||||||
  useAction?: any;
 | 
					 | 
				
			||||||
  [key: string]: any;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export type ActionType = React.FC<ActionProps> & {
 | 
					 | 
				
			||||||
  Link?: React.FC<LinkProps>;
 | 
					 | 
				
			||||||
  URL?: React.FC<any>;
 | 
					 | 
				
			||||||
  Page?: React.FC<any>;
 | 
					 | 
				
			||||||
  Container?: React.FC<any>;
 | 
					 | 
				
			||||||
  Popover?: React.FC<any>;
 | 
					 | 
				
			||||||
  Drawer?: React.FC<any>;
 | 
					 | 
				
			||||||
  Modal?: React.FC<any>;
 | 
					 | 
				
			||||||
  Dropdown?: React.FC<any>;
 | 
					 | 
				
			||||||
  Group?: React.FC<any>;
 | 
					 | 
				
			||||||
  DesignableBar?: React.FC<any>;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function useDesignableBar() {
 | 
					 | 
				
			||||||
  const schema = useFieldSchema();
 | 
					 | 
				
			||||||
  const options = useContext(SchemaOptionsContext);
 | 
					 | 
				
			||||||
  const DesignableBar = get(options.components, schema['x-designable-bar']);
 | 
					 | 
				
			||||||
  return {
 | 
					 | 
				
			||||||
    DesignableBar: DesignableBar || (() => null),
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const [VisibleProvider, useVisibleContext] = constate((props: any = {}) => {
 | 
					 | 
				
			||||||
  const { initialVisible = false, containerRef = null } = props;
 | 
					 | 
				
			||||||
  const [visible, setVisible] = useState(initialVisible);
 | 
					 | 
				
			||||||
  return { containerRef, visible, setVisible };
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export { VisibleProvider, useVisibleContext };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const BaseAction = observer((props: any) => {
 | 
					 | 
				
			||||||
  const {
 | 
					 | 
				
			||||||
    ButtonComponent = Button,
 | 
					 | 
				
			||||||
    className,
 | 
					 | 
				
			||||||
    icon,
 | 
					 | 
				
			||||||
    useAction = useDefaultAction,
 | 
					 | 
				
			||||||
    ...others
 | 
					 | 
				
			||||||
  } = props;
 | 
					 | 
				
			||||||
  const field = useField();
 | 
					 | 
				
			||||||
  const { run } = useAction();
 | 
					  const { run } = useAction();
 | 
				
			||||||
  const { DesignableBar } = useDesignableBar();
 | 
					  const { schema, DesignableBar } = useDesignable();
 | 
				
			||||||
  const { setVisible } = useVisibleContext();
 | 
					  const [visible, setVisible] = useState(false);
 | 
				
			||||||
  const fieldSchema = useFieldSchema();
 | 
					  const child = Object.values(schema.properties || {}).shift();
 | 
				
			||||||
  const { schema } = useDesignable();
 | 
					  const isDropdownOrPopover =
 | 
				
			||||||
 | 
					    child &&
 | 
				
			||||||
  useEffect(() => {
 | 
					    ['Action.Dropdown', 'Action.Popover'].includes(child['x-component']);
 | 
				
			||||||
    field.componentProps.setVisible = setVisible;
 | 
					  console.log({ DesignableBar, schema })
 | 
				
			||||||
  }, []);
 | 
					    return (
 | 
				
			||||||
 | 
					    <VisibleContext.Provider value={[visible, setVisible]}>
 | 
				
			||||||
  const renderButton = () => (
 | 
					      {!isDropdownOrPopover && (
 | 
				
			||||||
    <ButtonComponent
 | 
					        <Button
 | 
				
			||||||
          {...others}
 | 
					          {...others}
 | 
				
			||||||
      icon={icon ? <Icon type={icon} /> : null}
 | 
					          onClick={async () => {
 | 
				
			||||||
      className={classNames(className, `name-${schema.name}`)}
 | 
					            setVisible(true);
 | 
				
			||||||
      onClick={async (e) => {
 | 
					 | 
				
			||||||
        e.stopPropagation && e.stopPropagation();
 | 
					 | 
				
			||||||
        setVisible && setVisible(true);
 | 
					 | 
				
			||||||
            await run();
 | 
					            await run();
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          {schema.title}
 | 
					          {schema.title}
 | 
				
			||||||
          <DesignableBar />
 | 
					          <DesignableBar />
 | 
				
			||||||
    </ButtonComponent>
 | 
					        </Button>
 | 
				
			||||||
  );
 | 
					      )}
 | 
				
			||||||
 | 
					      <RecursionField schema={schema} onlyRenderProperties />
 | 
				
			||||||
  const popover = schema.reduceProperties((items, current) => {
 | 
					    </VisibleContext.Provider>
 | 
				
			||||||
    if (current['x-component'] === 'Action.Popover') {
 | 
					 | 
				
			||||||
      return current;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return null;
 | 
					 | 
				
			||||||
  }, null);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (popover) {
 | 
					 | 
				
			||||||
    return (
 | 
					 | 
				
			||||||
      <RecursionField
 | 
					 | 
				
			||||||
        schema={
 | 
					 | 
				
			||||||
          new Schema({
 | 
					 | 
				
			||||||
            type: 'object',
 | 
					 | 
				
			||||||
            properties: {
 | 
					 | 
				
			||||||
              [popover.name]: {
 | 
					 | 
				
			||||||
                ...popover.toJSON(),
 | 
					 | 
				
			||||||
                'x-button': renderButton(),
 | 
					 | 
				
			||||||
              },
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          })
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      />
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <>
 | 
					 | 
				
			||||||
      {renderButton()}
 | 
					 | 
				
			||||||
      {props.children}
 | 
					 | 
				
			||||||
    </>
 | 
					 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const Action: ActionType = observer((props: any) => {
 | 
					Action.Modal = observer((props: any) => {
 | 
				
			||||||
  const schema = useFieldSchema();
 | 
					 | 
				
			||||||
  if (schema.properties) {
 | 
					 | 
				
			||||||
    return (
 | 
					 | 
				
			||||||
      <VisibleProvider
 | 
					 | 
				
			||||||
        initialVisible={props.initialVisible}
 | 
					 | 
				
			||||||
        containerRef={props.containerRef}
 | 
					 | 
				
			||||||
      >
 | 
					 | 
				
			||||||
        <BaseAction {...props} />
 | 
					 | 
				
			||||||
      </VisibleProvider>
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return <BaseAction {...props} />;
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Action.Link = observer((props) => {
 | 
					 | 
				
			||||||
  const schema = useFieldSchema();
 | 
					 | 
				
			||||||
  return <Link {...props}>{schema.title}</Link>;
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Action.URL = observer((props) => {
 | 
					 | 
				
			||||||
  const { url, ...others } = props;
 | 
					 | 
				
			||||||
  const schema = useFieldSchema();
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <a target={'_blank'} href={url} {...others}>
 | 
					 | 
				
			||||||
      {schema.title}
 | 
					 | 
				
			||||||
    </a>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Action.Container = observer((props) => {
 | 
					 | 
				
			||||||
  const { visible, setVisible } = useVisibleContext();
 | 
					 | 
				
			||||||
  return visible && <div>{props.children}</div>;
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Action.Popover = observer((props) => {
 | 
					 | 
				
			||||||
  const { visible, setVisible } = useVisibleContext();
 | 
					 | 
				
			||||||
  const schema = useFieldSchema();
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <Popover
 | 
					 | 
				
			||||||
      placement={'bottom'}
 | 
					 | 
				
			||||||
      visible={visible}
 | 
					 | 
				
			||||||
      onVisibleChange={(visible) => {
 | 
					 | 
				
			||||||
        setVisible(visible);
 | 
					 | 
				
			||||||
      }}
 | 
					 | 
				
			||||||
      {...props}
 | 
					 | 
				
			||||||
      content={props.children}
 | 
					 | 
				
			||||||
    >
 | 
					 | 
				
			||||||
      {schema['x-button']}
 | 
					 | 
				
			||||||
    </Popover>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Action.Drawer = observer((props) => {
 | 
					 | 
				
			||||||
  const field = useField();
 | 
					 | 
				
			||||||
  // const { visible, setVisible } = useVisibleContext();
 | 
					 | 
				
			||||||
  const { useAction = () => ({ async run() {} }), ...others } = props;
 | 
					 | 
				
			||||||
  const { schema } = useDesignable();
 | 
					  const { schema } = useDesignable();
 | 
				
			||||||
  const { visible, setVisible } = useVisibleContext();
 | 
					  const [visible, setVisible] = useContext(VisibleContext);
 | 
				
			||||||
  const { run } = useAction();
 | 
					 | 
				
			||||||
  const form = useForm();
 | 
					  const form = useForm();
 | 
				
			||||||
  console.log(`schema['x-decorator']`, schema['x-decorator']);
 | 
					  const isFormDecorator = schema['x-decorator'] === 'Form';
 | 
				
			||||||
  if (schema['x-decorator'] === 'Form.Decorator') {
 | 
					  return (
 | 
				
			||||||
    Object.assign(others, {
 | 
					    <Modal
 | 
				
			||||||
      footer: (
 | 
					      title={schema.title}
 | 
				
			||||||
        <Space>
 | 
					      destroyOnClose
 | 
				
			||||||
 | 
					      maskClosable
 | 
				
			||||||
 | 
					      footer={
 | 
				
			||||||
 | 
					        isFormDecorator
 | 
				
			||||||
 | 
					          ? [
 | 
				
			||||||
              <Button
 | 
					              <Button
 | 
				
			||||||
                onClick={async () => {
 | 
					                onClick={async () => {
 | 
				
			||||||
 | 
					                  if (isFormDecorator) {
 | 
				
			||||||
 | 
					                    form.clearErrors();
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                  setVisible(false);
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                Cancel
 | 
				
			||||||
 | 
					              </Button>,
 | 
				
			||||||
 | 
					              <Button
 | 
				
			||||||
 | 
					                type={'primary'}
 | 
				
			||||||
 | 
					                onClick={async () => {
 | 
				
			||||||
 | 
					                  if (isFormDecorator) {
 | 
				
			||||||
                    await form.submit();
 | 
					                    await form.submit();
 | 
				
			||||||
              await run();
 | 
					                  }
 | 
				
			||||||
 | 
					                  setVisible(false);
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                OK
 | 
				
			||||||
 | 
					              </Button>,
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					          : null
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      {...props}
 | 
				
			||||||
 | 
					      onCancel={() => {
 | 
				
			||||||
 | 
					        if (isFormDecorator) {
 | 
				
			||||||
 | 
					          form.clearErrors();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        setVisible(false);
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      visible={visible}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      {props.children}
 | 
				
			||||||
 | 
					    </Modal>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Action.Drawer = observer((props: any) => {
 | 
				
			||||||
 | 
					  const { schema } = useDesignable();
 | 
				
			||||||
 | 
					  const [visible, setVisible] = useContext(VisibleContext);
 | 
				
			||||||
 | 
					  const form = useForm();
 | 
				
			||||||
 | 
					  const isFormDecorator = schema['x-decorator'] === 'Form';
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Drawer
 | 
				
			||||||
 | 
					      width={'50%'}
 | 
				
			||||||
 | 
					      title={schema.title}
 | 
				
			||||||
 | 
					      maskClosable
 | 
				
			||||||
 | 
					      destroyOnClose
 | 
				
			||||||
 | 
					      footer={
 | 
				
			||||||
 | 
					        isFormDecorator && (
 | 
				
			||||||
 | 
					          <Space style={{ float: 'right' }}>
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					              onClick={async (e) => {
 | 
				
			||||||
 | 
					                form.clearErrors();
 | 
				
			||||||
 | 
					                props.onClose && (await props.onClose(e));
 | 
				
			||||||
 | 
					                setVisible(false);
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Cancel
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					              onClick={async (e) => {
 | 
				
			||||||
 | 
					                await form.submit();
 | 
				
			||||||
 | 
					                props.onOk && (await props.onOk(e));
 | 
				
			||||||
                setVisible(false);
 | 
					                setVisible(false);
 | 
				
			||||||
              }}
 | 
					              }}
 | 
				
			||||||
              type={'primary'}
 | 
					              type={'primary'}
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
              OK
 | 
					              OK
 | 
				
			||||||
            </Button>
 | 
					            </Button>
 | 
				
			||||||
          <Button
 | 
					 | 
				
			||||||
            onClick={async () => {
 | 
					 | 
				
			||||||
              form.clearErrors();
 | 
					 | 
				
			||||||
              setVisible(false);
 | 
					 | 
				
			||||||
            }}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            Cancel
 | 
					 | 
				
			||||||
          </Button>
 | 
					 | 
				
			||||||
          </Space>
 | 
					          </Space>
 | 
				
			||||||
      ),
 | 
					        )
 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      {...props}
 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <Drawer
 | 
					 | 
				
			||||||
      onClick={(e) => {
 | 
					 | 
				
			||||||
        e.stopPropagation();
 | 
					 | 
				
			||||||
      }}
 | 
					 | 
				
			||||||
      title={field.title}
 | 
					 | 
				
			||||||
      width={'50%'}
 | 
					 | 
				
			||||||
      {...others}
 | 
					 | 
				
			||||||
      visible={visible}
 | 
					      visible={visible}
 | 
				
			||||||
      destroyOnClose
 | 
					      onClose={async (e) => {
 | 
				
			||||||
      onClose={(e) => {
 | 
					        props.onClose && (await props.onClose(e));
 | 
				
			||||||
        e.stopPropagation();
 | 
					 | 
				
			||||||
        form.clearErrors();
 | 
					 | 
				
			||||||
        setVisible(false);
 | 
					        setVisible(false);
 | 
				
			||||||
      }}
 | 
					      }}
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
@ -281,145 +147,121 @@ Action.Drawer = observer((props) => {
 | 
				
			|||||||
  );
 | 
					  );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const [DesignableContextProvider, useDesignableContext] = constate(() => {
 | 
					Action.Dropdown = observer((props: any) => {
 | 
				
			||||||
  return useDesignable();
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { DesignableBar } from './designableBar';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function useDesignableValues() {
 | 
					 | 
				
			||||||
  const { schema } = useDesignableContext();
 | 
					 | 
				
			||||||
  return schema
 | 
					 | 
				
			||||||
    ? {
 | 
					 | 
				
			||||||
        title: schema.title,
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    : {};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function useDesignableUpdate() {
 | 
					 | 
				
			||||||
  const { schema, refresh } = useDesignableContext();
 | 
					 | 
				
			||||||
  const form = useForm();
 | 
					 | 
				
			||||||
  return {
 | 
					 | 
				
			||||||
    async run() {
 | 
					 | 
				
			||||||
      schema.title = form.values.title;
 | 
					 | 
				
			||||||
      refresh();
 | 
					 | 
				
			||||||
      console.log('useDesignableUpdate', schema, form.values);
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function useDesignableSchemaRemove() {
 | 
					 | 
				
			||||||
  const { schema, refresh, remove } = useDesignableContext();
 | 
					 | 
				
			||||||
  return {
 | 
					 | 
				
			||||||
    async run() {
 | 
					 | 
				
			||||||
      remove();
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Action.Modal = observer((props) => {
 | 
					 | 
				
			||||||
  const { useAction = () => ({ async run() {} }), ...others } = props;
 | 
					 | 
				
			||||||
  const { schema } = useDesignable();
 | 
					  const { schema } = useDesignable();
 | 
				
			||||||
  const { setVisible: setDropdownVisible } = useDropdownVisibleContext();
 | 
					  const componentProps = schema.parent['x-component-props'] || {};
 | 
				
			||||||
  const { visible, setVisible } = useVisibleContext();
 | 
					 | 
				
			||||||
  const { run } = useAction();
 | 
					 | 
				
			||||||
  const form = useForm();
 | 
					 | 
				
			||||||
  // 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 (
 | 
					  return (
 | 
				
			||||||
    <Modal
 | 
					    <Dropdown
 | 
				
			||||||
      title={schema.title}
 | 
					      trigger={['click']}
 | 
				
			||||||
      width={'50%'}
 | 
					      overlay={
 | 
				
			||||||
      {...others}
 | 
					        <Menu>
 | 
				
			||||||
      visible={visible}
 | 
					          <RecursionField schema={schema} onlyRenderProperties />
 | 
				
			||||||
      closable
 | 
					        </Menu>
 | 
				
			||||||
      maskClosable
 | 
					      }
 | 
				
			||||||
      destroyOnClose
 | 
					 | 
				
			||||||
      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();
 | 
					 | 
				
			||||||
        setVisible(false);
 | 
					 | 
				
			||||||
        setDropdownVisible && setDropdownVisible(false);
 | 
					 | 
				
			||||||
      }}
 | 
					 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <div
 | 
					      <Button {...componentProps}>{schema.title || schema.parent.title}</Button>
 | 
				
			||||||
      //onClick={(e) => e.stopPropagation()}
 | 
					    </Dropdown>
 | 
				
			||||||
      >
 | 
					 | 
				
			||||||
        {props.children}
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    </Modal>
 | 
					 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const [DropdownVisibleProvider, useDropdownVisibleContext] = constate(
 | 
					Action.Popover = observer((props) => {
 | 
				
			||||||
  (props: any = {}) => {
 | 
					  const fieldSchema = useFieldSchema();
 | 
				
			||||||
    const { initialVisible = false } = props;
 | 
					  const { schema } = useDesignable();
 | 
				
			||||||
    const [visible, setVisible] = useState(initialVisible);
 | 
					  const form = useForm();
 | 
				
			||||||
    return { visible, setVisible };
 | 
					  const isFormDecorator = schema['x-decorator'] === 'Form';
 | 
				
			||||||
  },
 | 
					  const [visible, setVisible] = useContext(VisibleContext);
 | 
				
			||||||
);
 | 
					  console.log('Action.Popover', schema, fieldSchema);
 | 
				
			||||||
 | 
					  const componentProps =
 | 
				
			||||||
const ActionDropdown = observer((props: any) => {
 | 
					    (schema.parent && schema.parent['x-component-props']) || {};
 | 
				
			||||||
  const { icon, ...others } = props;
 | 
					 | 
				
			||||||
  const schema = useFieldSchema();
 | 
					 | 
				
			||||||
  const { visible, setVisible } = useDropdownVisibleContext();
 | 
					 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Popover
 | 
					    <Popover
 | 
				
			||||||
      visible={visible}
 | 
					      visible={visible}
 | 
				
			||||||
      onVisibleChange={(visible) => {
 | 
					      trigger={['click']}
 | 
				
			||||||
        setVisible(visible);
 | 
					      onVisibleChange={setVisible}
 | 
				
			||||||
 | 
					      {...props}
 | 
				
			||||||
 | 
					      title={schema.title}
 | 
				
			||||||
 | 
					      content={
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					          {props.children}
 | 
				
			||||||
 | 
					          {isFormDecorator && (
 | 
				
			||||||
 | 
					            <div>
 | 
				
			||||||
 | 
					              <Space>
 | 
				
			||||||
 | 
					                <Button
 | 
				
			||||||
 | 
					                  onClick={() => {
 | 
				
			||||||
 | 
					                    form.clearErrors();
 | 
				
			||||||
 | 
					                    setVisible(false);
 | 
				
			||||||
                  }}
 | 
					                  }}
 | 
				
			||||||
      // {...props}
 | 
					 | 
				
			||||||
      overlayClassName={'nb-action-group'}
 | 
					 | 
				
			||||||
      // trigger={'click'}
 | 
					 | 
				
			||||||
      content={props.children}
 | 
					 | 
				
			||||||
      placement={'bottomLeft'}
 | 
					 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
      <Button icon={icon ? <Icon type={icon} /> : null} {...others}>
 | 
					                  Cancel
 | 
				
			||||||
        {schema.title}
 | 
					 | 
				
			||||||
                </Button>
 | 
					                </Button>
 | 
				
			||||||
 | 
					                <Button
 | 
				
			||||||
 | 
					                  type={'primary'}
 | 
				
			||||||
 | 
					                  onClick={async () => {
 | 
				
			||||||
 | 
					                    await form.submit();
 | 
				
			||||||
 | 
					                    setVisible(false);
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  Ok
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </Space>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <Button {...componentProps}>{schema.parent.title || schema.title}</Button>
 | 
				
			||||||
    </Popover>
 | 
					    </Popover>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Action.Dropdown = observer((props) => {
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <DropdownVisibleProvider>
 | 
					 | 
				
			||||||
      <ActionDropdown {...props} />
 | 
					 | 
				
			||||||
      <div style={{ display: 'none' }}>{props.children}</div>
 | 
					 | 
				
			||||||
    </DropdownVisibleProvider>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Action.DesignableBar = () => {
 | 
					Action.DesignableBar = () => {
 | 
				
			||||||
  const field = useField();
 | 
					  const field = useField();
 | 
				
			||||||
  // const schema = useFieldSchema();
 | 
					  // const schema = useFieldSchema();
 | 
				
			||||||
  const { schema, insertAfter, refresh } = useDesignable();
 | 
					  const { schema, insertAfter, refresh } = useDesignable();
 | 
				
			||||||
  const [visible, setVisible] = useState(false);
 | 
					  const [visible, setVisible] = useState(false);
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <DesignableContextProvider>
 | 
					    <div className={cls('designable-bar', { active: visible })}>
 | 
				
			||||||
      <div className={classNames('designable-bar', { active: visible })}>
 | 
					 | 
				
			||||||
      <span
 | 
					      <span
 | 
				
			||||||
        onClick={(e) => {
 | 
					        onClick={(e) => {
 | 
				
			||||||
          e.stopPropagation();
 | 
					          e.stopPropagation();
 | 
				
			||||||
        }}
 | 
					        }}
 | 
				
			||||||
          className={classNames('designable-bar-actions', { active: visible })}
 | 
					        className={cls('designable-bar-actions', { active: visible })}
 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
          <SchemaRenderer schema={DesignableBar.Action} />
 | 
					        <Dropdown
 | 
				
			||||||
 | 
					          trigger={['click']}
 | 
				
			||||||
 | 
					          visible={visible}
 | 
				
			||||||
 | 
					          onVisibleChange={(visible) => {
 | 
				
			||||||
 | 
					            setVisible(visible);
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					          overlay={
 | 
				
			||||||
 | 
					            <Menu>
 | 
				
			||||||
 | 
					              <Menu.Item
 | 
				
			||||||
 | 
					                onClick={(e) => {
 | 
				
			||||||
 | 
					                  // console.log({ field, schema });
 | 
				
			||||||
 | 
					                  schema.title = '按钮文案被修改了';
 | 
				
			||||||
 | 
					                  // field.setTitle('按钮文案被修改了');
 | 
				
			||||||
 | 
					                  // setVisible(false);
 | 
				
			||||||
 | 
					                  refresh();
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                点击修改按钮文案
 | 
				
			||||||
 | 
					              </Menu.Item>
 | 
				
			||||||
 | 
					              <Menu.Item
 | 
				
			||||||
 | 
					                onClick={() => {
 | 
				
			||||||
 | 
					                  insertAfter({
 | 
				
			||||||
 | 
					                    name: uid(),
 | 
				
			||||||
 | 
					                    'x-component': 'Input',
 | 
				
			||||||
 | 
					                  });
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                insertAfter
 | 
				
			||||||
 | 
					              </Menu.Item>
 | 
				
			||||||
 | 
					            </Menu>
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          <MenuOutlined />
 | 
				
			||||||
 | 
					        </Dropdown>
 | 
				
			||||||
      </span>
 | 
					      </span>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    </DesignableContextProvider>
 | 
					 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -56,13 +56,13 @@ FormItem.DesignableBar = () => {
 | 
				
			|||||||
                <Menu.Item
 | 
					                <Menu.Item
 | 
				
			||||||
                  onClick={(e) => {
 | 
					                  onClick={(e) => {
 | 
				
			||||||
                    const title = uid();
 | 
					                    const title = uid();
 | 
				
			||||||
                    schema.title = title;
 | 
					                    // schema.title = title;
 | 
				
			||||||
                    field.title = title;
 | 
					                    field.title = title;
 | 
				
			||||||
                    setVisible(false);
 | 
					                    setVisible(false);
 | 
				
			||||||
                    refresh();
 | 
					                    // refresh();
 | 
				
			||||||
                  }}
 | 
					                  }}
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
                  点击修改按钮文案
 | 
					                  修改文案
 | 
				
			||||||
                </Menu.Item>
 | 
					                </Menu.Item>
 | 
				
			||||||
              </Menu>
 | 
					              </Menu>
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
				
			|||||||
@ -20,6 +20,7 @@ group:
 | 
				
			|||||||
  // 添加其他节点
 | 
					  // 添加其他节点
 | 
				
			||||||
</Form>
 | 
					</Form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Form 用作 decorator 时只用于创建独立的作用域
 | 
				
			||||||
<Table x-decorator={'Form'}/>
 | 
					<Table x-decorator={'Form'}/>
 | 
				
			||||||
</pre>
 | 
					</pre>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -36,15 +37,19 @@ import React from 'react';
 | 
				
			|||||||
import { SchemaRenderer } from '../';
 | 
					import { SchemaRenderer } from '../';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const schema = {
 | 
					const schema = {
 | 
				
			||||||
  name: 'form',
 | 
					 | 
				
			||||||
  type: 'object',
 | 
					  type: 'object',
 | 
				
			||||||
 | 
					  name: 'form',
 | 
				
			||||||
  'x-component': 'Form',
 | 
					  'x-component': 'Form',
 | 
				
			||||||
 | 
					  'x-component-props': {
 | 
				
			||||||
 | 
					    layout: 'horizontal',
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  properties: {
 | 
					  properties: {
 | 
				
			||||||
    username: {
 | 
					    username: {
 | 
				
			||||||
      type: 'string',
 | 
					      type: 'string',
 | 
				
			||||||
      title: '用户名',
 | 
					      title: '用户名',
 | 
				
			||||||
      required: true,
 | 
					      required: true,
 | 
				
			||||||
      'x-decorator': 'FormItem',
 | 
					      'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					      'x-designable-bar': 'FormItem.DesignableBar',
 | 
				
			||||||
      'x-component': 'Input',
 | 
					      'x-component': 'Input',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    password: {
 | 
					    password: {
 | 
				
			||||||
@ -59,7 +64,7 @@ const schema = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export default () => {
 | 
					export default () => {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <SchemaRenderer schema={schema} />
 | 
					    <SchemaRenderer debug={true} schema={schema} />
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
@ -124,25 +129,14 @@ const schema = {
 | 
				
			|||||||
          type: 'void',
 | 
					          type: 'void',
 | 
				
			||||||
          'x-component': 'Action',
 | 
					          'x-component': 'Action',
 | 
				
			||||||
          'x-component-props': {
 | 
					          'x-component-props': {
 | 
				
			||||||
            // block: true,
 | 
					 | 
				
			||||||
            type: 'primary',
 | 
					            type: 'primary',
 | 
				
			||||||
            useAction: '{{ useLogin }}',
 | 
					 | 
				
			||||||
            style: {
 | 
					 | 
				
			||||||
              // width: '100%',
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          title: '提交',
 | 
					          title: '提交',
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        reset: {
 | 
					        reset: {
 | 
				
			||||||
          type: 'void',
 | 
					          type: 'void',
 | 
				
			||||||
          'x-component': 'Action',
 | 
					          'x-component': 'Action',
 | 
				
			||||||
          'x-component-props': {
 | 
					          'x-component-props': {},
 | 
				
			||||||
            // block: true,
 | 
					 | 
				
			||||||
            useAction: '{{ useLogin }}',
 | 
					 | 
				
			||||||
            style: {
 | 
					 | 
				
			||||||
              // width: '100%',
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          title: '重置',
 | 
					          title: '重置',
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
@ -220,12 +214,7 @@ const schema = {
 | 
				
			|||||||
          type: 'void',
 | 
					          type: 'void',
 | 
				
			||||||
          'x-component': 'Action',
 | 
					          'x-component': 'Action',
 | 
				
			||||||
          'x-component-props': {
 | 
					          'x-component-props': {
 | 
				
			||||||
            // block: true,
 | 
					 | 
				
			||||||
            type: 'primary',
 | 
					            type: 'primary',
 | 
				
			||||||
            useAction: '{{ useLogin }}',
 | 
					 | 
				
			||||||
            style: {
 | 
					 | 
				
			||||||
              // width: '100%',
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          title: '提交',
 | 
					          title: '提交',
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -233,11 +222,6 @@ const schema = {
 | 
				
			|||||||
          type: 'void',
 | 
					          type: 'void',
 | 
				
			||||||
          'x-component': 'Action',
 | 
					          'x-component': 'Action',
 | 
				
			||||||
          'x-component-props': {
 | 
					          'x-component-props': {
 | 
				
			||||||
            // block: true,
 | 
					 | 
				
			||||||
            useAction: '{{ useLogin }}',
 | 
					 | 
				
			||||||
            style: {
 | 
					 | 
				
			||||||
              // width: '100%',
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          title: '重置',
 | 
					          title: '重置',
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
				
			|||||||
@ -9,77 +9,53 @@ import {
 | 
				
			|||||||
  FormProvider,
 | 
					  FormProvider,
 | 
				
			||||||
  ISchema,
 | 
					  ISchema,
 | 
				
			||||||
} from '@formily/react';
 | 
					} from '@formily/react';
 | 
				
			||||||
import { SchemaRenderer, useDesignable } from '../';
 | 
					import { useSchemaPath, SchemaField, useDesignable } from '../';
 | 
				
			||||||
import get from 'lodash/get';
 | 
					import get from 'lodash/get';
 | 
				
			||||||
import { Dropdown, Menu } from 'antd';
 | 
					import { Dropdown, Menu } from 'antd';
 | 
				
			||||||
import {
 | 
					import { MenuOutlined } from '@ant-design/icons';
 | 
				
			||||||
  MenuOutlined,
 | 
					 | 
				
			||||||
  ArrowUpOutlined,
 | 
					 | 
				
			||||||
  ArrowDownOutlined,
 | 
					 | 
				
			||||||
  DeleteOutlined,
 | 
					 | 
				
			||||||
} from '@ant-design/icons';
 | 
					 | 
				
			||||||
import { useMouseEvents } from 'beautiful-react-hooks';
 | 
					 | 
				
			||||||
import cls from 'classnames';
 | 
					import cls from 'classnames';
 | 
				
			||||||
 | 
					import { FormLayout } from '@formily/antd';
 | 
				
			||||||
import './style.less';
 | 
					import './style.less';
 | 
				
			||||||
import { clone } from '@formily/shared';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function useDefaultValues() {
 | 
					 | 
				
			||||||
  return {};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const Form: any = observer((props: any) => {
 | 
					export const Form: any = observer((props: any) => {
 | 
				
			||||||
  const scope = useContext(SchemaExpressionScopeContext);
 | 
					  const { useValues = () => ({}), ...others } = props;
 | 
				
			||||||
  const { useValues = useDefaultValues } = props;
 | 
					  const initialValues = useValues();
 | 
				
			||||||
  const values = useValues();
 | 
					 | 
				
			||||||
  const form = useMemo(() => {
 | 
					  const form = useMemo(() => {
 | 
				
			||||||
    console.log('Form.useMemo', values);
 | 
					    console.log('Form.useMemo', initialValues);
 | 
				
			||||||
    return createForm({
 | 
					    return createForm({ initialValues });
 | 
				
			||||||
      values,
 | 
					  }, []);
 | 
				
			||||||
      // values: clone(values),
 | 
					  const { schema } = useDesignable();
 | 
				
			||||||
    });
 | 
					  const path = useSchemaPath();
 | 
				
			||||||
  }, [values]);
 | 
					  return (
 | 
				
			||||||
  const schema = useFieldSchema();
 | 
					    <FormProvider form={form}>
 | 
				
			||||||
  const { schema: designableSchema, refresh } = useDesignable();
 | 
					      {schema['x-decorator'] === 'Form' ? (
 | 
				
			||||||
  const formSchema: ISchema = schema['x-decorator'] === 'Form' ? {
 | 
					        <SchemaField
 | 
				
			||||||
 | 
					          schema={{
 | 
				
			||||||
            type: 'object',
 | 
					            type: 'object',
 | 
				
			||||||
            properties: {
 | 
					            properties: {
 | 
				
			||||||
              [schema.name]: {
 | 
					              [schema.name]: {
 | 
				
			||||||
                ...schema.toJSON(),
 | 
					                ...schema.toJSON(),
 | 
				
			||||||
        "x-decorator": 'Form.Decorator',
 | 
					                'x-path': path,
 | 
				
			||||||
      }
 | 
					                'x-decorator': 'Form.__Decorator',
 | 
				
			||||||
    }
 | 
					              },
 | 
				
			||||||
  } : schema.toJSON();
 | 
					            },
 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <SchemaRenderer
 | 
					 | 
				
			||||||
      scope={scope}
 | 
					 | 
				
			||||||
      // components={options.components}
 | 
					 | 
				
			||||||
      onRefresh={(subSchema: Schema) => {
 | 
					 | 
				
			||||||
        designableSchema.properties = subSchema.properties;
 | 
					 | 
				
			||||||
        refresh();
 | 
					 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
      form={form}
 | 
					 | 
				
			||||||
      schema={formSchema}
 | 
					 | 
				
			||||||
      onlyRenderProperties
 | 
					 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
 | 
					      ) : (
 | 
				
			||||||
 | 
					        <FormLayout layout={'vertical'} {...others}>
 | 
				
			||||||
 | 
					          <SchemaField
 | 
				
			||||||
 | 
					            schema={{
 | 
				
			||||||
 | 
					              type: 'object',
 | 
				
			||||||
 | 
					              properties: schema.properties,
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </FormLayout>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    </FormProvider>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Form.Decorator = ({children}) => children;
 | 
					Form.__Decorator = ({ children }) => children;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Form.DesignableBar = (props) => {
 | 
					Form.DesignableBar = (props) => {
 | 
				
			||||||
  const { active } = props;
 | 
					  const { active } = props;
 | 
				
			||||||
 | 
				
			|||||||
@ -3,77 +3,10 @@ import { connect, mapProps, mapReadPretty } from '@formily/react';
 | 
				
			|||||||
import { Input, Popover, Button } from 'antd';
 | 
					import { Input, Popover, Button } from 'antd';
 | 
				
			||||||
import { LoadingOutlined, CloseOutlined } from '@ant-design/icons';
 | 
					import { LoadingOutlined, CloseOutlined } from '@ant-design/icons';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  createFromIconfontCN,
 | 
					  icons,
 | 
				
			||||||
  UserOutlined,
 | 
					  hasIcon,
 | 
				
			||||||
  TeamOutlined,
 | 
					  IconPicker as Icon,
 | 
				
			||||||
  DatabaseOutlined,
 | 
					} from '../../components/icon-picker';
 | 
				
			||||||
  DashboardOutlined,
 | 
					 | 
				
			||||||
  SettingOutlined,
 | 
					 | 
				
			||||||
  TableOutlined,
 | 
					 | 
				
			||||||
  MenuOutlined,
 | 
					 | 
				
			||||||
  HistoryOutlined,
 | 
					 | 
				
			||||||
  NotificationOutlined,
 | 
					 | 
				
			||||||
} 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]);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
registerIcons({
 | 
					 | 
				
			||||||
  // HistoryOutlined,
 | 
					 | 
				
			||||||
  // MenuOutlined,
 | 
					 | 
				
			||||||
  // TableOutlined,
 | 
					 | 
				
			||||||
  // SettingOutlined,
 | 
					 | 
				
			||||||
  // TeamOutlined,
 | 
					 | 
				
			||||||
  // UserOutlined,
 | 
					 | 
				
			||||||
  // DatabaseOutlined,
 | 
					 | 
				
			||||||
  // DashboardOutlined,
 | 
					 | 
				
			||||||
  // NotificationOutlined,
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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} />;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
function IconField(props: any) {
 | 
					function IconField(props: any) {
 | 
				
			||||||
  const { value, onChange } = props;
 | 
					  const { value, onChange } = props;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,2 +1,11 @@
 | 
				
			|||||||
// export * from './DesignableSchemaField';
 | 
					import { createContext } from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export * from '../components/schema-renderer';
 | 
					export * from '../components/schema-renderer';
 | 
				
			||||||
 | 
					export const VisibleContext = createContext(null);
 | 
				
			||||||
 | 
					export const DesignableBarContext = createContext(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function useDefaultAction() {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    async run () {}
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										86
									
								
								packages/client/src/schemas/menu/demos/demo1.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								packages/client/src/schemas/menu/demos/demo1.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * title: 横向菜单
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { SchemaRenderer } from '@nocobase/client';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const schema = {
 | 
				
			||||||
 | 
					  type: 'object',
 | 
				
			||||||
 | 
					  properties: {
 | 
				
			||||||
 | 
					    menu1: {
 | 
				
			||||||
 | 
					      type: 'void',
 | 
				
			||||||
 | 
					      'x-component': 'Menu',
 | 
				
			||||||
 | 
					      'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					      'x-component-props': {
 | 
				
			||||||
 | 
					        mode: 'horizontal',
 | 
				
			||||||
 | 
					        theme: 'dark',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      properties: {
 | 
				
			||||||
 | 
					        item1: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          title: `菜单1`,
 | 
				
			||||||
 | 
					          'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					          'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        item2: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          title: `菜单2`,
 | 
				
			||||||
 | 
					          'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					          'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        item3: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          title: '菜单组3',
 | 
				
			||||||
 | 
					          'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					          'x-component': 'Menu.SubMenu',
 | 
				
			||||||
 | 
					          properties: {
 | 
				
			||||||
 | 
					            item5: {
 | 
				
			||||||
 | 
					              type: 'void',
 | 
				
			||||||
 | 
					              title: `子菜单5`,
 | 
				
			||||||
 | 
					              'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					              'x-component': 'Menu.SubMenu',
 | 
				
			||||||
 | 
					              properties: {
 | 
				
			||||||
 | 
					                item8: {
 | 
				
			||||||
 | 
					                  type: 'void',
 | 
				
			||||||
 | 
					                  title: `子菜单8`,
 | 
				
			||||||
 | 
					                  'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					                  'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                item9: {
 | 
				
			||||||
 | 
					                  type: 'void',
 | 
				
			||||||
 | 
					                  title: `子菜单9`,
 | 
				
			||||||
 | 
					                  'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					                  'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        item4: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          title: '菜单组4',
 | 
				
			||||||
 | 
					          'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					          'x-component': 'Menu.SubMenu',
 | 
				
			||||||
 | 
					          properties: {
 | 
				
			||||||
 | 
					            item6: {
 | 
				
			||||||
 | 
					              type: 'void',
 | 
				
			||||||
 | 
					              title: `子菜单6`,
 | 
				
			||||||
 | 
					              'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					              'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            item7: {
 | 
				
			||||||
 | 
					              type: 'void',
 | 
				
			||||||
 | 
					              title: `子菜单7`,
 | 
				
			||||||
 | 
					              'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					              'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default () => {
 | 
				
			||||||
 | 
					  return <SchemaRenderer schema={schema} />;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										90
									
								
								packages/client/src/schemas/menu/demos/demo2.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								packages/client/src/schemas/menu/demos/demo2.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,90 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * title: 横向菜单
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { SchemaRenderer } from '@nocobase/client';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const schema = {
 | 
				
			||||||
 | 
					  type: 'object',
 | 
				
			||||||
 | 
					  properties: {
 | 
				
			||||||
 | 
					    menu1: {
 | 
				
			||||||
 | 
					      type: 'void',
 | 
				
			||||||
 | 
					      'x-component': 'Menu',
 | 
				
			||||||
 | 
					      'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					      'x-component-props': {
 | 
				
			||||||
 | 
					        mode: 'inline',
 | 
				
			||||||
 | 
					        //  theme: 'dark',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      properties: {
 | 
				
			||||||
 | 
					        item1: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          title: `菜单1`,
 | 
				
			||||||
 | 
					          'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					          'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        item2: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          title: `菜单2`,
 | 
				
			||||||
 | 
					          'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					          'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        item3: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          title: '菜单组3',
 | 
				
			||||||
 | 
					          'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					          'x-component': 'Menu.SubMenu',
 | 
				
			||||||
 | 
					          properties: {
 | 
				
			||||||
 | 
					            item5: {
 | 
				
			||||||
 | 
					              type: 'void',
 | 
				
			||||||
 | 
					              title: `子菜单5`,
 | 
				
			||||||
 | 
					              'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					              'x-component': 'Menu.SubMenu',
 | 
				
			||||||
 | 
					              properties: {
 | 
				
			||||||
 | 
					                item8: {
 | 
				
			||||||
 | 
					                  type: 'void',
 | 
				
			||||||
 | 
					                  title: `子菜单8`,
 | 
				
			||||||
 | 
					                  'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					                  'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                item9: {
 | 
				
			||||||
 | 
					                  type: 'void',
 | 
				
			||||||
 | 
					                  title: `子菜单9`,
 | 
				
			||||||
 | 
					                  'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					                  'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        item4: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          title: '菜单组4',
 | 
				
			||||||
 | 
					          'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					          'x-component': 'Menu.SubMenu',
 | 
				
			||||||
 | 
					          properties: {
 | 
				
			||||||
 | 
					            item6: {
 | 
				
			||||||
 | 
					              type: 'void',
 | 
				
			||||||
 | 
					              title: `子菜单6`,
 | 
				
			||||||
 | 
					              'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					              'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            item7: {
 | 
				
			||||||
 | 
					              type: 'void',
 | 
				
			||||||
 | 
					              title: `子菜单7`,
 | 
				
			||||||
 | 
					              'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					              'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default () => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div style={{ width: 200 }}>
 | 
				
			||||||
 | 
					      <SchemaRenderer schema={schema} />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										68
									
								
								packages/client/src/schemas/menu/demos/demo3.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								packages/client/src/schemas/menu/demos/demo3.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,68 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * title: 横向菜单
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { SchemaRenderer } from '@nocobase/client';
 | 
				
			||||||
 | 
					import { ISchema } from '@formily/react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const schema: ISchema = {
 | 
				
			||||||
 | 
					  type: 'object',
 | 
				
			||||||
 | 
					  properties: {
 | 
				
			||||||
 | 
					    menu1: {
 | 
				
			||||||
 | 
					      type: 'void',
 | 
				
			||||||
 | 
					      'x-component': 'Menu',
 | 
				
			||||||
 | 
					      'x-component-props': {
 | 
				
			||||||
 | 
					        mode: 'horizontal',
 | 
				
			||||||
 | 
					        // theme: 'dark',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      properties: {
 | 
				
			||||||
 | 
					        item1: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          title: `菜单1`,
 | 
				
			||||||
 | 
					          'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					          'x-component': 'Menu.Action',
 | 
				
			||||||
 | 
					          properties: {
 | 
				
			||||||
 | 
					            modal1: {
 | 
				
			||||||
 | 
					              type: 'void',
 | 
				
			||||||
 | 
					              title: '对话框标题',
 | 
				
			||||||
 | 
					              'x-component': 'Action.Modal',
 | 
				
			||||||
 | 
					              properties: {
 | 
				
			||||||
 | 
					                input: {
 | 
				
			||||||
 | 
					                  type: 'string',
 | 
				
			||||||
 | 
					                  'x-component': 'Input',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        item2: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          title: `菜单2`,
 | 
				
			||||||
 | 
					          'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					          'x-component': 'Menu.Action',
 | 
				
			||||||
 | 
					          properties: {
 | 
				
			||||||
 | 
					            modal2: {
 | 
				
			||||||
 | 
					              type: 'void',
 | 
				
			||||||
 | 
					              title: '对话框标题',
 | 
				
			||||||
 | 
					              'x-component': 'Action.Modal',
 | 
				
			||||||
 | 
					              properties: {
 | 
				
			||||||
 | 
					                input: {
 | 
				
			||||||
 | 
					                  type: 'string',
 | 
				
			||||||
 | 
					                  'x-component': 'Input',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default () => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div style={{ width: 200 }}>
 | 
				
			||||||
 | 
					      <SchemaRenderer schema={schema} />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										98
									
								
								packages/client/src/schemas/menu/demos/demo4.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								packages/client/src/schemas/menu/demos/demo4.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,98 @@
 | 
				
			|||||||
 | 
					import React, { useRef } from 'react';
 | 
				
			||||||
 | 
					import { SchemaRenderer } from '@nocobase/client';
 | 
				
			||||||
 | 
					import { Layout } from 'antd';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const schema = {
 | 
				
			||||||
 | 
					  type: 'object',
 | 
				
			||||||
 | 
					  properties: {
 | 
				
			||||||
 | 
					    menu1: {
 | 
				
			||||||
 | 
					      type: 'void',
 | 
				
			||||||
 | 
					      'x-component': 'Menu',
 | 
				
			||||||
 | 
					      'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					      'x-component-props': {
 | 
				
			||||||
 | 
					        sideMenuRef: '{{ sideMenuRef }}',
 | 
				
			||||||
 | 
					        mode: 'mix',
 | 
				
			||||||
 | 
					        theme: 'dark',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      properties: {
 | 
				
			||||||
 | 
					        item1: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          title: `菜单1`,
 | 
				
			||||||
 | 
					          'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					          'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        item2: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          title: `菜单2`,
 | 
				
			||||||
 | 
					          'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					          'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        item3: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          title: '菜单组3',
 | 
				
			||||||
 | 
					          'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					          'x-component': 'Menu.SubMenu',
 | 
				
			||||||
 | 
					          properties: {
 | 
				
			||||||
 | 
					            item5: {
 | 
				
			||||||
 | 
					              type: 'void',
 | 
				
			||||||
 | 
					              title: `子菜单5`,
 | 
				
			||||||
 | 
					              'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					              'x-component': 'Menu.SubMenu',
 | 
				
			||||||
 | 
					              properties: {
 | 
				
			||||||
 | 
					                item8: {
 | 
				
			||||||
 | 
					                  type: 'void',
 | 
				
			||||||
 | 
					                  title: `子菜单8`,
 | 
				
			||||||
 | 
					                  'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					                  'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                item9: {
 | 
				
			||||||
 | 
					                  type: 'void',
 | 
				
			||||||
 | 
					                  title: `子菜单9`,
 | 
				
			||||||
 | 
					                  'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					                  'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        item4: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          title: '菜单组4',
 | 
				
			||||||
 | 
					          'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					          'x-component': 'Menu.SubMenu',
 | 
				
			||||||
 | 
					          properties: {
 | 
				
			||||||
 | 
					            item6: {
 | 
				
			||||||
 | 
					              type: 'void',
 | 
				
			||||||
 | 
					              title: `子菜单6`,
 | 
				
			||||||
 | 
					              'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					              'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            item7: {
 | 
				
			||||||
 | 
					              type: 'void',
 | 
				
			||||||
 | 
					              title: `子菜单7`,
 | 
				
			||||||
 | 
					              'x-designable-bar': 'Menu.DesignableBar',
 | 
				
			||||||
 | 
					              'x-component': 'Menu.Item',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default () => {
 | 
				
			||||||
 | 
					  const sideMenuRef = useRef();
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Layout>
 | 
				
			||||||
 | 
					      <Layout.Header>
 | 
				
			||||||
 | 
					        <SchemaRenderer
 | 
				
			||||||
 | 
					          scope={{ sideMenuRef }}
 | 
				
			||||||
 | 
					          schema={schema}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      </Layout.Header>
 | 
				
			||||||
 | 
					      <Layout>
 | 
				
			||||||
 | 
					        <Layout.Sider theme={'light'} ref={sideMenuRef}></Layout.Sider>
 | 
				
			||||||
 | 
					      </Layout>
 | 
				
			||||||
 | 
					    </Layout>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -51,539 +51,16 @@ group:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
### 横向菜单
 | 
					### 横向菜单
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```tsx
 | 
					<code src="./demos/demo1.tsx"/>
 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * title: 横向菜单
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
import React from 'react';
 | 
					 | 
				
			||||||
import { SchemaRenderer } from '../';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const schema = {
 | 
					 | 
				
			||||||
  type: 'object',
 | 
					 | 
				
			||||||
  properties: {
 | 
					 | 
				
			||||||
    menu1: {
 | 
					 | 
				
			||||||
      type: 'void',
 | 
					 | 
				
			||||||
      'x-component': 'Menu',
 | 
					 | 
				
			||||||
      'x-designable-bar': 'Menu.DesignableBar',
 | 
					 | 
				
			||||||
      'x-component-props': {
 | 
					 | 
				
			||||||
        mode: 'horizontal',
 | 
					 | 
				
			||||||
        theme: 'dark',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      properties: {
 | 
					 | 
				
			||||||
        item1: {
 | 
					 | 
				
			||||||
          type: 'void',
 | 
					 | 
				
			||||||
          title: `菜单1`,
 | 
					 | 
				
			||||||
          'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        item2: {
 | 
					 | 
				
			||||||
          type: 'void',
 | 
					 | 
				
			||||||
          title: `菜单2`,
 | 
					 | 
				
			||||||
          'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        item3: {
 | 
					 | 
				
			||||||
          type: 'void',
 | 
					 | 
				
			||||||
          title: '菜单组3',
 | 
					 | 
				
			||||||
          'x-component': 'Menu.SubMenu',
 | 
					 | 
				
			||||||
          properties: {
 | 
					 | 
				
			||||||
            item5: {
 | 
					 | 
				
			||||||
              type: 'void',
 | 
					 | 
				
			||||||
              title: `子菜单5`,
 | 
					 | 
				
			||||||
              'x-component': 'Menu.SubMenu',
 | 
					 | 
				
			||||||
              properties: {
 | 
					 | 
				
			||||||
                item8: {
 | 
					 | 
				
			||||||
                  type: 'void',
 | 
					 | 
				
			||||||
                  title: `子菜单8`,
 | 
					 | 
				
			||||||
                  'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                item9: {
 | 
					 | 
				
			||||||
                  type: 'void',
 | 
					 | 
				
			||||||
                  title: `子菜单9`,
 | 
					 | 
				
			||||||
                  'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
              },
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        item4: {
 | 
					 | 
				
			||||||
          type: 'void',
 | 
					 | 
				
			||||||
          title: '菜单组4',
 | 
					 | 
				
			||||||
          'x-component': 'Menu.SubMenu',
 | 
					 | 
				
			||||||
          properties: {
 | 
					 | 
				
			||||||
            item6: {
 | 
					 | 
				
			||||||
              type: 'void',
 | 
					 | 
				
			||||||
              title: `子菜单6`,
 | 
					 | 
				
			||||||
              'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            item7: {
 | 
					 | 
				
			||||||
              type: 'void',
 | 
					 | 
				
			||||||
              title: `子菜单7`,
 | 
					 | 
				
			||||||
              'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default () => {
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <SchemaRenderer schema={schema} />
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
### 竖向菜单
 | 
					### 竖向菜单
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```tsx
 | 
					<code src="./demos/demo2.tsx"/>
 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * title: 竖向菜单
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
import React from 'react';
 | 
					 | 
				
			||||||
import { SchemaRenderer } from '../';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const schema = {
 | 
					 | 
				
			||||||
  type: 'object',
 | 
					 | 
				
			||||||
  properties: {
 | 
					 | 
				
			||||||
    menu1: {
 | 
					 | 
				
			||||||
      type: 'void',
 | 
					 | 
				
			||||||
      name: 'name1',
 | 
					 | 
				
			||||||
      'x-component': 'Menu',
 | 
					 | 
				
			||||||
      // 'x-decorator': 'Menu.Designable',
 | 
					 | 
				
			||||||
      'x-component-props': {
 | 
					 | 
				
			||||||
        mode: 'inline',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      properties: {
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        item1: {
 | 
					 | 
				
			||||||
          type: 'void',
 | 
					 | 
				
			||||||
          title: `菜单1`,
 | 
					 | 
				
			||||||
          'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        item2: {
 | 
					 | 
				
			||||||
          type: 'void',
 | 
					 | 
				
			||||||
          title: `菜单2`,
 | 
					 | 
				
			||||||
          'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        item3: {
 | 
					 | 
				
			||||||
          type: 'void',
 | 
					 | 
				
			||||||
          title: '菜单组3',
 | 
					 | 
				
			||||||
          'x-component': 'Menu.SubMenu',
 | 
					 | 
				
			||||||
          properties: {
 | 
					 | 
				
			||||||
            item4: {
 | 
					 | 
				
			||||||
              type: 'void',
 | 
					 | 
				
			||||||
              title: `子菜单4`,
 | 
					 | 
				
			||||||
              'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            item5: {
 | 
					 | 
				
			||||||
              type: 'void',
 | 
					 | 
				
			||||||
              title: `子菜单5`,
 | 
					 | 
				
			||||||
              'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        item4: {
 | 
					 | 
				
			||||||
          type: 'void',
 | 
					 | 
				
			||||||
          title: '菜单组4',
 | 
					 | 
				
			||||||
          'x-component': 'Menu.SubMenu',
 | 
					 | 
				
			||||||
          properties: {
 | 
					 | 
				
			||||||
            item6: {
 | 
					 | 
				
			||||||
              type: 'void',
 | 
					 | 
				
			||||||
              title: `子菜单6`,
 | 
					 | 
				
			||||||
              'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            item7: {
 | 
					 | 
				
			||||||
              type: 'void',
 | 
					 | 
				
			||||||
              title: `子菜单7`,
 | 
					 | 
				
			||||||
              'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default () => {
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <div style={{width: 200}}><SchemaRenderer schema={schema} /></div>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
### 混合菜单
 | 
					### 混合菜单
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```tsx
 | 
					<code src="./demos/demo4.tsx"/>
 | 
				
			||||||
import React, { useRef, useState } from 'react';
 | 
					 | 
				
			||||||
import { SchemaRenderer } from '../';
 | 
					 | 
				
			||||||
import { MenuContainerContext } from './';
 | 
					 | 
				
			||||||
import { Layout } from 'antd';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default () => {
 | 
					 | 
				
			||||||
  const sideMenuRef = useRef();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const [activeKey, setActiveKey] = useState('item3');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const onSelect = (info) => {
 | 
					 | 
				
			||||||
    setActiveKey(info.key);
 | 
					 | 
				
			||||||
    console.log({ info })
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const schema = {
 | 
					 | 
				
			||||||
    type: 'object',
 | 
					 | 
				
			||||||
    properties: {
 | 
					 | 
				
			||||||
      menu1: {
 | 
					 | 
				
			||||||
        type: 'void',
 | 
					 | 
				
			||||||
        'x-component': 'Menu',
 | 
					 | 
				
			||||||
        'x-component-props': {
 | 
					 | 
				
			||||||
          sideMenuRef: '{{ sideMenuRef }}',
 | 
					 | 
				
			||||||
          defaultSelectedKeys: [activeKey],
 | 
					 | 
				
			||||||
          mode: 'mix',
 | 
					 | 
				
			||||||
          theme: 'dark',
 | 
					 | 
				
			||||||
          onSelect: '{{ onSelect }}',
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        properties: {
 | 
					 | 
				
			||||||
          item1: {
 | 
					 | 
				
			||||||
            type: 'void',
 | 
					 | 
				
			||||||
            title: `菜单1`,
 | 
					 | 
				
			||||||
            'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          item2: {
 | 
					 | 
				
			||||||
            type: 'void',
 | 
					 | 
				
			||||||
            title: `菜单2`,
 | 
					 | 
				
			||||||
            'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          item3: {
 | 
					 | 
				
			||||||
            type: 'void',
 | 
					 | 
				
			||||||
            title: '菜单组3',
 | 
					 | 
				
			||||||
            'x-component': 'Menu.SubMenu',
 | 
					 | 
				
			||||||
            properties: {
 | 
					 | 
				
			||||||
              item4: {
 | 
					 | 
				
			||||||
                type: 'void',
 | 
					 | 
				
			||||||
                title: `子菜单4`,
 | 
					 | 
				
			||||||
                'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
              },
 | 
					 | 
				
			||||||
              item5: {
 | 
					 | 
				
			||||||
                type: 'void',
 | 
					 | 
				
			||||||
                title: `子菜单5`,
 | 
					 | 
				
			||||||
                'x-component': 'Menu.SubMenu',
 | 
					 | 
				
			||||||
                properties: {
 | 
					 | 
				
			||||||
                  item8: {
 | 
					 | 
				
			||||||
                    type: 'void',
 | 
					 | 
				
			||||||
                    title: `子菜单8`,
 | 
					 | 
				
			||||||
                    'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
                  },
 | 
					 | 
				
			||||||
                  item9: {
 | 
					 | 
				
			||||||
                    type: 'void',
 | 
					 | 
				
			||||||
                    title: `子菜单9`,
 | 
					 | 
				
			||||||
                    'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
                  },
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
              },
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          item4: {
 | 
					 | 
				
			||||||
            type: 'void',
 | 
					 | 
				
			||||||
            title: '菜单组4',
 | 
					 | 
				
			||||||
            'x-component': 'Menu.SubMenu',
 | 
					 | 
				
			||||||
            properties: {
 | 
					 | 
				
			||||||
              item6: {
 | 
					 | 
				
			||||||
                type: 'void',
 | 
					 | 
				
			||||||
                title: `子菜单6`,
 | 
					 | 
				
			||||||
                'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
              },
 | 
					 | 
				
			||||||
              item7: {
 | 
					 | 
				
			||||||
                type: 'void',
 | 
					 | 
				
			||||||
                title: `子菜单7`,
 | 
					 | 
				
			||||||
                'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
              },
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <div>
 | 
					 | 
				
			||||||
      <Layout>
 | 
					 | 
				
			||||||
        <Layout.Header>
 | 
					 | 
				
			||||||
          <SchemaRenderer 
 | 
					 | 
				
			||||||
            schema={schema}
 | 
					 | 
				
			||||||
            scope={{ onSelect, sideMenuRef }}
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
        </Layout.Header>
 | 
					 | 
				
			||||||
        <Layout>
 | 
					 | 
				
			||||||
          <Layout.Sider ref={sideMenuRef} theme={'light'} width={200}>
 | 
					 | 
				
			||||||
          </Layout.Sider>
 | 
					 | 
				
			||||||
          <Layout.Content>
 | 
					 | 
				
			||||||
            {activeKey}
 | 
					 | 
				
			||||||
          </Layout.Content>
 | 
					 | 
				
			||||||
        </Layout>
 | 
					 | 
				
			||||||
      </Layout>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### 设计器模式
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```tsx
 | 
					 | 
				
			||||||
import React, { useRef, useState } from 'react';
 | 
					 | 
				
			||||||
import { SchemaRenderer } from '../';
 | 
					 | 
				
			||||||
import { MenuContainerContext } from './';
 | 
					 | 
				
			||||||
import { Layout } from 'antd';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default () => {
 | 
					 | 
				
			||||||
  const sideMenuRef = useRef();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const [activeKey, setActiveKey] = useState('item3');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const onSelect = (info) => {
 | 
					 | 
				
			||||||
    setActiveKey(info.key);
 | 
					 | 
				
			||||||
    console.log({ info })
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const schema = {
 | 
					 | 
				
			||||||
    type: 'object',
 | 
					 | 
				
			||||||
    properties: {
 | 
					 | 
				
			||||||
      menu1: {
 | 
					 | 
				
			||||||
        type: 'void',
 | 
					 | 
				
			||||||
        'x-component': 'Menu',
 | 
					 | 
				
			||||||
        'x-designable-bar': 'Menu.DesignableBar',
 | 
					 | 
				
			||||||
        'x-component-props': {
 | 
					 | 
				
			||||||
          defaultSelectedKeys: [activeKey],
 | 
					 | 
				
			||||||
          mode: 'mix',
 | 
					 | 
				
			||||||
          theme: 'dark',
 | 
					 | 
				
			||||||
          sideMenuRef: '{{ sideMenuRef }}',
 | 
					 | 
				
			||||||
          onSelect: '{{ onSelect }}',
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        properties: {
 | 
					 | 
				
			||||||
          item1: {
 | 
					 | 
				
			||||||
            type: 'void',
 | 
					 | 
				
			||||||
            title: `菜单1`,
 | 
					 | 
				
			||||||
            'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          item2: {
 | 
					 | 
				
			||||||
            type: 'void',
 | 
					 | 
				
			||||||
            title: `菜单2`,
 | 
					 | 
				
			||||||
            'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          item3: {
 | 
					 | 
				
			||||||
            type: 'void',
 | 
					 | 
				
			||||||
            title: '菜单组3',
 | 
					 | 
				
			||||||
            'x-component': 'Menu.SubMenu',
 | 
					 | 
				
			||||||
            properties: {
 | 
					 | 
				
			||||||
              item34: {
 | 
					 | 
				
			||||||
                type: 'void',
 | 
					 | 
				
			||||||
                title: `子菜单4`,
 | 
					 | 
				
			||||||
                'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
              },
 | 
					 | 
				
			||||||
              item5: {
 | 
					 | 
				
			||||||
                type: 'void',
 | 
					 | 
				
			||||||
                title: `子菜单5`,
 | 
					 | 
				
			||||||
                'x-component': 'Menu.SubMenu',
 | 
					 | 
				
			||||||
                properties: {
 | 
					 | 
				
			||||||
                  item8: {
 | 
					 | 
				
			||||||
                    type: 'void',
 | 
					 | 
				
			||||||
                    title: `子菜单8`,
 | 
					 | 
				
			||||||
                    'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
                  },
 | 
					 | 
				
			||||||
                  item9: {
 | 
					 | 
				
			||||||
                    type: 'void',
 | 
					 | 
				
			||||||
                    title: `子菜单9`,
 | 
					 | 
				
			||||||
                    'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
                  },
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
              },
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          item4: {
 | 
					 | 
				
			||||||
            type: 'void',
 | 
					 | 
				
			||||||
            title: '菜单组4',
 | 
					 | 
				
			||||||
            'x-component': 'Menu.SubMenu',
 | 
					 | 
				
			||||||
            properties: {
 | 
					 | 
				
			||||||
              item6: {
 | 
					 | 
				
			||||||
                type: 'void',
 | 
					 | 
				
			||||||
                title: `子菜单6`,
 | 
					 | 
				
			||||||
                'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
              },
 | 
					 | 
				
			||||||
              item7: {
 | 
					 | 
				
			||||||
                type: 'void',
 | 
					 | 
				
			||||||
                title: `子菜单7`,
 | 
					 | 
				
			||||||
                'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
              },
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <div>
 | 
					 | 
				
			||||||
      <Layout>
 | 
					 | 
				
			||||||
        <Layout.Header>
 | 
					 | 
				
			||||||
          <SchemaRenderer
 | 
					 | 
				
			||||||
            debug={true}
 | 
					 | 
				
			||||||
            schema={schema}
 | 
					 | 
				
			||||||
            scope={{ onSelect, sideMenuRef }}
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
        </Layout.Header>
 | 
					 | 
				
			||||||
        <Layout>
 | 
					 | 
				
			||||||
          <Layout.Sider 
 | 
					 | 
				
			||||||
            ref={sideMenuRef}
 | 
					 | 
				
			||||||
            theme={'light'}
 | 
					 | 
				
			||||||
            width={200}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
          </Layout.Sider>
 | 
					 | 
				
			||||||
          <Layout.Content>
 | 
					 | 
				
			||||||
            {activeKey}
 | 
					 | 
				
			||||||
          </Layout.Content>
 | 
					 | 
				
			||||||
        </Layout>
 | 
					 | 
				
			||||||
      </Layout>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### 设计器模式 - 菜单项为空时
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```tsx
 | 
					 | 
				
			||||||
import React, { useRef, useState } from 'react';
 | 
					 | 
				
			||||||
import { SchemaRenderer } from '../';
 | 
					 | 
				
			||||||
import { MenuContainerContext } from './';
 | 
					 | 
				
			||||||
import { Layout } from 'antd';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default () => {
 | 
					 | 
				
			||||||
  const sideMenuRef = useRef();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const [activeKey, setActiveKey] = useState('item3');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const onSelect = (info) => {
 | 
					 | 
				
			||||||
    setActiveKey(info.key);
 | 
					 | 
				
			||||||
    console.log({ info })
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const schema = {
 | 
					 | 
				
			||||||
    type: 'object',
 | 
					 | 
				
			||||||
    properties: {
 | 
					 | 
				
			||||||
      menu1: {
 | 
					 | 
				
			||||||
        type: 'void',
 | 
					 | 
				
			||||||
        'x-component': 'Menu',
 | 
					 | 
				
			||||||
        'x-designable-bar': 'Menu.DesignableBar',
 | 
					 | 
				
			||||||
        'x-component-props': {
 | 
					 | 
				
			||||||
          defaultSelectedKeys: [activeKey],
 | 
					 | 
				
			||||||
          mode: 'mix',
 | 
					 | 
				
			||||||
          theme: 'dark',
 | 
					 | 
				
			||||||
          sideMenuRef: '{{ sideMenuRef }}',
 | 
					 | 
				
			||||||
          onSelect: '{{ onSelect }}',
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <div>
 | 
					 | 
				
			||||||
      <Layout>
 | 
					 | 
				
			||||||
        <Layout.Header>
 | 
					 | 
				
			||||||
          <SchemaRenderer
 | 
					 | 
				
			||||||
            schema={schema}
 | 
					 | 
				
			||||||
            scope={{ onSelect, sideMenuRef }}
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
        </Layout.Header>
 | 
					 | 
				
			||||||
        <Layout>
 | 
					 | 
				
			||||||
          <Layout.Sider 
 | 
					 | 
				
			||||||
            ref={sideMenuRef}
 | 
					 | 
				
			||||||
            theme={'light'}
 | 
					 | 
				
			||||||
            width={200}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
          </Layout.Sider>
 | 
					 | 
				
			||||||
          <Layout.Content>
 | 
					 | 
				
			||||||
            {activeKey}
 | 
					 | 
				
			||||||
          </Layout.Content>
 | 
					 | 
				
			||||||
        </Layout>
 | 
					 | 
				
			||||||
      </Layout>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Menu.Action
 | 
					### Menu.Action
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<code src="./demos/demo3.tsx"/>
 | 
				
			||||||
```tsx
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * title: 横向菜单
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
import React from 'react';
 | 
					 | 
				
			||||||
import { SchemaRenderer } from '../';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const schema = {
 | 
					 | 
				
			||||||
  type: 'object',
 | 
					 | 
				
			||||||
  properties: {
 | 
					 | 
				
			||||||
    menu1: {
 | 
					 | 
				
			||||||
      type: 'void',
 | 
					 | 
				
			||||||
      'x-component': 'Menu',
 | 
					 | 
				
			||||||
      'x-designable-bar': 'Menu.DesignableBar',
 | 
					 | 
				
			||||||
      'x-component-props': {
 | 
					 | 
				
			||||||
        mode: 'horizontal',
 | 
					 | 
				
			||||||
        theme: 'dark',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      properties: {
 | 
					 | 
				
			||||||
        item1: {
 | 
					 | 
				
			||||||
          type: 'void',
 | 
					 | 
				
			||||||
          title: `菜单1`,
 | 
					 | 
				
			||||||
          'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        item2: {
 | 
					 | 
				
			||||||
          type: 'void',
 | 
					 | 
				
			||||||
          title: `菜单1`,
 | 
					 | 
				
			||||||
          'x-component': 'Menu.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',
 | 
					 | 
				
			||||||
                        },
 | 
					 | 
				
			||||||
                      },
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                  },
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
              },
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default () => {
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <SchemaRenderer schema={schema} />
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@ import React, {
 | 
				
			|||||||
  useEffect,
 | 
					  useEffect,
 | 
				
			||||||
  useRef,
 | 
					  useRef,
 | 
				
			||||||
  useState,
 | 
					  useState,
 | 
				
			||||||
 | 
					  forwardRef,
 | 
				
			||||||
} from 'react';
 | 
					} from 'react';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  connect,
 | 
					  connect,
 | 
				
			||||||
@ -29,311 +30,94 @@ import {
 | 
				
			|||||||
  Modal,
 | 
					  Modal,
 | 
				
			||||||
  Button,
 | 
					  Button,
 | 
				
			||||||
} from 'antd';
 | 
					} from 'antd';
 | 
				
			||||||
import get from 'lodash/get';
 | 
					 | 
				
			||||||
import { uid } from '@formily/shared';
 | 
					import { uid } from '@formily/shared';
 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  MenuOutlined,
 | 
					 | 
				
			||||||
  GroupOutlined,
 | 
					 | 
				
			||||||
  PlusOutlined,
 | 
					 | 
				
			||||||
  LinkOutlined,
 | 
					 | 
				
			||||||
  AppstoreAddOutlined,
 | 
					 | 
				
			||||||
  EditOutlined,
 | 
					 | 
				
			||||||
  DeleteOutlined,
 | 
					 | 
				
			||||||
  ArrowRightOutlined,
 | 
					 | 
				
			||||||
  SettingOutlined,
 | 
					 | 
				
			||||||
  ArrowUpOutlined,
 | 
					 | 
				
			||||||
  ArrowDownOutlined,
 | 
					 | 
				
			||||||
} from '@ant-design/icons';
 | 
					 | 
				
			||||||
import './style.less';
 | 
					 | 
				
			||||||
import { Icon } from '../icon-picker';
 | 
					 | 
				
			||||||
import { useHistory } from 'react-router-dom';
 | 
					 | 
				
			||||||
import cls from 'classnames';
 | 
					import cls from 'classnames';
 | 
				
			||||||
import ReactDOM from 'react-dom';
 | 
					import { useDesignable } from '../../components/schema-renderer';
 | 
				
			||||||
 | 
					import { MenuOutlined } from '@ant-design/icons';
 | 
				
			||||||
 | 
					import { IconPicker } from '../../components/icon-picker';
 | 
				
			||||||
 | 
					import { VisibleContext } from '..';
 | 
				
			||||||
import { useMount } from 'ahooks';
 | 
					import { useMount } from 'ahooks';
 | 
				
			||||||
import { useDesignable, SchemaRenderer } from '..';
 | 
					import './style.less';
 | 
				
			||||||
import { Router } from 'react-router';
 | 
					 | 
				
			||||||
import { Action, useDefaultAction } from '../action';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type MenuType = React.FC<MenuProps & { hideSubMenu?: boolean }> & {
 | 
					export const MenuModeContext = createContext(null);
 | 
				
			||||||
  Item?: React.FC<MenuItemProps>;
 | 
					
 | 
				
			||||||
  SubMenu?: React.FC<SubMenuProps>;
 | 
					const SideMenu = (props: any) => {
 | 
				
			||||||
  Divider?: React.FC<DividerProps>;
 | 
					  const { visible, selectedKey, onSelect } = props;
 | 
				
			||||||
  DesignableBar?: React.FC<any>;
 | 
					  const { schema } = useDesignable();
 | 
				
			||||||
  AddNew?: React.FC<any>;
 | 
					  if (!selectedKey || !visible) {
 | 
				
			||||||
  Link?: React.FC<MenuItemProps>;
 | 
					    return null;
 | 
				
			||||||
  Action?: React.FC<MenuItemProps>;
 | 
					  }
 | 
				
			||||||
  Url?: React.FC<MenuItemProps & { url: string }>;
 | 
					  const child = schema.properties && schema.properties[selectedKey];
 | 
				
			||||||
 | 
					  if (!child || child['x-component'] !== 'Menu.SubMenu') {
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <AntdMenu mode={'inline'} onSelect={onSelect}>
 | 
				
			||||||
 | 
					      <RecursionField schema={child} onlyRenderProperties />
 | 
				
			||||||
 | 
					      <AntdMenu.Item key={uid()}>{selectedKey}</AntdMenu.Item>
 | 
				
			||||||
 | 
					    </AntdMenu>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const MenuContext = createContext({
 | 
					export const Menu: any = observer((props: any) => {
 | 
				
			||||||
  mode: null,
 | 
					  const { mode, onSelect, sideMenuRef, ...others } = props;
 | 
				
			||||||
  designableBar: null,
 | 
					  const { schema } = useDesignable();
 | 
				
			||||||
});
 | 
					  const [selectedKey, setSelectedKey] = useState(null);
 | 
				
			||||||
 | 
					  const ref = useRef();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function useDesignableBar() {
 | 
					 | 
				
			||||||
  const { designableBar } = useContext(MenuContext);
 | 
					 | 
				
			||||||
  const options = useContext(SchemaOptionsContext);
 | 
					 | 
				
			||||||
  const DesignableBar = designableBar
 | 
					 | 
				
			||||||
    ? get(options.components, designableBar)
 | 
					 | 
				
			||||||
    : null;
 | 
					 | 
				
			||||||
  return DesignableBar || (() => null);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const Menu: MenuType = observer((props: any) => {
 | 
					 | 
				
			||||||
  const {
 | 
					 | 
				
			||||||
    children,
 | 
					 | 
				
			||||||
    sideMenuRef,
 | 
					 | 
				
			||||||
    onSelect,
 | 
					 | 
				
			||||||
    mode,
 | 
					 | 
				
			||||||
    defaultSelectedKeys,
 | 
					 | 
				
			||||||
    ...others
 | 
					 | 
				
			||||||
  } = props;
 | 
					 | 
				
			||||||
  let defaultSelectedKey = defaultSelectedKeys ? defaultSelectedKeys[0] : null;
 | 
					 | 
				
			||||||
  const { schema, schema: designableSchema, refresh } = useDesignable();
 | 
					 | 
				
			||||||
  const designableBar = schema['x-designable-bar'];
 | 
					 | 
				
			||||||
  const history = useHistory();
 | 
					 | 
				
			||||||
  const renderSideMenu = (selectedKey) => {
 | 
					 | 
				
			||||||
    if ((mode as any) !== 'mix') {
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (!sideMenuRef || !sideMenuRef.current) {
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (!selectedKey || !schema.properties) {
 | 
					 | 
				
			||||||
      sideMenuRef.current.style.display = 'none';
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const subSchema = schema.properties[selectedKey];
 | 
					 | 
				
			||||||
    if (!subSchema) {
 | 
					 | 
				
			||||||
      sideMenuRef.current.style.display = 'none';
 | 
					 | 
				
			||||||
      ReactDOM.render(null, sideMenuRef.current);
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (subSchema['x-component'] !== 'Menu.SubMenu') {
 | 
					 | 
				
			||||||
      sideMenuRef.current.style.display = 'none';
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const properties = subSchema.properties || {};
 | 
					 | 
				
			||||||
    sideMenuRef.current.style.display = 'block';
 | 
					 | 
				
			||||||
    const newProps = {};
 | 
					 | 
				
			||||||
    Object.keys(properties || {}).forEach((name) => {
 | 
					 | 
				
			||||||
      newProps[name] = properties[name].toJSON();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    ReactDOM.render(
 | 
					 | 
				
			||||||
      <Router history={history}>
 | 
					 | 
				
			||||||
        <SchemaRenderer
 | 
					 | 
				
			||||||
          key={`${Math.random()}`}
 | 
					 | 
				
			||||||
          onRefresh={(subSchema: Schema) => {
 | 
					 | 
				
			||||||
            const selected = designableSchema.properties[selectedKey];
 | 
					 | 
				
			||||||
            const diff = subSchema.properties[`${schema.name}.${selectedKey}`];
 | 
					 | 
				
			||||||
            Object.keys(selected.properties || {}).forEach((name) => {
 | 
					 | 
				
			||||||
              selected.properties[name].parent.removeProperty(name);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            Object.keys(diff.properties).forEach((name) => {
 | 
					 | 
				
			||||||
              if (name.endsWith('-add-new')) {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
              console.log('diff', name);
 | 
					 | 
				
			||||||
              const current = diff.properties[name];
 | 
					 | 
				
			||||||
              selected.addProperty(current.name, current.toJSON());
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            console.log({ selected });
 | 
					 | 
				
			||||||
            refresh();
 | 
					 | 
				
			||||||
          }}
 | 
					 | 
				
			||||||
          schema={{
 | 
					 | 
				
			||||||
            type: 'void',
 | 
					 | 
				
			||||||
            name: `${schema.name}.${selectedKey}`,
 | 
					 | 
				
			||||||
            'x-component': 'Menu',
 | 
					 | 
				
			||||||
            'x-designable-bar': designableBar,
 | 
					 | 
				
			||||||
            'x-component-props': {
 | 
					 | 
				
			||||||
              mode: 'inline',
 | 
					 | 
				
			||||||
              onSelect,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            properties: {
 | 
					 | 
				
			||||||
              ...newProps,
 | 
					 | 
				
			||||||
              [`${uid()}-add-new`]: {
 | 
					 | 
				
			||||||
                type: 'void',
 | 
					 | 
				
			||||||
                parentKey: subSchema['key'],
 | 
					 | 
				
			||||||
                'x-component': 'Menu.AddNew',
 | 
					 | 
				
			||||||
              },
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          }}
 | 
					 | 
				
			||||||
        />
 | 
					 | 
				
			||||||
      </Router>,
 | 
					 | 
				
			||||||
      sideMenuRef.current,
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  useMount(() => {
 | 
					  useMount(() => {
 | 
				
			||||||
    console.log({ defaultSelectedKey }, schema.properties);
 | 
					    if (mode !== 'mix') {
 | 
				
			||||||
    renderSideMenu(defaultSelectedKey);
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const sideMenuElement = sideMenuRef && (sideMenuRef.current as HTMLElement);
 | 
				
			||||||
 | 
					    if (sideMenuElement && ref.current) {
 | 
				
			||||||
 | 
					      sideMenuElement.querySelector(':scope > div').appendChild(ref.current);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  const isEmpty = !Object.keys(designableSchema.properties || {}).length;
 | 
					
 | 
				
			||||||
  console.log({ designableSchema });
 | 
					 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <MenuContext.Provider
 | 
					    <MenuModeContext.Provider value={mode}>
 | 
				
			||||||
      value={{
 | 
					 | 
				
			||||||
        mode,
 | 
					 | 
				
			||||||
        designableBar,
 | 
					 | 
				
			||||||
      }}
 | 
					 | 
				
			||||||
    >
 | 
					 | 
				
			||||||
      <AntdMenu
 | 
					      <AntdMenu
 | 
				
			||||||
        defaultSelectedKeys={defaultSelectedKeys}
 | 
					 | 
				
			||||||
        {...others}
 | 
					        {...others}
 | 
				
			||||||
 | 
					        mode={mode === 'mix' ? 'horizontal' : mode}
 | 
				
			||||||
        onSelect={(info) => {
 | 
					        onSelect={(info) => {
 | 
				
			||||||
          console.log('info.key', info.key);
 | 
					          if (mode === 'mix') {
 | 
				
			||||||
          renderSideMenu(info.key);
 | 
					            setSelectedKey(info.key);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
          onSelect && onSelect(info);
 | 
					          onSelect && onSelect(info);
 | 
				
			||||||
        }}
 | 
					        }}
 | 
				
			||||||
        mode={(mode as any) === 'mix' ? 'horizontal' : mode}
 | 
					 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
        {!isEmpty ? (
 | 
					 | 
				
			||||||
        <RecursionField schema={schema} onlyRenderProperties />
 | 
					        <RecursionField schema={schema} onlyRenderProperties />
 | 
				
			||||||
        ) : (
 | 
					        <AntdMenu.ItemGroup title={
 | 
				
			||||||
          <SchemaRenderer
 | 
					          <div>新增</div>
 | 
				
			||||||
            onRefresh={(subSchema: Schema) => {
 | 
					        }></AntdMenu.ItemGroup>
 | 
				
			||||||
              Object.keys(subSchema.properties).forEach((name) => {
 | 
					      </AntdMenu>
 | 
				
			||||||
                if (name === 'add-new') {
 | 
					      {mode === 'mix' && (
 | 
				
			||||||
                  return;
 | 
					        <div ref={ref}>
 | 
				
			||||||
                }
 | 
					          <SideMenu
 | 
				
			||||||
                designableSchema.addProperty(
 | 
					            onSelect={onSelect}
 | 
				
			||||||
                  name,
 | 
					            visible={mode === 'mix'}
 | 
				
			||||||
                  subSchema.properties[name].toJSON(),
 | 
					            selectedKey={selectedKey}
 | 
				
			||||||
                );
 | 
					            sideMenuRef={sideMenuRef}
 | 
				
			||||||
              });
 | 
					 | 
				
			||||||
              refresh();
 | 
					 | 
				
			||||||
            }}
 | 
					 | 
				
			||||||
            schema={{
 | 
					 | 
				
			||||||
              type: 'object',
 | 
					 | 
				
			||||||
              properties: {
 | 
					 | 
				
			||||||
                'add-new': {
 | 
					 | 
				
			||||||
                  parentKey: schema['key'],
 | 
					 | 
				
			||||||
                  type: 'void',
 | 
					 | 
				
			||||||
                  'x-component': 'Menu.AddNew',
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
              },
 | 
					 | 
				
			||||||
            }}
 | 
					 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
      )}
 | 
					      )}
 | 
				
			||||||
      </AntdMenu>
 | 
					    </MenuModeContext.Provider>
 | 
				
			||||||
    </MenuContext.Provider>
 | 
					 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const AddNewAction = () => {
 | 
					Menu.Divider = observer(AntdMenu.Divider);
 | 
				
			||||||
  const { schema, insertBefore } = useDesignable();
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <Dropdown
 | 
					 | 
				
			||||||
      overlayStyle={{
 | 
					 | 
				
			||||||
        minWidth: 150,
 | 
					 | 
				
			||||||
      }}
 | 
					 | 
				
			||||||
      trigger={['click']}
 | 
					 | 
				
			||||||
      overlay={
 | 
					 | 
				
			||||||
        <AntdMenu>
 | 
					 | 
				
			||||||
          <AntdMenu.Item
 | 
					 | 
				
			||||||
            onClick={() => {
 | 
					 | 
				
			||||||
              const s = insertBefore({
 | 
					 | 
				
			||||||
                type: 'void',
 | 
					 | 
				
			||||||
                title: uid(),
 | 
					 | 
				
			||||||
                key: uid(),
 | 
					 | 
				
			||||||
                'x-component': 'Menu.Item',
 | 
					 | 
				
			||||||
              });
 | 
					 | 
				
			||||||
              console.log('s.s.s.s.s.s', s);
 | 
					 | 
				
			||||||
            }}
 | 
					 | 
				
			||||||
            style={{ minWidth: 150 }}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            <MenuOutlined /> 新建菜单
 | 
					 | 
				
			||||||
          </AntdMenu.Item>
 | 
					 | 
				
			||||||
          <AntdMenu.Item
 | 
					 | 
				
			||||||
            onClick={() => {
 | 
					 | 
				
			||||||
              const s = insertBefore({
 | 
					 | 
				
			||||||
                type: 'void',
 | 
					 | 
				
			||||||
                key: uid(),
 | 
					 | 
				
			||||||
                title: uid(),
 | 
					 | 
				
			||||||
                'x-component': 'Menu.SubMenu',
 | 
					 | 
				
			||||||
              });
 | 
					 | 
				
			||||||
            }}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            <GroupOutlined /> 新建分组
 | 
					 | 
				
			||||||
          </AntdMenu.Item>
 | 
					 | 
				
			||||||
          <AntdMenu.Item>
 | 
					 | 
				
			||||||
            <LinkOutlined /> 添加链接
 | 
					 | 
				
			||||||
          </AntdMenu.Item>
 | 
					 | 
				
			||||||
        </AntdMenu>
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    >
 | 
					 | 
				
			||||||
      <Button block type={'dashed'} icon={<PlusOutlined />}></Button>
 | 
					 | 
				
			||||||
    </Dropdown>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Menu.AddNew = observer((props) => {
 | 
					 | 
				
			||||||
  const { designableBar } = useContext(MenuContext);
 | 
					 | 
				
			||||||
  return designableBar ? (
 | 
					 | 
				
			||||||
    <AntdMenu.ItemGroup
 | 
					 | 
				
			||||||
      key={'add-menu-item'}
 | 
					 | 
				
			||||||
      className={'menu-add'}
 | 
					 | 
				
			||||||
      title={<AddNewAction />}
 | 
					 | 
				
			||||||
    ></AntdMenu.ItemGroup>
 | 
					 | 
				
			||||||
  ) : null;
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Menu.Url = observer((props) => {
 | 
					 | 
				
			||||||
  const DesignableBar = useDesignableBar();
 | 
					 | 
				
			||||||
  const { schema } = useDesignable();
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <AntdMenu.Item
 | 
					 | 
				
			||||||
      {...props}
 | 
					 | 
				
			||||||
      // @ts-ignore
 | 
					 | 
				
			||||||
      eventKey={schema.name}
 | 
					 | 
				
			||||||
      key={schema.name}
 | 
					 | 
				
			||||||
      onClick={(e) => {
 | 
					 | 
				
			||||||
        window.open(props.url);
 | 
					 | 
				
			||||||
      }}
 | 
					 | 
				
			||||||
      icon={props.icon ? <Icon type={props.icon as string} /> : undefined}
 | 
					 | 
				
			||||||
    >
 | 
					 | 
				
			||||||
      {schema.title}
 | 
					 | 
				
			||||||
      <DesignableBar />
 | 
					 | 
				
			||||||
    </AntdMenu.Item>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Menu.Link = observer((props) => {
 | 
					 | 
				
			||||||
  const history = useHistory();
 | 
					 | 
				
			||||||
  const DesignableBar = useDesignableBar();
 | 
					 | 
				
			||||||
  const { schema } = useDesignable();
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <AntdMenu.Item
 | 
					 | 
				
			||||||
      {...props}
 | 
					 | 
				
			||||||
      // @ts-ignore
 | 
					 | 
				
			||||||
      eventKey={schema.name}
 | 
					 | 
				
			||||||
      key={schema.name}
 | 
					 | 
				
			||||||
      onClick={(e) => {
 | 
					 | 
				
			||||||
        history.push(schema.name as string);
 | 
					 | 
				
			||||||
      }}
 | 
					 | 
				
			||||||
      icon={props.icon ? <Icon type={props.icon as string} /> : undefined}
 | 
					 | 
				
			||||||
    >
 | 
					 | 
				
			||||||
      {schema.title}
 | 
					 | 
				
			||||||
      <DesignableBar />
 | 
					 | 
				
			||||||
    </AntdMenu.Item>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Menu.Item = observer((props: any) => {
 | 
					Menu.Item = observer((props: any) => {
 | 
				
			||||||
  const { useAction = useDefaultAction, ...others } = props;
 | 
					  const { icon } = props;
 | 
				
			||||||
  const { run } = useAction();
 | 
					  const { schema, DesignableBar } = useDesignable();
 | 
				
			||||||
  const DesignableBar = useDesignableBar();
 | 
					 | 
				
			||||||
  const { schema } = useDesignable();
 | 
					 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <AntdMenu.Item
 | 
					    <AntdMenu.Item
 | 
				
			||||||
      {...others}
 | 
					      {...props}
 | 
				
			||||||
      onClick={async () => {
 | 
					      icon={<IconPicker type={icon} />}
 | 
				
			||||||
        await run();
 | 
					 | 
				
			||||||
      }}
 | 
					 | 
				
			||||||
      schema={schema}
 | 
					 | 
				
			||||||
      // @ts-ignore
 | 
					 | 
				
			||||||
      eventKey={schema.name}
 | 
					      eventKey={schema.name}
 | 
				
			||||||
      key={schema.name}
 | 
					      key={schema.name}
 | 
				
			||||||
      icon={props.icon ? <Icon type={props.icon as string} /> : undefined}
 | 
					 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      {schema.title}
 | 
					      {schema.title}
 | 
				
			||||||
      <DesignableBar />
 | 
					      <DesignableBar />
 | 
				
			||||||
@ -342,32 +126,39 @@ Menu.Item = observer((props: any) => {
 | 
				
			|||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Menu.Action = observer((props: any) => {
 | 
					Menu.Action = observer((props: any) => {
 | 
				
			||||||
  const { icon, ...others } = props;
 | 
					  const { icon } = props;
 | 
				
			||||||
  // const DesignableBar = useDesignableBar();
 | 
					  const { schema, DesignableBar } = useDesignable();
 | 
				
			||||||
  const { schema } = useDesignable();
 | 
					  const [visible, setVisible] = useState(false);
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Action
 | 
					    <VisibleContext.Provider value={[visible, setVisible]}>
 | 
				
			||||||
      // @ts-ignore
 | 
					      <AntdMenu.Item
 | 
				
			||||||
      eventKey={schema.name}
 | 
					        {...props}
 | 
				
			||||||
        key={schema.name}
 | 
					        key={schema.name}
 | 
				
			||||||
      icon={icon ? <Icon type={icon as string} /> : undefined}
 | 
					        eventKey={schema.name}
 | 
				
			||||||
      ButtonComponent={AntdMenu.Item}
 | 
					        icon={<IconPicker type={icon} />}
 | 
				
			||||||
      {...others}
 | 
					        onClick={() => {
 | 
				
			||||||
    />
 | 
					          setVisible(true);
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        {schema.title}
 | 
				
			||||||
 | 
					        <DesignableBar />
 | 
				
			||||||
 | 
					      </AntdMenu.Item>
 | 
				
			||||||
 | 
					      {props.children}
 | 
				
			||||||
 | 
					      {/* <RecursionField schema={schema} onlyRenderProperties /> */}
 | 
				
			||||||
 | 
					    </VisibleContext.Provider>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Menu.SubMenu = observer((props) => {
 | 
					Menu.SubMenu = observer((props: any) => {
 | 
				
			||||||
  const DesignableBar = useDesignableBar();
 | 
					  const { icon } = props;
 | 
				
			||||||
  const { schema } = useDesignable();
 | 
					  const { schema, DesignableBar } = useDesignable();
 | 
				
			||||||
  const { mode } = useContext(MenuContext);
 | 
					  const mode = useContext(MenuModeContext);
 | 
				
			||||||
  return mode === 'mix' ? (
 | 
					  return mode === 'mix' ? (
 | 
				
			||||||
    <Menu.Item {...props} />
 | 
					    <Menu.Item {...props} />
 | 
				
			||||||
  ) : (
 | 
					  ) : (
 | 
				
			||||||
    <AntdMenu.SubMenu
 | 
					    <AntdMenu.SubMenu
 | 
				
			||||||
      {...props}
 | 
					      {...props}
 | 
				
			||||||
      // @ts-ignore
 | 
					      icon={<IconPicker type={icon} />}
 | 
				
			||||||
      schema={schema}
 | 
					 | 
				
			||||||
      title={
 | 
					      title={
 | 
				
			||||||
        <>
 | 
					        <>
 | 
				
			||||||
          {schema.title} <DesignableBar />
 | 
					          {schema.title} <DesignableBar />
 | 
				
			||||||
@ -375,28 +166,12 @@ Menu.SubMenu = observer((props) => {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      eventKey={schema.name}
 | 
					      eventKey={schema.name}
 | 
				
			||||||
      key={schema.name}
 | 
					      key={schema.name}
 | 
				
			||||||
      icon={
 | 
					    >
 | 
				
			||||||
        props.icon ? (
 | 
					      <RecursionField schema={schema} onlyRenderProperties />
 | 
				
			||||||
          <RecursionField
 | 
					    </AntdMenu.SubMenu>
 | 
				
			||||||
            name={schema.name}
 | 
					 | 
				
			||||||
            schema={
 | 
					 | 
				
			||||||
              new Schema({
 | 
					 | 
				
			||||||
                type: 'string',
 | 
					 | 
				
			||||||
                'x-read-pretty': true,
 | 
					 | 
				
			||||||
                default: props.icon,
 | 
					 | 
				
			||||||
                'x-component': 'IconPicker',
 | 
					 | 
				
			||||||
              })
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            onlyRenderProperties
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
        ) : undefined
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    />
 | 
					 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Menu.Divider = observer(AntdMenu.Divider);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Menu.DesignableBar = (props) => {
 | 
					Menu.DesignableBar = (props) => {
 | 
				
			||||||
  const field = useField();
 | 
					  const field = useField();
 | 
				
			||||||
  const [visible, setVisible] = useState(false);
 | 
					  const [visible, setVisible] = useState(false);
 | 
				
			||||||
@ -481,10 +256,7 @@ Menu.DesignableBar = (props) => {
 | 
				
			|||||||
                  });
 | 
					                  });
 | 
				
			||||||
                }}
 | 
					                }}
 | 
				
			||||||
              >
 | 
					              >
 | 
				
			||||||
                <DeleteOutlined /> 删除菜单
 | 
					                删除菜单
 | 
				
			||||||
              </AntdMenu.Item>
 | 
					 | 
				
			||||||
              <AntdMenu.Item>
 | 
					 | 
				
			||||||
                <ModalButton />
 | 
					 | 
				
			||||||
              </AntdMenu.Item>
 | 
					              </AntdMenu.Item>
 | 
				
			||||||
            </AntdMenu>
 | 
					            </AntdMenu>
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
@ -496,27 +268,4 @@ Menu.DesignableBar = (props) => {
 | 
				
			|||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function ModalButton() {
 | 
					 | 
				
			||||||
  const [visible, setVisible] = useState(false);
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <>
 | 
					 | 
				
			||||||
      <div
 | 
					 | 
				
			||||||
        onClick={() => {
 | 
					 | 
				
			||||||
          setVisible(true);
 | 
					 | 
				
			||||||
        }}
 | 
					 | 
				
			||||||
      >
 | 
					 | 
				
			||||||
        按钮
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <Modal
 | 
					 | 
				
			||||||
        visible={visible}
 | 
					 | 
				
			||||||
        onCancel={() => {
 | 
					 | 
				
			||||||
          setVisible(false);
 | 
					 | 
				
			||||||
        }}
 | 
					 | 
				
			||||||
      >
 | 
					 | 
				
			||||||
        aaa
 | 
					 | 
				
			||||||
      </Modal>
 | 
					 | 
				
			||||||
    </>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Menu;
 | 
					export default Menu;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,9 @@
 | 
				
			|||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import { SchemaRenderer } from '../..';
 | 
					import { SchemaRenderer } from '../..';
 | 
				
			||||||
 | 
					import { Table } from '../';
 | 
				
			||||||
import { uid } from '@formily/shared';
 | 
					import { uid } from '@formily/shared';
 | 
				
			||||||
import Editor from '@monaco-editor/react';
 | 
					 | 
				
			||||||
import { createForm } from '@formily/core';
 | 
					import { createForm } from '@formily/core';
 | 
				
			||||||
import { observer } from '@formily/react';
 | 
					import { ISchema, observer } from '@formily/react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function useAction() {
 | 
					function useAction() {
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
@ -13,7 +13,7 @@ function useAction() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const schema = {
 | 
					const schema: ISchema = {
 | 
				
			||||||
  name: `table_${uid()}`,
 | 
					  name: `table_${uid()}`,
 | 
				
			||||||
  type: 'array',
 | 
					  type: 'array',
 | 
				
			||||||
  'x-component': 'Table',
 | 
					  'x-component': 'Table',
 | 
				
			||||||
@ -49,13 +49,9 @@ const schema = {
 | 
				
			|||||||
            popover1: {
 | 
					            popover1: {
 | 
				
			||||||
              type: 'void',
 | 
					              type: 'void',
 | 
				
			||||||
              title: '弹窗标题',
 | 
					              title: '弹窗标题',
 | 
				
			||||||
 | 
					              'x-decorator': 'Form',
 | 
				
			||||||
              'x-component': 'Action.Popover',
 | 
					              'x-component': 'Action.Popover',
 | 
				
			||||||
              'x-component-props': {},
 | 
					              'x-component-props': {},
 | 
				
			||||||
              properties: {
 | 
					 | 
				
			||||||
                [uid()]: {
 | 
					 | 
				
			||||||
                  name: 'form',
 | 
					 | 
				
			||||||
                  type: 'object',
 | 
					 | 
				
			||||||
                  'x-component': 'Form',
 | 
					 | 
				
			||||||
              properties: {
 | 
					              properties: {
 | 
				
			||||||
                filter: {
 | 
					                filter: {
 | 
				
			||||||
                  name: 'filter',
 | 
					                  name: 'filter',
 | 
				
			||||||
@ -98,17 +94,7 @@ const schema = {
 | 
				
			|||||||
                      },
 | 
					                      },
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                    },
 | 
					                }
 | 
				
			||||||
                    action: {
 | 
					 | 
				
			||||||
                      type: 'void',
 | 
					 | 
				
			||||||
                      title: '提交',
 | 
					 | 
				
			||||||
                      'x-component': 'Action',
 | 
					 | 
				
			||||||
                      'x-component-props': {
 | 
					 | 
				
			||||||
                        useAction: '{{ useTableFilterAction }}',
 | 
					 | 
				
			||||||
                      },
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                  },
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
@ -148,7 +134,7 @@ const schema = {
 | 
				
			|||||||
                      title: '提交',
 | 
					                      title: '提交',
 | 
				
			||||||
                      'x-component': 'Action',
 | 
					                      'x-component': 'Action',
 | 
				
			||||||
                      'x-component-props': {
 | 
					                      'x-component-props': {
 | 
				
			||||||
                        useAction: '{{ useTableCreateAction }}',
 | 
					                        useAction: '{{ Table.useTableCreateAction }}',
 | 
				
			||||||
                      },
 | 
					                      },
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                  },
 | 
					                  },
 | 
				
			||||||
@ -163,7 +149,7 @@ const schema = {
 | 
				
			|||||||
          title: '删除',
 | 
					          title: '删除',
 | 
				
			||||||
          'x-component': 'Action',
 | 
					          'x-component': 'Action',
 | 
				
			||||||
          'x-component-props': {
 | 
					          'x-component-props': {
 | 
				
			||||||
            useAction: '{{ useTableDestroyAction }}',
 | 
					            useAction: '{{ Table.useTableDestroyAction }}',
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
@ -340,7 +326,7 @@ const schema = {
 | 
				
			|||||||
                  type: 'void',
 | 
					                  type: 'void',
 | 
				
			||||||
                  'x-component': 'Form',
 | 
					                  'x-component': 'Form',
 | 
				
			||||||
                  'x-component-props': {
 | 
					                  'x-component-props': {
 | 
				
			||||||
                    useValues: '{{ useTableRow }}',
 | 
					                    useValues: '{{ Table.useTableRow }}',
 | 
				
			||||||
                  },
 | 
					                  },
 | 
				
			||||||
                  properties: {
 | 
					                  properties: {
 | 
				
			||||||
                    field1: {
 | 
					                    field1: {
 | 
				
			||||||
@ -360,7 +346,7 @@ const schema = {
 | 
				
			|||||||
                      title: '提交',
 | 
					                      title: '提交',
 | 
				
			||||||
                      'x-component': 'Action',
 | 
					                      'x-component': 'Action',
 | 
				
			||||||
                      'x-component-props': {
 | 
					                      'x-component-props': {
 | 
				
			||||||
                        useAction: '{{ useTableUpdateAction }}',
 | 
					                        useAction: '{{ Table.useTableUpdateAction }}',
 | 
				
			||||||
                      },
 | 
					                      },
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                  },
 | 
					                  },
 | 
				
			||||||
@ -374,7 +360,7 @@ const schema = {
 | 
				
			|||||||
          title: '删除',
 | 
					          title: '删除',
 | 
				
			||||||
          'x-component': 'Action',
 | 
					          'x-component': 'Action',
 | 
				
			||||||
          'x-component-props': {
 | 
					          'x-component-props': {
 | 
				
			||||||
            useAction: '{{ useTableDestroyAction }}',
 | 
					            useAction: '{{ Table.useTableDestroyAction }}',
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        [uid()]: {
 | 
					        [uid()]: {
 | 
				
			||||||
@ -474,7 +460,7 @@ const schema = {
 | 
				
			|||||||
                      type: 'void',
 | 
					                      type: 'void',
 | 
				
			||||||
                      'x-component': 'Form',
 | 
					                      'x-component': 'Form',
 | 
				
			||||||
                      'x-component-props': {
 | 
					                      'x-component-props': {
 | 
				
			||||||
                        useValues: '{{ useTableRow }}',
 | 
					                        useValues: '{{ Table.useTableRow }}',
 | 
				
			||||||
                      },
 | 
					                      },
 | 
				
			||||||
                      properties: {
 | 
					                      properties: {
 | 
				
			||||||
                        field1: {
 | 
					                        field1: {
 | 
				
			||||||
@ -494,7 +480,7 @@ const schema = {
 | 
				
			|||||||
                          title: '提交',
 | 
					                          title: '提交',
 | 
				
			||||||
                          'x-component': 'Action',
 | 
					                          'x-component': 'Action',
 | 
				
			||||||
                          'x-component-props': {
 | 
					                          'x-component-props': {
 | 
				
			||||||
                            useAction: '{{ useTableUpdateAction }}',
 | 
					                            useAction: '{{ Table.useTableUpdateAction }}',
 | 
				
			||||||
                          },
 | 
					                          },
 | 
				
			||||||
                        },
 | 
					                        },
 | 
				
			||||||
                      },
 | 
					                      },
 | 
				
			||||||
@ -508,7 +494,7 @@ const schema = {
 | 
				
			|||||||
              title: '删除',
 | 
					              title: '删除',
 | 
				
			||||||
              'x-component': 'Action',
 | 
					              'x-component': 'Action',
 | 
				
			||||||
              'x-component-props': {
 | 
					              'x-component-props': {
 | 
				
			||||||
                useAction: '{{ useTableDestroyAction }}',
 | 
					                useAction: '{{ Table.useTableDestroyAction }}',
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
@ -525,7 +511,7 @@ const form = createForm({
 | 
				
			|||||||
export default observer(() => {
 | 
					export default observer(() => {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
      <SchemaRenderer form={form} schema={schema} />
 | 
					      <SchemaRenderer scope={{ Table }} form={form} schema={schema} />
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
@ -26,14 +26,13 @@ import useRequest from '@ahooksjs/use-request';
 | 
				
			|||||||
import { BaseResult } from '@ahooksjs/use-request/lib/types';
 | 
					import { BaseResult } from '@ahooksjs/use-request/lib/types';
 | 
				
			||||||
import { uid, clone } from '@formily/shared';
 | 
					import { uid, clone } from '@formily/shared';
 | 
				
			||||||
import { MenuOutlined } from '@ant-design/icons';
 | 
					import { MenuOutlined } from '@ant-design/icons';
 | 
				
			||||||
import { useVisibleContext } from '../action';
 | 
					 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  SortableHandle,
 | 
					  SortableHandle,
 | 
				
			||||||
  SortableContainer,
 | 
					  SortableContainer,
 | 
				
			||||||
  SortableElement,
 | 
					  SortableElement,
 | 
				
			||||||
} from 'react-sortable-hoc';
 | 
					} from 'react-sortable-hoc';
 | 
				
			||||||
import cls from 'classnames';
 | 
					import cls from 'classnames';
 | 
				
			||||||
import { getSchemaPath, useDesignable, useSchemaPath } from '../';
 | 
					import { getSchemaPath, useDesignable, useSchemaPath, VisibleContext } from '../';
 | 
				
			||||||
import './style.less';
 | 
					import './style.less';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface TableRowProps {
 | 
					interface TableRowProps {
 | 
				
			||||||
@ -198,7 +197,7 @@ export function useTableDestroyAction() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export function useTableFilterAction() {
 | 
					export function useTableFilterAction() {
 | 
				
			||||||
  const { field, refresh, params } = useTableContext();
 | 
					  const { field, refresh, params } = useTableContext();
 | 
				
			||||||
  const { setVisible } = useVisibleContext();
 | 
					  const [,setVisible] = useContext(VisibleContext);
 | 
				
			||||||
  const form = useForm();
 | 
					  const form = useForm();
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    async run() {
 | 
					    async run() {
 | 
				
			||||||
@ -210,7 +209,7 @@ export function useTableFilterAction() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export function useTableCreateAction() {
 | 
					export function useTableCreateAction() {
 | 
				
			||||||
  const { field, run: exec, params } = useTableContext();
 | 
					  const { field, run: exec, params } = useTableContext();
 | 
				
			||||||
  const { setVisible } = useVisibleContext();
 | 
					  const [,setVisible] = useContext(VisibleContext);
 | 
				
			||||||
  const form = useForm();
 | 
					  const form = useForm();
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    async run() {
 | 
					    async run() {
 | 
				
			||||||
@ -227,7 +226,7 @@ export function useTableCreateAction() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export function useTableUpdateAction() {
 | 
					export function useTableUpdateAction() {
 | 
				
			||||||
  const { field, refresh, params } = useTableContext();
 | 
					  const { field, refresh, params } = useTableContext();
 | 
				
			||||||
  const { setVisible } = useVisibleContext();
 | 
					  const [,setVisible] = useContext(VisibleContext);
 | 
				
			||||||
  const form = useForm();
 | 
					  const form = useForm();
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    async run() {
 | 
					    async run() {
 | 
				
			||||||
@ -317,7 +316,7 @@ const TableContainer = observer((props) => {
 | 
				
			|||||||
      {actionBars.top.map((actionBarSchema) => {
 | 
					      {actionBars.top.map((actionBarSchema) => {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
          <RecursionField
 | 
					          <RecursionField
 | 
				
			||||||
            onlyRenderProperties
 | 
					            // onlyRenderProperties
 | 
				
			||||||
            schema={
 | 
					            schema={
 | 
				
			||||||
              new Schema({
 | 
					              new Schema({
 | 
				
			||||||
                type: 'object',
 | 
					                type: 'object',
 | 
				
			||||||
@ -419,6 +418,13 @@ export const Table: any = observer((props) => {
 | 
				
			|||||||
  );
 | 
					  );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Table.useTableRow = useTableRow;
 | 
				
			||||||
 | 
					Table.useTableIndex = useTableIndex;
 | 
				
			||||||
 | 
					Table.useTableDestroyAction = useTableDestroyAction;
 | 
				
			||||||
 | 
					Table.useTableFilterAction = useTableFilterAction;
 | 
				
			||||||
 | 
					Table.useTableCreateAction = useTableCreateAction;
 | 
				
			||||||
 | 
					Table.useTableUpdateAction = useTableUpdateAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Blank() {
 | 
					function Blank() {
 | 
				
			||||||
  return null;
 | 
					  return null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user