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,10 +157,12 @@ 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('useDesignable', { schema, schemaPath, currentSchema });
|
console.log({ currentSchema })
|
||||||
|
// console.log('useDesignable', { schema, schemaPath, currentSchema });
|
||||||
const options = useContext(SchemaOptionsContext);
|
const options = useContext(SchemaOptionsContext);
|
||||||
const DesignableBar =
|
const DesignableBar =
|
||||||
get(options.components, currentSchema['x-designable-bar']) || (() => null);
|
get(options.components, currentSchema['x-designable-bar']) || (() => null);
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
path.unshift(parent.name);
|
if (parent['x-path']) {
|
||||||
|
path.unshift(...parent['x-path']);
|
||||||
|
} else {
|
||||||
|
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: {
|
'x-component-props': {
|
||||||
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: {
|
properties: {
|
||||||
drawer1: {
|
modal1: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '弹窗标题',
|
title: '对话框标题',
|
||||||
'x-component': 'Action.Drawer',
|
'x-component': 'Action.Modal',
|
||||||
'x-component-props': {
|
|
||||||
// footer: null,
|
|
||||||
},
|
|
||||||
'x-decorator': 'Form',
|
|
||||||
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',
|
||||||
|
'x-designable-bar': 'Action.DesignableBar',
|
||||||
|
'x-component-props': {
|
||||||
|
},
|
||||||
properties: {
|
properties: {
|
||||||
drawer1: {
|
modal1: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '弹窗标题',
|
title: '对话框标题',
|
||||||
'x-component': 'Action.Modal',
|
|
||||||
'x-component-props': {
|
|
||||||
// footer: null,
|
|
||||||
},
|
|
||||||
'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();
|
type: 'void',
|
||||||
const schema = {
|
name: 'action1',
|
||||||
type: 'void',
|
title: '按钮',
|
||||||
name: 'action1',
|
'x-component': 'Action',
|
||||||
title: '按钮',
|
'x-designable-bar': 'Action.DesignableBar',
|
||||||
'x-component': 'Action',
|
'x-component-props': {
|
||||||
'x-component-props': {
|
},
|
||||||
containerRef
|
properties: {
|
||||||
},
|
modal1: {
|
||||||
properties: {
|
type: 'void',
|
||||||
container1: {
|
title: '抽屉标题',
|
||||||
type: 'void',
|
'x-component': 'Action.Drawer',
|
||||||
title: '页面标题',
|
properties: {
|
||||||
'x-component': 'Action.Container',
|
input: {
|
||||||
properties: {
|
type: 'string',
|
||||||
input: {
|
title: '输入框',
|
||||||
type: 'string',
|
required: true,
|
||||||
title: '字段',
|
'x-decorator': 'FormItem',
|
||||||
'x-designable-bar': 'FormItem.DesignableBar',
|
'x-component': 'Input',
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'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 isDropdownOrPopover =
|
||||||
|
child &&
|
||||||
|
['Action.Dropdown', 'Action.Popover'].includes(child['x-component']);
|
||||||
|
console.log({ DesignableBar, schema })
|
||||||
|
return (
|
||||||
|
<VisibleContext.Provider value={[visible, setVisible]}>
|
||||||
|
{!isDropdownOrPopover && (
|
||||||
|
<Button
|
||||||
|
{...others}
|
||||||
|
onClick={async () => {
|
||||||
|
setVisible(true);
|
||||||
|
await run();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{schema.title}
|
||||||
|
<DesignableBar />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<RecursionField schema={schema} onlyRenderProperties />
|
||||||
|
</VisibleContext.Provider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Action.Modal = observer((props: any) => {
|
||||||
const { schema } = useDesignable();
|
const { schema } = useDesignable();
|
||||||
|
const [visible, setVisible] = useContext(VisibleContext);
|
||||||
useEffect(() => {
|
const form = useForm();
|
||||||
field.componentProps.setVisible = setVisible;
|
const isFormDecorator = schema['x-decorator'] === 'Form';
|
||||||
}, []);
|
|
||||||
|
|
||||||
const renderButton = () => (
|
|
||||||
<ButtonComponent
|
|
||||||
{...others}
|
|
||||||
icon={icon ? <Icon type={icon} /> : null}
|
|
||||||
className={classNames(className, `name-${schema.name}`)}
|
|
||||||
onClick={async (e) => {
|
|
||||||
e.stopPropagation && e.stopPropagation();
|
|
||||||
setVisible && setVisible(true);
|
|
||||||
await run();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{schema.title}
|
|
||||||
<DesignableBar />
|
|
||||||
</ButtonComponent>
|
|
||||||
);
|
|
||||||
|
|
||||||
const popover = schema.reduceProperties((items, current) => {
|
|
||||||
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 (
|
return (
|
||||||
<>
|
<Modal
|
||||||
{renderButton()}
|
title={schema.title}
|
||||||
{props.children}
|
destroyOnClose
|
||||||
</>
|
maskClosable
|
||||||
);
|
footer={
|
||||||
});
|
isFormDecorator
|
||||||
|
? [
|
||||||
export const Action: ActionType = observer((props: any) => {
|
<Button
|
||||||
const schema = useFieldSchema();
|
onClick={async () => {
|
||||||
if (schema.properties) {
|
if (isFormDecorator) {
|
||||||
return (
|
form.clearErrors();
|
||||||
<VisibleProvider
|
}
|
||||||
initialVisible={props.initialVisible}
|
setVisible(false);
|
||||||
containerRef={props.containerRef}
|
}}
|
||||||
>
|
>
|
||||||
<BaseAction {...props} />
|
Cancel
|
||||||
</VisibleProvider>
|
</Button>,
|
||||||
);
|
<Button
|
||||||
}
|
type={'primary'}
|
||||||
return <BaseAction {...props} />;
|
onClick={async () => {
|
||||||
});
|
if (isFormDecorator) {
|
||||||
|
await form.submit();
|
||||||
Action.Link = observer((props) => {
|
}
|
||||||
const schema = useFieldSchema();
|
setVisible(false);
|
||||||
return <Link {...props}>{schema.title}</Link>;
|
}}
|
||||||
});
|
>
|
||||||
|
OK
|
||||||
Action.URL = observer((props) => {
|
</Button>,
|
||||||
const { url, ...others } = props;
|
]
|
||||||
const schema = useFieldSchema();
|
: null
|
||||||
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}
|
{...props}
|
||||||
content={props.children}
|
onCancel={() => {
|
||||||
|
if (isFormDecorator) {
|
||||||
|
form.clearErrors();
|
||||||
|
}
|
||||||
|
setVisible(false);
|
||||||
|
}}
|
||||||
|
visible={visible}
|
||||||
>
|
>
|
||||||
{schema['x-button']}
|
{props.children}
|
||||||
</Popover>
|
</Modal>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Action.Drawer = observer((props) => {
|
Action.Drawer = observer((props: any) => {
|
||||||
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') {
|
|
||||||
Object.assign(others, {
|
|
||||||
footer: (
|
|
||||||
<Space>
|
|
||||||
<Button
|
|
||||||
onClick={async () => {
|
|
||||||
await form.submit();
|
|
||||||
await run();
|
|
||||||
setVisible(false);
|
|
||||||
}}
|
|
||||||
type={'primary'}
|
|
||||||
>
|
|
||||||
OK
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={async () => {
|
|
||||||
form.clearErrors();
|
|
||||||
setVisible(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
}}
|
|
||||||
title={field.title}
|
|
||||||
width={'50%'}
|
width={'50%'}
|
||||||
{...others}
|
title={schema.title}
|
||||||
visible={visible}
|
maskClosable
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
onClose={(e) => {
|
footer={
|
||||||
e.stopPropagation();
|
isFormDecorator && (
|
||||||
form.clearErrors();
|
<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);
|
||||||
|
}}
|
||||||
|
type={'primary'}
|
||||||
|
>
|
||||||
|
OK
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{...props}
|
||||||
|
visible={visible}
|
||||||
|
onClose={async (e) => {
|
||||||
|
props.onClose && (await props.onClose(e));
|
||||||
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}
|
||||||
// {...props}
|
title={schema.title}
|
||||||
overlayClassName={'nb-action-group'}
|
content={
|
||||||
// trigger={'click'}
|
<div>
|
||||||
content={props.children}
|
{props.children}
|
||||||
placement={'bottomLeft'}
|
{isFormDecorator && (
|
||||||
|
<div>
|
||||||
|
<Space>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
form.clearErrors();
|
||||||
|
setVisible(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type={'primary'}
|
||||||
|
onClick={async () => {
|
||||||
|
await form.submit();
|
||||||
|
setVisible(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Ok
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Button icon={icon ? <Icon type={icon} /> : null} {...others}>
|
<Button {...componentProps}>{schema.parent.title || schema.title}</Button>
|
||||||
{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={cls('designable-bar-actions', { active: visible })}
|
||||||
|
>
|
||||||
|
<Dropdown
|
||||||
|
trigger={['click']}
|
||||||
|
visible={visible}
|
||||||
|
onVisibleChange={(visible) => {
|
||||||
|
setVisible(visible);
|
||||||
}}
|
}}
|
||||||
className={classNames('designable-bar-actions', { active: 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>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<SchemaRenderer schema={DesignableBar.Action} />
|
<MenuOutlined />
|
||||||
</span>
|
</Dropdown>
|
||||||
</div>
|
</span>
|
||||||
</DesignableContextProvider>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
@ -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]);
|
|
||||||
const schema = useFieldSchema();
|
|
||||||
const { schema: designableSchema, refresh } = useDesignable();
|
|
||||||
const formSchema: ISchema = schema['x-decorator'] === 'Form' ? {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
[schema.name]: {
|
|
||||||
...schema.toJSON(),
|
|
||||||
"x-decorator": 'Form.Decorator',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} : schema.toJSON();
|
|
||||||
return (
|
return (
|
||||||
<SchemaRenderer
|
<FormProvider form={form}>
|
||||||
scope={scope}
|
{schema['x-decorator'] === 'Form' ? (
|
||||||
// components={options.components}
|
<SchemaField
|
||||||
onRefresh={(subSchema: Schema) => {
|
schema={{
|
||||||
designableSchema.properties = subSchema.properties;
|
type: 'object',
|
||||||
refresh();
|
properties: {
|
||||||
}}
|
[schema.name]: {
|
||||||
form={form}
|
...schema.toJSON(),
|
||||||
schema={formSchema}
|
'x-path': path,
|
||||||
onlyRenderProperties
|
'x-decorator': 'Form.__Decorator',
|
||||||
/>
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<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={
|
||||||
) : (
|
<div>新增</div>
|
||||||
<SchemaRenderer
|
}></AntdMenu.ItemGroup>
|
||||||
onRefresh={(subSchema: Schema) => {
|
|
||||||
Object.keys(subSchema.properties).forEach((name) => {
|
|
||||||
if (name === 'add-new') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
designableSchema.addProperty(
|
|
||||||
name,
|
|
||||||
subSchema.properties[name].toJSON(),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
refresh();
|
|
||||||
}}
|
|
||||||
schema={{
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
'add-new': {
|
|
||||||
parentKey: schema['key'],
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Menu.AddNew',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</AntdMenu>
|
</AntdMenu>
|
||||||
</MenuContext.Provider>
|
{mode === 'mix' && (
|
||||||
|
<div ref={ref}>
|
||||||
|
<SideMenu
|
||||||
|
onSelect={onSelect}
|
||||||
|
visible={mode === 'mix'}
|
||||||
|
selectedKey={selectedKey}
|
||||||
|
sideMenuRef={sideMenuRef}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</MenuModeContext.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,66 +49,52 @@ 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: {
|
properties: {
|
||||||
[uid()]: {
|
filter: {
|
||||||
name: 'form',
|
name: 'filter',
|
||||||
type: 'object',
|
type: 'object',
|
||||||
'x-component': 'Form',
|
'x-component': 'Filter',
|
||||||
properties: {
|
properties: {
|
||||||
filter: {
|
column1: {
|
||||||
name: 'filter',
|
|
||||||
type: 'object',
|
|
||||||
'x-component': 'Filter',
|
|
||||||
properties: {
|
|
||||||
column1: {
|
|
||||||
type: 'void',
|
|
||||||
title: '字段1',
|
|
||||||
'x-component': 'Filter.Column',
|
|
||||||
'x-component-props': {
|
|
||||||
operations: [
|
|
||||||
{ label: '等于', value: 'eq' },
|
|
||||||
{ label: '不等于', value: 'ne' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
field1: {
|
|
||||||
type: 'string',
|
|
||||||
'x-component': 'Input',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
column2: {
|
|
||||||
type: 'void',
|
|
||||||
title: '字段2',
|
|
||||||
'x-component': 'Filter.Column',
|
|
||||||
'x-component-props': {
|
|
||||||
operations: [
|
|
||||||
{ label: '大于', value: 'gt' },
|
|
||||||
{ label: '小于', value: 'lt' },
|
|
||||||
{ label: '非空', value: 'notNull', noValue: true },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
field1: {
|
|
||||||
type: 'number',
|
|
||||||
'x-component': 'InputNumber',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
action: {
|
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '提交',
|
title: '字段1',
|
||||||
'x-component': 'Action',
|
'x-component': 'Filter.Column',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
useAction: '{{ useTableFilterAction }}',
|
operations: [
|
||||||
|
{ label: '等于', value: 'eq' },
|
||||||
|
{ label: '不等于', value: 'ne' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
field1: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
column2: {
|
||||||
},
|
type: 'void',
|
||||||
|
title: '字段2',
|
||||||
|
'x-component': 'Filter.Column',
|
||||||
|
'x-component-props': {
|
||||||
|
operations: [
|
||||||
|
{ label: '大于', value: 'gt' },
|
||||||
|
{ label: '小于', value: 'lt' },
|
||||||
|
{ label: '非空', value: 'notNull', noValue: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
field1: {
|
||||||
|
type: 'number',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -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