updates
This commit is contained in:
parent
e53a1edf7d
commit
708afa059c
@ -1,7 +1,7 @@
|
|||||||
export default {
|
export default {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
input: {
|
email: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: true,
|
required: true,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
@ -13,7 +13,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
textarea: {
|
password: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: true,
|
required: true,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
@ -27,7 +27,6 @@ export default {
|
|||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
// 'x-decorator': 'Div',
|
|
||||||
'x-component': 'Div',
|
'x-component': 'Div',
|
||||||
properties: {
|
properties: {
|
||||||
submit: {
|
submit: {
|
||||||
|
@ -2,15 +2,18 @@ import { uid } from '@formily/shared';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
name: `m_${uid()}`,
|
name: `menu_${uid()}`,
|
||||||
'x-component': 'Menu',
|
'x-component': 'Menu',
|
||||||
'x-designable-bar': 'Menu.DesignableBar',
|
'x-designable-bar': 'Menu.DesignableBar',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
mode: 'mix',
|
mode: 'mix',
|
||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
|
defaultSelectedKeys: '{{ selectedKeys }}',
|
||||||
|
sideMenuRef: '{{ sideMenuRef }}',
|
||||||
|
onSelect: '{{ onSelect }}',
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
item2: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: `菜单2`,
|
title: `菜单2`,
|
||||||
'x-component': 'Menu.Link',
|
'x-component': 'Menu.Link',
|
||||||
@ -18,7 +21,7 @@ export default {
|
|||||||
icon: 'MailOutlined',
|
icon: 'MailOutlined',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
item22: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: `菜单22`,
|
title: `菜单22`,
|
||||||
'x-component': 'Menu.Link',
|
'x-component': 'Menu.Link',
|
||||||
@ -27,15 +30,23 @@ export default {
|
|||||||
// url: 'https://www.google.com',
|
// url: 'https://www.google.com',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
item3: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '菜单组',
|
title: '菜单组1',
|
||||||
|
'x-component': 'Menu.SubMenu',
|
||||||
|
'x-component-props': {
|
||||||
|
icon: 'SettingOutlined',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
title: '菜单组2',
|
||||||
'x-component': 'Menu.SubMenu',
|
'x-component': 'Menu.SubMenu',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
icon: 'SettingOutlined',
|
icon: 'SettingOutlined',
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
item6: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '菜单6',
|
title: '菜单6',
|
||||||
'x-component': 'Menu.SubMenu',
|
'x-component': 'Menu.SubMenu',
|
||||||
@ -43,55 +54,48 @@ export default {
|
|||||||
icon: 'AppstoreOutlined',
|
icon: 'AppstoreOutlined',
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
item9: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '菜单9',
|
title: '菜单9',
|
||||||
'x-component': 'Menu.SubMenu',
|
'x-component': 'Menu.SubMenu',
|
||||||
properties: {
|
properties: {
|
||||||
item10: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: `子菜单10`,
|
title: `子菜单10`,
|
||||||
'x-component': 'Menu.Link',
|
'x-component': 'Menu.Link',
|
||||||
// properties: {
|
|
||||||
// action1: {
|
|
||||||
// type: 'void',
|
|
||||||
// title: '页面标题2',
|
|
||||||
// 'x-component': 'Action.Page',
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
},
|
},
|
||||||
item11: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: `子菜单11`,
|
title: `子菜单11`,
|
||||||
'x-component': 'Menu.Link',
|
'x-component': 'Menu.Link',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
item7: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: `子菜单7`,
|
title: `子菜单7`,
|
||||||
'x-component': 'Menu.Link',
|
'x-component': 'Menu.Link',
|
||||||
},
|
},
|
||||||
item8: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: `子菜单8`,
|
title: `子菜单8`,
|
||||||
'x-component': 'Menu.Link',
|
'x-component': 'Menu.Link',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
item4: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: `子菜单1`,
|
title: `子菜单1`,
|
||||||
'x-component': 'Menu.Link',
|
'x-component': 'Menu.Link',
|
||||||
},
|
},
|
||||||
item5: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: `子菜单2`,
|
title: `子菜单2`,
|
||||||
'x-component': 'Menu.Link',
|
'x-component': 'Menu.Link',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
item1: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: `菜单1`,
|
title: `菜单1`,
|
||||||
'x-component': 'Menu.Link',
|
'x-component': 'Menu.Link',
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
FormProvider,
|
FormProvider,
|
||||||
useField,
|
useField,
|
||||||
useFieldSchema,
|
useFieldSchema,
|
||||||
|
useForm,
|
||||||
} from '@formily/react';
|
} from '@formily/react';
|
||||||
import { observable } from '@formily/reactive';
|
import { observable } from '@formily/reactive';
|
||||||
import { uid, clone } from '@formily/shared';
|
import { uid, clone } from '@formily/shared';
|
||||||
@ -304,8 +305,7 @@ export function useDesignable(path?: any) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSchemaPath() {
|
export function getSchemaPath(schema: Schema) {
|
||||||
const schema = useFieldSchema();
|
|
||||||
const path = [schema.name];
|
const path = [schema.name];
|
||||||
let parent = schema.parent;
|
let parent = schema.parent;
|
||||||
while (parent) {
|
while (parent) {
|
||||||
@ -315,7 +315,13 @@ export function useSchemaPath() {
|
|||||||
path.unshift(parent.name);
|
path.unshift(parent.name);
|
||||||
parent = parent.parent;
|
parent = parent.parent;
|
||||||
}
|
}
|
||||||
console.log('useSchemaPath', path, schema);
|
console.log('getSchemaPath', path, schema);
|
||||||
|
return [...path];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSchemaPath() {
|
||||||
|
const schema = useFieldSchema();
|
||||||
|
const path = getSchemaPath(schema);
|
||||||
return [...path];
|
return [...path];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,6 +331,7 @@ export const createDesignableSchemaField = (options) => {
|
|||||||
const SchemaField = createSchemaField(options);
|
const SchemaField = createSchemaField(options);
|
||||||
|
|
||||||
const DesignableSchemaField = (props) => {
|
const DesignableSchemaField = (props) => {
|
||||||
|
|
||||||
const schema = useMemo(() => new Schema(props.schema), [props.schema]);
|
const schema = useMemo(() => new Schema(props.schema), [props.schema]);
|
||||||
const [, refresh] = useState(0);
|
const [, refresh] = useState(0);
|
||||||
if (props.designable === false) {
|
if (props.designable === false) {
|
||||||
@ -340,8 +347,9 @@ export const createDesignableSchemaField = (options) => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SchemaField schema={schema} />
|
<SchemaField scope={props.scope} components={props.components} schema={schema} />
|
||||||
<CodePreview schema={schema} />
|
<CodePreview schema={schema} />
|
||||||
|
<FormValues />
|
||||||
</DesignableContext.Provider>
|
</DesignableContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -349,11 +357,34 @@ export const createDesignableSchemaField = (options) => {
|
|||||||
return DesignableSchemaField;
|
return DesignableSchemaField;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CodePreview = ({ schema }) => {
|
const FormValues = () => {
|
||||||
|
const form = useForm();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CodeOutlined onClick={() => setVisible(true)} />
|
<CodeOutlined onClick={() => setVisible(true)} />
|
||||||
|
<Modal
|
||||||
|
width={'50%'}
|
||||||
|
onOk={() => setVisible(false)}
|
||||||
|
onCancel={() => setVisible(false)}
|
||||||
|
visible={visible}
|
||||||
|
>
|
||||||
|
<Editor
|
||||||
|
height="60vh"
|
||||||
|
defaultLanguage="json"
|
||||||
|
value={JSON.stringify(form.values, null, 2)}
|
||||||
|
/>
|
||||||
|
{/* <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre> */}
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CodePreview = ({ schema }) => {
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CodeOutlined style={{position: 'relative', zIndex: 100}} onClick={() => setVisible(true)} />
|
||||||
<Modal
|
<Modal
|
||||||
width={'50%'}
|
width={'50%'}
|
||||||
onOk={() => setVisible(false)}
|
onOk={() => setVisible(false)}
|
||||||
@ -387,6 +418,8 @@ export interface SchemaRendererProps {
|
|||||||
designable?: boolean;
|
designable?: boolean;
|
||||||
onRefresh?: any;
|
onRefresh?: any;
|
||||||
onlyRenderProperties?: boolean;
|
onlyRenderProperties?: boolean;
|
||||||
|
scope?: any;
|
||||||
|
components?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SchemaRenderer = (props: SchemaRendererProps) => {
|
export const SchemaRenderer = (props: SchemaRendererProps) => {
|
||||||
@ -415,6 +448,8 @@ export const SchemaRenderer = (props: SchemaRendererProps) => {
|
|||||||
return (
|
return (
|
||||||
<FormProvider form={form}>
|
<FormProvider form={form}>
|
||||||
<DesignableSchemaField
|
<DesignableSchemaField
|
||||||
|
scope={props.scope}
|
||||||
|
components={props.components}
|
||||||
onRefresh={props.onRefresh}
|
onRefresh={props.onRefresh}
|
||||||
designable={props.designable}
|
designable={props.designable}
|
||||||
schema={schema}
|
schema={schema}
|
||||||
|
@ -13,11 +13,13 @@ group:
|
|||||||
|
|
||||||
操作有三类:
|
操作有三类:
|
||||||
|
|
||||||
- 常规操作
|
- 常规操作:Action
|
||||||
- 弹出层操作,弹出层有 Drawer、Modal、Popover
|
- 弹出层操作,弹出层可以是 Action.Drawer、Action.Modal、Action.Popover
|
||||||
- 指定容器内操作 Container
|
- 指定容器内打开: Action.Container
|
||||||
- 下拉菜单,用于收纳多种操作
|
- 下拉菜单:Action.Dropdown,用于收纳多种操作
|
||||||
- 跳转操作
|
- 跳转操作:Action.Link、Action.URL 和 Action.Route
|
||||||
|
|
||||||
|
Action.Drawer、Action.Modal、Action.Popover 和 Action.Container 需要和 Action 搭配使用
|
||||||
|
|
||||||
## Action - 常规操作
|
## Action - 常规操作
|
||||||
|
|
||||||
|
@ -106,12 +106,18 @@ const [VisibleProvider, useVisibleContext] = constate((props: any = {}) => {
|
|||||||
export { VisibleProvider, useVisibleContext };
|
export { VisibleProvider, useVisibleContext };
|
||||||
|
|
||||||
const BaseAction = observer((props: any) => {
|
const BaseAction = observer((props: any) => {
|
||||||
const { useAction = useDefaultAction, ...others } = props;
|
const {
|
||||||
|
ButtonComponent = Button,
|
||||||
|
className,
|
||||||
|
useAction = useDefaultAction,
|
||||||
|
...others
|
||||||
|
} = props;
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const { run } = useAction();
|
const { run } = useAction();
|
||||||
const { DesignableBar } = useDesignableBar();
|
const { DesignableBar } = useDesignableBar();
|
||||||
const { setVisible } = useVisibleContext();
|
const { setVisible } = useVisibleContext();
|
||||||
const schema = useFieldSchema();
|
// const schema = useFieldSchema();
|
||||||
|
const { schema } = useDesignable();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
field.componentProps.setVisible = setVisible;
|
field.componentProps.setVisible = setVisible;
|
||||||
}, []);
|
}, []);
|
||||||
@ -119,17 +125,18 @@ const BaseAction = observer((props: any) => {
|
|||||||
console.log('BaseAction', { field, schema }, field.title);
|
console.log('BaseAction', { field, schema }, field.title);
|
||||||
|
|
||||||
const renderButton = () => (
|
const renderButton = () => (
|
||||||
<Button
|
<ButtonComponent
|
||||||
{...others}
|
{...others}
|
||||||
|
className={classNames(className, `name-${schema.name}`)}
|
||||||
onClick={async (e) => {
|
onClick={async (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation && e.stopPropagation();
|
||||||
setVisible && setVisible(true);
|
setVisible && setVisible(true);
|
||||||
await run();
|
await run();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{field.title}
|
{schema.title}
|
||||||
<DesignableBar />
|
<DesignableBar />
|
||||||
</Button>
|
</ButtonComponent>
|
||||||
);
|
);
|
||||||
|
|
||||||
const popover = schema.reduceProperties((items, current) => {
|
const popover = schema.reduceProperties((items, current) => {
|
||||||
@ -268,6 +275,7 @@ Action.Dropdown = observer((props) => {
|
|||||||
// const { visible, setVisible } = useVisibleContext();
|
// const { visible, setVisible } = useVisibleContext();
|
||||||
const schema = useFieldSchema();
|
const schema = useFieldSchema();
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Popover
|
<Popover
|
||||||
// visible={visible}
|
// visible={visible}
|
||||||
// onVisibleChange={(visible) => {
|
// onVisibleChange={(visible) => {
|
||||||
@ -281,13 +289,16 @@ Action.Dropdown = observer((props) => {
|
|||||||
>
|
>
|
||||||
<Button>{schema.title}</Button>
|
<Button>{schema.title}</Button>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
{/* popover 的按钮初始化时并未渲染,暂时先这么处理 */}
|
||||||
|
<div style={{display: 'none'}}>{props.children}</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Action.DesignableBar = () => {
|
Action.DesignableBar = () => {
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const schema = useFieldSchema();
|
// const schema = useFieldSchema();
|
||||||
const { insertAfter } = useDesignable();
|
const { schema, insertAfter } = useDesignable();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
return (
|
return (
|
||||||
<div className={classNames('designable-bar', { active: visible })}>
|
<div className={classNames('designable-bar', { active: visible })}>
|
||||||
|
@ -186,10 +186,15 @@ import { MenuContainerContext } from './';
|
|||||||
import { Layout } from 'antd';
|
import { Layout } from 'antd';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const ref = useRef();
|
const sideMenuRef = useRef();
|
||||||
|
|
||||||
const [activeKey, setActiveKey] = useState('item3');
|
const [activeKey, setActiveKey] = useState('item3');
|
||||||
|
|
||||||
|
const onSelect = (info) => {
|
||||||
|
setActiveKey(info.key);
|
||||||
|
console.log({ info })
|
||||||
|
}
|
||||||
|
|
||||||
const schema = {
|
const schema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
@ -197,13 +202,11 @@ export default () => {
|
|||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': 'Menu',
|
'x-component': 'Menu',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
|
sideMenuRef: '{{ sideMenuRef }}',
|
||||||
defaultSelectedKeys: [activeKey],
|
defaultSelectedKeys: [activeKey],
|
||||||
mode: 'mix',
|
mode: 'mix',
|
||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
onSelect(info) {
|
onSelect: '{{ onSelect }}',
|
||||||
setActiveKey(info.key);
|
|
||||||
console.log({ info })
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
item1: {
|
item1: {
|
||||||
@ -271,14 +274,13 @@ export default () => {
|
|||||||
<div>
|
<div>
|
||||||
<Layout>
|
<Layout>
|
||||||
<Layout.Header>
|
<Layout.Header>
|
||||||
<MenuContainerContext.Provider value={{
|
<SchemaRenderer
|
||||||
sideMenuRef: ref,
|
schema={schema}
|
||||||
}}>
|
scope={{ onSelect, sideMenuRef }}
|
||||||
<SchemaRenderer schema={schema} />
|
/>
|
||||||
</MenuContainerContext.Provider>
|
|
||||||
</Layout.Header>
|
</Layout.Header>
|
||||||
<Layout>
|
<Layout>
|
||||||
<Layout.Sider ref={ref} theme={'light'} width={200}>
|
<Layout.Sider ref={sideMenuRef} theme={'light'} width={200}>
|
||||||
</Layout.Sider>
|
</Layout.Sider>
|
||||||
<Layout.Content>
|
<Layout.Content>
|
||||||
{activeKey}
|
{activeKey}
|
||||||
@ -299,10 +301,15 @@ import { MenuContainerContext } from './';
|
|||||||
import { Layout } from 'antd';
|
import { Layout } from 'antd';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const ref = useRef();
|
const sideMenuRef = useRef();
|
||||||
|
|
||||||
const [activeKey, setActiveKey] = useState('item3');
|
const [activeKey, setActiveKey] = useState('item3');
|
||||||
|
|
||||||
|
const onSelect = (info) => {
|
||||||
|
setActiveKey(info.key);
|
||||||
|
console.log({ info })
|
||||||
|
}
|
||||||
|
|
||||||
const schema = {
|
const schema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
@ -314,10 +321,8 @@ export default () => {
|
|||||||
defaultSelectedKeys: [activeKey],
|
defaultSelectedKeys: [activeKey],
|
||||||
mode: 'mix',
|
mode: 'mix',
|
||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
onSelect(info) {
|
sideMenuRef: '{{ sideMenuRef }}',
|
||||||
setActiveKey(info.key);
|
onSelect: '{{ onSelect }}',
|
||||||
console.log({ info })
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
item1: {
|
item1: {
|
||||||
@ -385,14 +390,17 @@ export default () => {
|
|||||||
<div>
|
<div>
|
||||||
<Layout>
|
<Layout>
|
||||||
<Layout.Header>
|
<Layout.Header>
|
||||||
<MenuContainerContext.Provider value={{
|
<SchemaRenderer
|
||||||
sideMenuRef: ref,
|
schema={schema}
|
||||||
}}>
|
scope={{ onSelect, sideMenuRef }}
|
||||||
<SchemaRenderer schema={schema} />
|
/>
|
||||||
</MenuContainerContext.Provider>
|
|
||||||
</Layout.Header>
|
</Layout.Header>
|
||||||
<Layout>
|
<Layout>
|
||||||
<Layout.Sider ref={ref} theme={'light'} width={200}>
|
<Layout.Sider
|
||||||
|
ref={sideMenuRef}
|
||||||
|
theme={'light'}
|
||||||
|
width={200}
|
||||||
|
>
|
||||||
</Layout.Sider>
|
</Layout.Sider>
|
||||||
<Layout.Content>
|
<Layout.Content>
|
||||||
{activeKey}
|
{activeKey}
|
||||||
@ -403,3 +411,82 @@ export default () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Menu.Action
|
||||||
|
|
||||||
|
|
||||||
|
```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} />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
@ -51,10 +51,10 @@ import cls from 'classnames';
|
|||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { useMount } from 'ahooks';
|
import { useMount } from 'ahooks';
|
||||||
import { useDesignable, SchemaRenderer } from '..';
|
import { useDesignable, SchemaRenderer } from '..';
|
||||||
import { Router } from "react-router";
|
import { Router } from 'react-router';
|
||||||
|
|
||||||
import { useLifecycle } from 'beautiful-react-hooks';
|
import { useLifecycle } from 'beautiful-react-hooks';
|
||||||
import { useDefaultAction } from '../action';
|
import { Action, useDefaultAction } from '../action';
|
||||||
|
|
||||||
export type MenuType = React.FC<MenuProps & { hideSubMenu?: boolean }> & {
|
export type MenuType = React.FC<MenuProps & { hideSubMenu?: boolean }> & {
|
||||||
Item?: React.FC<MenuItemProps>;
|
Item?: React.FC<MenuItemProps>;
|
||||||
@ -63,6 +63,7 @@ export type MenuType = React.FC<MenuProps & { hideSubMenu?: boolean }> & {
|
|||||||
DesignableBar?: React.FC<any>;
|
DesignableBar?: React.FC<any>;
|
||||||
AddNew?: React.FC<any>;
|
AddNew?: React.FC<any>;
|
||||||
Link?: React.FC<MenuItemProps>;
|
Link?: React.FC<MenuItemProps>;
|
||||||
|
Action?: React.FC<MenuItemProps>;
|
||||||
Url?: React.FC<MenuItemProps & { url: string }>;
|
Url?: React.FC<MenuItemProps & { url: string }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -92,9 +93,9 @@ function useDesignableBar() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Menu: MenuType = observer((props) => {
|
export const Menu: MenuType = observer((props: any) => {
|
||||||
const { onSelect, mode, defaultSelectedKeys, ...others } = props;
|
const { sideMenuRef, onSelect, mode, defaultSelectedKeys, ...others } = props;
|
||||||
const { sideMenuRef } = useContext(MenuContainerContext);
|
let defaultSelectedKey = defaultSelectedKeys ? defaultSelectedKeys[0] : null;
|
||||||
const schema = useFieldSchema();
|
const schema = useFieldSchema();
|
||||||
const { schema: designableSchema, refresh } = useDesignable();
|
const { schema: designableSchema, refresh } = useDesignable();
|
||||||
const designableBar = schema['x-designable-bar'];
|
const designableBar = schema['x-designable-bar'];
|
||||||
@ -109,33 +110,41 @@ export const Menu: MenuType = observer((props) => {
|
|||||||
if (!sideMenuRef || !sideMenuRef.current) {
|
if (!sideMenuRef || !sideMenuRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const properties = schema.properties[selectedKey].properties;
|
const subSchema = schema.properties[selectedKey];
|
||||||
console.log({ selectedKey, properties });
|
if (!subSchema) {
|
||||||
sideMenuRef.current.style.display = properties ? 'block' : 'none';
|
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 = {};
|
const newProps = {};
|
||||||
Object.keys(properties || {}).forEach((name) => {
|
Object.keys(properties || {}).forEach((name) => {
|
||||||
newProps[name] = properties[name].toJSON();
|
newProps[name] = properties[name].toJSON();
|
||||||
});
|
});
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
properties ? (
|
<Router history={history}>
|
||||||
<Router history={history}>
|
|
||||||
<SchemaRenderer
|
<SchemaRenderer
|
||||||
key={`${Math.random()}`}
|
key={`${Math.random()}`}
|
||||||
onRefresh={(subSchema: Schema) => {
|
onRefresh={(subSchema: Schema) => {
|
||||||
const selected = designableSchema.properties[selectedKey];
|
const selected = designableSchema.properties[selectedKey];
|
||||||
const diff = subSchema.properties[`${schema.name}.${selectedKey}`];
|
const diff = subSchema.properties[`${schema.name}.${selectedKey}`];
|
||||||
Object.keys(selected.properties).forEach((name) => {
|
Object.keys(selected.properties || {}).forEach((name) => {
|
||||||
selected.properties[name].parent.removeProperty(name);
|
selected.properties[name].parent.removeProperty(name);
|
||||||
});
|
});
|
||||||
Object.keys(diff.properties).forEach((name) => {
|
Object.keys(diff.properties).forEach((name) => {
|
||||||
if (name.endsWith('-add-new')) {
|
if (name.endsWith('-add-new')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('diff', name)
|
console.log('diff', name);
|
||||||
const current = diff.properties[name];
|
const current = diff.properties[name];
|
||||||
selected.addProperty(current.name, current.toJSON());
|
selected.addProperty(current.name, current.toJSON());
|
||||||
});
|
});
|
||||||
console.log({selected })
|
console.log({ selected });
|
||||||
refresh();
|
refresh();
|
||||||
}}
|
}}
|
||||||
schema={{
|
schema={{
|
||||||
@ -155,15 +164,13 @@ export const Menu: MenuType = observer((props) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/></Router>
|
/>
|
||||||
) : null,
|
</Router>,
|
||||||
sideMenuRef.current,
|
sideMenuRef.current,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
useMount(() => {
|
useMount(() => {
|
||||||
const defaultSelectedKey = defaultSelectedKeys
|
console.log({ defaultSelectedKey }, schema.properties);
|
||||||
? defaultSelectedKeys[0]
|
|
||||||
: null;
|
|
||||||
renderSideMenu(defaultSelectedKey);
|
renderSideMenu(defaultSelectedKey);
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
@ -197,13 +204,16 @@ const AddNewAction = () => {
|
|||||||
trigger={['click']}
|
trigger={['click']}
|
||||||
overlay={
|
overlay={
|
||||||
<AntdMenu>
|
<AntdMenu>
|
||||||
<AntdMenu.Item onClick={() => {
|
<AntdMenu.Item
|
||||||
insertBefore({
|
onClick={() => {
|
||||||
type: 'void',
|
insertBefore({
|
||||||
title: uid(),
|
type: 'void',
|
||||||
"x-component": 'Menu.Item',
|
title: uid(),
|
||||||
})
|
'x-component': 'Menu.Item',
|
||||||
}} style={{ minWidth: 150 }}>
|
});
|
||||||
|
}}
|
||||||
|
style={{ minWidth: 150 }}
|
||||||
|
>
|
||||||
<MenuOutlined /> 新建菜单
|
<MenuOutlined /> 新建菜单
|
||||||
</AntdMenu.Item>
|
</AntdMenu.Item>
|
||||||
<AntdMenu.Item>
|
<AntdMenu.Item>
|
||||||
@ -298,6 +308,25 @@ Menu.Item = observer((props: any) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Menu.Action = observer((props: any) => {
|
||||||
|
const { icon, ...others } = props;
|
||||||
|
const schema = useFieldSchema();
|
||||||
|
const { DesignableBar } = useDesignableBar();
|
||||||
|
return (
|
||||||
|
<Action
|
||||||
|
// @ts-ignore
|
||||||
|
eventKey={schema.name}
|
||||||
|
key={schema.name}
|
||||||
|
icon={icon ? <Icon type={icon as string} /> : undefined}
|
||||||
|
ButtonComponent={AntdMenu.Item}
|
||||||
|
{...others}
|
||||||
|
/>
|
||||||
|
// <Action {...others}/>
|
||||||
|
// <DesignableBar />
|
||||||
|
// </AntdMenu.Item>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
Menu.SubMenu = observer((props) => {
|
Menu.SubMenu = observer((props) => {
|
||||||
const { DesignableBar } = useDesignableBar();
|
const { DesignableBar } = useDesignableBar();
|
||||||
const schema = useFieldSchema();
|
const schema = useFieldSchema();
|
||||||
@ -372,11 +401,13 @@ Menu.DesignableBar = (props) => {
|
|||||||
const title = uid();
|
const title = uid();
|
||||||
field.title = title;
|
field.title = title;
|
||||||
field.componentProps['icon'] = 'DeleteOutlined';
|
field.componentProps['icon'] = 'DeleteOutlined';
|
||||||
schema['x-component-props'] = schema['x-component-props'] || {};
|
schema['x-component-props'] =
|
||||||
|
schema['x-component-props'] || {};
|
||||||
schema['x-component-props']['icon'] = 'DeleteOutlined';
|
schema['x-component-props']['icon'] = 'DeleteOutlined';
|
||||||
schema.title = title;
|
schema.title = title;
|
||||||
fieldSchema.title = title;
|
fieldSchema.title = title;
|
||||||
fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
|
fieldSchema['x-component-props'] =
|
||||||
|
fieldSchema['x-component-props'] || {};
|
||||||
fieldSchema['x-component-props']['icon'] = 'DeleteOutlined';
|
fieldSchema['x-component-props']['icon'] = 'DeleteOutlined';
|
||||||
refresh();
|
refresh();
|
||||||
}}
|
}}
|
||||||
@ -396,6 +427,9 @@ Menu.DesignableBar = (props) => {
|
|||||||
>
|
>
|
||||||
<DeleteOutlined /> 删除菜单
|
<DeleteOutlined /> 删除菜单
|
||||||
</AntdMenu.Item>
|
</AntdMenu.Item>
|
||||||
|
<AntdMenu.Item>
|
||||||
|
<ModalButton />
|
||||||
|
</AntdMenu.Item>
|
||||||
</AntdMenu>
|
</AntdMenu>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -406,4 +440,27 @@ 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;
|
||||||
|
@ -35,10 +35,12 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
border: 2px solid #1890ff;
|
border: 2px solid #1890ff;
|
||||||
|
pointer-events: none;
|
||||||
&.active {
|
&.active {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.designable-bar-actions {
|
.designable-bar-actions {
|
||||||
|
pointer-events: auto;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
line-height: 1rem;
|
line-height: 1rem;
|
||||||
@ -60,4 +62,16 @@
|
|||||||
right: -20px;
|
right: -20px;
|
||||||
border: 2px solid #1890ff;
|
border: 2px solid #1890ff;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-menu-title-content {
|
||||||
|
.ant-btn {
|
||||||
|
background: none;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
color: inherit;
|
||||||
|
padding: 0 20px;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0 -20px;
|
||||||
|
}
|
||||||
}
|
}
|
@ -14,7 +14,7 @@ function useAction() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const schema = {
|
const schema = {
|
||||||
name: `t_${uid()}`,
|
name: `table_${uid()}`,
|
||||||
type: 'array',
|
type: 'array',
|
||||||
'x-component': 'Table',
|
'x-component': 'Table',
|
||||||
// default: [
|
// default: [
|
||||||
@ -36,7 +36,7 @@ const schema = {
|
|||||||
// isRemoteDataSource: true,
|
// isRemoteDataSource: true,
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
[`a_${uid()}`]: {
|
[`action_bar_${uid()}`]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': 'Table.ActionBar',
|
'x-component': 'Table.ActionBar',
|
||||||
properties: {
|
properties: {
|
||||||
@ -132,22 +132,22 @@ const schema = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[`a_${uid()}`]: {
|
// [`a_${uid()}`]: {
|
||||||
type: 'void',
|
// type: 'void',
|
||||||
'x-component': 'Table.ActionBar',
|
// 'x-component': 'Table.ActionBar',
|
||||||
'x-component-props': {
|
// 'x-component-props': {
|
||||||
align: 'bottom',
|
// align: 'bottom',
|
||||||
},
|
// },
|
||||||
properties: {
|
// properties: {
|
||||||
pagination: {
|
// pagination: {
|
||||||
type: 'void',
|
// type: 'void',
|
||||||
'x-component': 'Table.Pagination',
|
// 'x-component': 'Table.Pagination',
|
||||||
'x-component-props': {
|
// 'x-component-props': {
|
||||||
defaultPageSize: 5,
|
// defaultPageSize: 5,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
[`a_${uid()}`]: {
|
[`a_${uid()}`]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': 'Table.ActionBar',
|
'x-component': 'Table.ActionBar',
|
||||||
@ -163,7 +163,7 @@ const schema = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[`c_${uid()}`]: {
|
[`column_${uid()}`]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '排序',
|
title: '排序',
|
||||||
'x-component': 'Table.Column',
|
'x-component': 'Table.Column',
|
||||||
@ -174,7 +174,7 @@ const schema = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[`c_${uid()}`]: {
|
[`column_${uid()}`]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '序号',
|
title: '序号',
|
||||||
'x-component': 'Table.Column',
|
'x-component': 'Table.Column',
|
||||||
@ -185,7 +185,7 @@ const schema = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[`c_${uid()}`]: {
|
[`column_${uid()}`]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '字段1',
|
title: '字段1',
|
||||||
'x-component': 'Table.Column',
|
'x-component': 'Table.Column',
|
||||||
@ -197,7 +197,7 @@ const schema = {
|
|||||||
field1: {
|
field1: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: true,
|
required: true,
|
||||||
// 'x-read-pretty': true,
|
'x-read-pretty': true,
|
||||||
'x-decorator-props': {
|
'x-decorator-props': {
|
||||||
feedbackLayout: 'popover',
|
feedbackLayout: 'popover',
|
||||||
},
|
},
|
||||||
@ -206,16 +206,17 @@ const schema = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[`c_${uid()}`]: {
|
[`column_${uid()}`]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '字段2',
|
title: '字段2',
|
||||||
'x-component': 'Table.Column',
|
'x-component': 'Table.Column',
|
||||||
|
'x-designable-bar': 'Table.Column.DesignableBar',
|
||||||
properties: {
|
properties: {
|
||||||
field2: {
|
field2: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
// title: '字段2',
|
// title: '字段2',
|
||||||
required: true,
|
required: true,
|
||||||
// 'x-read-pretty': true,
|
'x-read-pretty': true,
|
||||||
'x-decorator-props': {
|
'x-decorator-props': {
|
||||||
feedbackLayout: 'popover',
|
feedbackLayout: 'popover',
|
||||||
},
|
},
|
||||||
@ -224,18 +225,18 @@ const schema = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[`col_${uid()}`]: {
|
[`column_${uid()}`]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '操作',
|
title: '操作',
|
||||||
'x-component': 'Table.Column',
|
'x-component': 'Table.Column',
|
||||||
properties: {
|
properties: {
|
||||||
action1: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
name: 'action1',
|
name: 'action1',
|
||||||
title: '查看',
|
title: '查看',
|
||||||
|
|
||||||
'x-component': 'Action',
|
'x-component': 'Action',
|
||||||
'x-default-action': true,
|
'x-designable-bar': 'Table.Action.DesignableBar',
|
||||||
'x-designable-bar': 'Action.DesignableBar',
|
|
||||||
properties: {
|
properties: {
|
||||||
drawer1: {
|
drawer1: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
@ -285,13 +286,12 @@ const schema = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
action2: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
name: 'action1',
|
name: 'action1',
|
||||||
title: '修改',
|
title: '修改',
|
||||||
'x-component': 'Action',
|
'x-component': 'Action',
|
||||||
'x-default-action': true,
|
'x-designable-bar': 'Table.Action.DesignableBar',
|
||||||
'x-designable-bar': 'Action.DesignableBar',
|
|
||||||
properties: {
|
properties: {
|
||||||
drawer1: {
|
drawer1: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
@ -333,7 +333,7 @@ const schema = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
action3: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '删除',
|
title: '删除',
|
||||||
'x-component': 'Action',
|
'x-component': 'Action',
|
||||||
@ -341,12 +341,12 @@ const schema = {
|
|||||||
useAction: '{{ useTableDestroyAction }}',
|
useAction: '{{ useTableDestroyAction }}',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
action4: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '...',
|
title: '...',
|
||||||
'x-component': 'Action.Dropdown',
|
'x-component': 'Action.Dropdown',
|
||||||
properties: {
|
properties: {
|
||||||
action5: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '操作 1',
|
title: '操作 1',
|
||||||
'x-component': 'Action',
|
'x-component': 'Action',
|
||||||
@ -355,21 +355,22 @@ const schema = {
|
|||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
action4: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '操作 2',
|
title: '操作 2',
|
||||||
'x-component': 'Action',
|
'x-component': 'Action',
|
||||||
|
'x-default-action': true,
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
useAction,
|
useAction,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
action1: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
name: 'action1',
|
name: 'action1',
|
||||||
title: '查看',
|
title: '查看',
|
||||||
'x-component': 'Action',
|
'x-component': 'Action',
|
||||||
// 'x-default-action': true,
|
// 'x-default-action': true,
|
||||||
'x-designable-bar': 'Action.DesignableBar',
|
'x-designable-bar': 'Table.Action.DesignableBar',
|
||||||
properties: {
|
properties: {
|
||||||
drawer1: {
|
drawer1: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
@ -419,13 +420,12 @@ const schema = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
action2: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
name: 'action1',
|
name: 'action1',
|
||||||
title: '修改',
|
title: '修改',
|
||||||
'x-component': 'Action',
|
'x-component': 'Action',
|
||||||
'x-default-action': true,
|
'x-designable-bar': 'Table.Action.DesignableBar',
|
||||||
'x-designable-bar': 'Action.DesignableBar',
|
|
||||||
properties: {
|
properties: {
|
||||||
drawer1: {
|
drawer1: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
@ -467,7 +467,7 @@ const schema = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
action3: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '删除',
|
title: '删除',
|
||||||
'x-component': 'Action',
|
'x-component': 'Action',
|
||||||
@ -482,17 +482,14 @@ const schema = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const form = createForm();
|
const form = createForm({
|
||||||
|
// designable: true,
|
||||||
|
});
|
||||||
|
|
||||||
export default observer(() => {
|
export default observer(() => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SchemaRenderer form={form} schema={schema} />
|
<SchemaRenderer form={form} schema={schema} />
|
||||||
{/* <Editor
|
|
||||||
height="200px"
|
|
||||||
defaultLanguage="json"
|
|
||||||
value={JSON.stringify(form.values, null, 2)}
|
|
||||||
/> */}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
useForm,
|
useForm,
|
||||||
FormProvider,
|
FormProvider,
|
||||||
createSchemaField,
|
createSchemaField,
|
||||||
|
SchemaOptionsContext,
|
||||||
} from '@formily/react';
|
} from '@formily/react';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@ -16,24 +17,32 @@ import {
|
|||||||
Space,
|
Space,
|
||||||
Spin,
|
Spin,
|
||||||
Table as AntdTable,
|
Table as AntdTable,
|
||||||
|
Dropdown,
|
||||||
|
Menu,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { findIndex } from 'lodash';
|
import { findIndex, get } from 'lodash';
|
||||||
import constate from 'constate';
|
import constate from 'constate';
|
||||||
import useRequest from '@ahooksjs/use-request';
|
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 { useVisibleContext } from '../action';
|
||||||
import { SortableHandle, SortableContainer, SortableElement } from 'react-sortable-hoc'
|
import {
|
||||||
|
SortableHandle,
|
||||||
|
SortableContainer,
|
||||||
|
SortableElement,
|
||||||
|
} from 'react-sortable-hoc';
|
||||||
import cls from 'classnames';
|
import cls from 'classnames';
|
||||||
|
import { getSchemaPath, useDesignable, useSchemaPath } from '../DesignableSchemaField';
|
||||||
|
import './style.less';
|
||||||
|
|
||||||
interface TableRowProps {
|
interface TableRowProps {
|
||||||
index: number;
|
index: number;
|
||||||
data: any;
|
data: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SortableRow = SortableElement((props: any) => <tr {...props} />)
|
const SortableRow = SortableElement((props: any) => <tr {...props} />);
|
||||||
const SortableBody = SortableContainer((props: any) => <tbody {...props} />)
|
const SortableBody = SortableContainer((props: any) => <tbody {...props} />);
|
||||||
|
|
||||||
const TableRowContext = createContext<TableRowProps>(null);
|
const TableRowContext = createContext<TableRowProps>(null);
|
||||||
|
|
||||||
@ -105,7 +114,8 @@ function useTableActionBars() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function useTableColumns(props?: any) {
|
function useTableColumns(props?: any) {
|
||||||
const schema = useFieldSchema();
|
const { schema } = useDesignable();
|
||||||
|
// const schema = useFieldSchema();
|
||||||
const { dataSource } = props || {};
|
const { dataSource } = props || {};
|
||||||
|
|
||||||
function findColumns(schema: Schema): Schema[] {
|
function findColumns(schema: Schema): Schema[] {
|
||||||
@ -120,7 +130,7 @@ function useTableColumns(props?: any) {
|
|||||||
return findColumns(schema).map((item) => {
|
return findColumns(schema).map((item) => {
|
||||||
const columnProps = item['x-component-props'] || {};
|
const columnProps = item['x-component-props'] || {};
|
||||||
return {
|
return {
|
||||||
title: item.title,
|
title: <RecursionField name={item.name} schema={item} onlyRenderSelf />,
|
||||||
dataIndex: item.name,
|
dataIndex: item.name,
|
||||||
...columnProps,
|
...columnProps,
|
||||||
render(value, record, recordIndex) {
|
render(value, record, recordIndex) {
|
||||||
@ -282,23 +292,22 @@ const TableContainer = observer((props) => {
|
|||||||
useTableContext();
|
useTableContext();
|
||||||
const rowKey = field.componentProps.rowKey || 'id';
|
const rowKey = field.componentProps.rowKey || 'id';
|
||||||
const defaultAction = useDefaultAction();
|
const defaultAction = useDefaultAction();
|
||||||
console.log({ defaultAction });
|
|
||||||
const dataSource = Array.isArray(field.value) ? field.value.slice() : [];
|
const dataSource = Array.isArray(field.value) ? field.value.slice() : [];
|
||||||
const columns = useTableColumns({ dataSource });
|
const columns = useTableColumns({ dataSource });
|
||||||
const ref = useRef<HTMLDivElement>()
|
const ref = useRef<HTMLDivElement>();
|
||||||
const addTdStyles = (node: HTMLElement) => {
|
const addTdStyles = (node: HTMLElement) => {
|
||||||
const helper = document.body.querySelector(`.nb-table-sort-helper`)
|
const helper = document.body.querySelector(`.nb-table-sort-helper`);
|
||||||
if (helper) {
|
if (helper) {
|
||||||
const tds = node.querySelectorAll('td')
|
const tds = node.querySelectorAll('td');
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
helper.querySelectorAll('td').forEach((td, index) => {
|
helper.querySelectorAll('td').forEach((td, index) => {
|
||||||
if (tds[index]) {
|
if (tds[index]) {
|
||||||
td.style.width = getComputedStyle(tds[index]).width
|
td.style.width = getComputedStyle(tds[index]).width;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<div ref={ref} className={'nb-table'}>
|
<div ref={ref} className={'nb-table'}>
|
||||||
{actionBars.top.map((actionBarSchema) => {
|
{actionBars.top.map((actionBarSchema) => {
|
||||||
@ -339,10 +348,10 @@ const TableContainer = observer((props) => {
|
|||||||
// disableAutoscroll
|
// disableAutoscroll
|
||||||
helperClass={`nb-table-sort-helper`}
|
helperClass={`nb-table-sort-helper`}
|
||||||
helperContainer={() => {
|
helperContainer={() => {
|
||||||
return ref.current?.querySelector('tbody')
|
return ref.current?.querySelector('tbody');
|
||||||
}}
|
}}
|
||||||
onSortStart={({ node }) => {
|
onSortStart={({ node }) => {
|
||||||
addTdStyles(node)
|
addTdStyles(node);
|
||||||
}}
|
}}
|
||||||
onSortEnd={({ oldIndex, newIndex }) => {
|
onSortEnd={({ oldIndex, newIndex }) => {
|
||||||
field.move(oldIndex, newIndex);
|
field.move(oldIndex, newIndex);
|
||||||
@ -352,13 +361,11 @@ const TableContainer = observer((props) => {
|
|||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
row: (props: any) => {
|
row: (props: any) => {
|
||||||
const index = findIndex(field.value, item => item[rowKey] === props['data-row-key']);
|
const index = findIndex(
|
||||||
return (
|
field.value,
|
||||||
<SortableRow
|
(item) => item[rowKey] === props['data-row-key'],
|
||||||
index={index}
|
);
|
||||||
{...props}
|
return <SortableRow index={index} {...props} />;
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
@ -366,20 +373,17 @@ const TableContainer = observer((props) => {
|
|||||||
const index = dataSource.indexOf(data);
|
const index = dataSource.indexOf(data);
|
||||||
return {
|
return {
|
||||||
onClick(e) {
|
onClick(e) {
|
||||||
console.log('onRow', (e.target as HTMLElement), (e.target as HTMLElement).classList.contains('ant-table-cell'));
|
|
||||||
if (!(e.target as HTMLElement).classList.contains('ant-table-cell')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!defaultAction) {
|
if (!defaultAction) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// console.log('defaultAction');
|
const el = (e.target as HTMLElement);
|
||||||
field
|
if (
|
||||||
.query(`.${schema.name}.${index}.${defaultAction.name}`)
|
!el.classList.contains('ant-table-cell')
|
||||||
.take((f) => {
|
) {
|
||||||
const setVisible = f.componentProps.setVisible;
|
return;
|
||||||
setVisible && setVisible(true);
|
}
|
||||||
});
|
const btn = el.parentElement.querySelector<HTMLElement>(`.name-${defaultAction.name}`);
|
||||||
|
btn && btn.click();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
@ -411,7 +415,99 @@ export const Table: any = observer((props) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Table.Column = () => null;
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Table.Column = observer((props) => {
|
||||||
|
const schema = useFieldSchema();
|
||||||
|
const field = useField();
|
||||||
|
console.log('Table.Column', schema, field.title);
|
||||||
|
const { DesignableBar } = useDesignableBar();
|
||||||
|
return (
|
||||||
|
<div className={'nb-table-column'}>
|
||||||
|
{field.title}
|
||||||
|
<DesignableBar />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Table.Column.DesignableBar = () => {
|
||||||
|
const field = useField();
|
||||||
|
// const fieldSchema = useFieldSchema();
|
||||||
|
const { schema, remove, refresh, insertAfter } = useDesignable();
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
console.log('Table.Column.DesignableBar', { schema });
|
||||||
|
return (
|
||||||
|
<div className={cls('designable-bar', { active: visible })}>
|
||||||
|
<span
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
className={cls('designable-bar-actions', { active: visible })}
|
||||||
|
>
|
||||||
|
<Dropdown
|
||||||
|
trigger={['click']}
|
||||||
|
visible={visible}
|
||||||
|
onVisibleChange={(visible) => {
|
||||||
|
setVisible(visible);
|
||||||
|
}}
|
||||||
|
overlay={
|
||||||
|
<Menu>
|
||||||
|
<Menu.Item onClick={(e) => {
|
||||||
|
const title = uid();
|
||||||
|
field.title = title;
|
||||||
|
schema.title = title;
|
||||||
|
setVisible(false);
|
||||||
|
}}>点击修改按钮文案</Menu.Item>
|
||||||
|
<Menu.Item onClick={() => {
|
||||||
|
remove();
|
||||||
|
console.log('Table.Column.DesignableBar', { schema });
|
||||||
|
}}>删除列</Menu.Item>
|
||||||
|
<Menu.Item onClick={() => {
|
||||||
|
const name = uid();
|
||||||
|
insertAfter({
|
||||||
|
name: `column_${name}`,
|
||||||
|
type: 'void',
|
||||||
|
title: `字段 ${name}`,
|
||||||
|
'x-component': 'Table.Column',
|
||||||
|
'x-component-props': {
|
||||||
|
// title: 'z1',
|
||||||
|
},
|
||||||
|
'x-designable-bar': 'Table.Column.DesignableBar',
|
||||||
|
properties: {
|
||||||
|
[name]: {
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
// 'x-read-pretty': true,
|
||||||
|
'x-decorator-props': {
|
||||||
|
feedbackLayout: 'popover',
|
||||||
|
},
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}}>插入列</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<MenuOutlined />
|
||||||
|
</Dropdown>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
Table.ActionBar = observer((props) => {
|
Table.ActionBar = observer((props) => {
|
||||||
return (
|
return (
|
||||||
@ -447,17 +543,20 @@ const SortHandle = SortableHandle((props: any) => {
|
|||||||
className={cls(`nb-table-sort-handle`, props.className)}
|
className={cls(`nb-table-sort-handle`, props.className)}
|
||||||
style={{ ...props.style }}
|
style={{ ...props.style }}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}) as any
|
}) as any;
|
||||||
|
|
||||||
Table.SortHandle = observer((props) => {
|
Table.SortHandle = observer((props) => {
|
||||||
const field = useField<Formily.Core.Models.Field>();
|
const field = useField<Formily.Core.Models.Field>();
|
||||||
console.log('SortHandle', field.value);
|
console.log('SortHandle', field.value);
|
||||||
return <SortHandle {...props}/>;
|
return <SortHandle {...props} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
Table.Index = observer((props) => {
|
Table.Index = observer((props) => {
|
||||||
const index = useTableIndex();
|
const index = useTableIndex();
|
||||||
|
const schema = useFieldSchema();
|
||||||
|
const field = useField<Formily.Core.Models.Field>();
|
||||||
|
const path = useSchemaPath();
|
||||||
return <div>#{index + 1}</div>;
|
return <div>#{index + 1}</div>;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -484,3 +583,41 @@ Table.Addition = observer((props: any) => {
|
|||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Table.Action = () => null;
|
||||||
|
|
||||||
|
Table.Action.DesignableBar = () => {
|
||||||
|
const field = useField();
|
||||||
|
const path = useSchemaPath();
|
||||||
|
const { schema, remove, refresh, insertAfter } = useDesignable();
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
console.log('Table.Action.DesignableBar', path, field.address.entire, { schema, field });
|
||||||
|
return (
|
||||||
|
<div className={cls('designable-bar', { active: visible })}>
|
||||||
|
<span
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
className={cls('designable-bar-actions', { active: visible })}
|
||||||
|
>
|
||||||
|
<Dropdown
|
||||||
|
trigger={['click']}
|
||||||
|
visible={visible}
|
||||||
|
onVisibleChange={(visible) => {
|
||||||
|
setVisible(visible);
|
||||||
|
}}
|
||||||
|
overlay={
|
||||||
|
<Menu>
|
||||||
|
<Menu.Item onClick={(e) => {
|
||||||
|
schema.title = uid();
|
||||||
|
refresh();
|
||||||
|
}}>点击修改按钮文案</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<MenuOutlined />
|
||||||
|
</Dropdown>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
61
packages/client/src/schemas/table/style.less
Normal file
61
packages/client/src/schemas/table/style.less
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
.nb-table-column {
|
||||||
|
&:hover {
|
||||||
|
> .designable-bar {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .designable-bar {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
border: 2px solid #1890ff;
|
||||||
|
&.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.designable-bar-actions {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
line-height: 1rem;
|
||||||
|
background-color: #1890ff;
|
||||||
|
color: #fff;
|
||||||
|
z-index: 10;
|
||||||
|
padding: 0 3px;
|
||||||
|
.anticon {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-popover.nb-action-group {
|
||||||
|
z-index: 1000;
|
||||||
|
padding-top: 0;
|
||||||
|
.ant-popover-arrow {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.ant-popover-inner-content {
|
||||||
|
padding: 4px 0;
|
||||||
|
.ant-btn {
|
||||||
|
display: block;
|
||||||
|
min-width: 100px;
|
||||||
|
width: 100%;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
text-align: left;
|
||||||
|
padding: 5px 12px;
|
||||||
|
color: #000;
|
||||||
|
&:hover {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
&[disabled] {
|
||||||
|
color: #00000040;
|
||||||
|
background-color:#fff;
|
||||||
|
cursor:not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -161,33 +161,36 @@ function Database() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function LayoutWithMenu({ schema }) {
|
function LayoutWithMenu({ schema }) {
|
||||||
|
const match = useRouteMatch<any>();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const ref = useRef();
|
const sideMenuRef = useRef();
|
||||||
const [activeKey, setActiveKey] = useState('item3');
|
const [activeKey, setActiveKey] = useState(match.params.name);
|
||||||
schema['x-component-props']['defaultSelectedKeys'] = [activeKey];
|
const onSelect = (info) => {
|
||||||
schema['x-component-props']['onSelect'] = (info) => {
|
console.log('LayoutWithMenu', schema);
|
||||||
console.log('LayoutWithMenu', schema)
|
|
||||||
setActiveKey(info.key);
|
setActiveKey(info.key);
|
||||||
}
|
};
|
||||||
|
console.log({ match });
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Layout.Header>
|
<Layout.Header>
|
||||||
<MenuContainerContext.Provider value={{
|
<SchemaRenderer
|
||||||
sideMenuRef: ref,
|
schema={schema}
|
||||||
}}>
|
scope={{ sideMenuRef, onSelect, selectedKeys: [activeKey].filter(Boolean) }}
|
||||||
<SchemaRenderer schema={schema} />
|
/>
|
||||||
</MenuContainerContext.Provider>
|
</Layout.Header>
|
||||||
</Layout.Header>
|
<Layout>
|
||||||
<Layout>
|
<Layout.Sider
|
||||||
<Layout.Sider ref={ref} theme={'light'} width={200}>
|
ref={sideMenuRef}
|
||||||
</Layout.Sider>
|
theme={'light'}
|
||||||
<Layout.Content>
|
width={200}
|
||||||
{location.pathname}
|
></Layout.Sider>
|
||||||
<Content activeKey={activeKey}/>
|
<Layout.Content>
|
||||||
</Layout.Content>
|
{location.pathname}
|
||||||
</Layout>
|
<Content activeKey={activeKey} />
|
||||||
|
</Layout.Content>
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
</Layout>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Content({ activeKey }) {
|
function Content({ activeKey }) {
|
||||||
@ -207,7 +210,6 @@ function Content({ activeKey }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function AdminLayout({ route, children }: any) {
|
export function AdminLayout({ route, children }: any) {
|
||||||
const match = useRouteMatch<any>();
|
|
||||||
|
|
||||||
const { data = {}, loading } = useRequest(
|
const { data = {}, loading } = useRequest(
|
||||||
`/api/blocks:getSchema/${route.blockId}`,
|
`/api/blocks:getSchema/${route.blockId}`,
|
||||||
@ -220,9 +222,7 @@ export function AdminLayout({ route, children }: any) {
|
|||||||
return <Spin />;
|
return <Spin />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <LayoutWithMenu schema={data} />;
|
||||||
<LayoutWithMenu schema={data}/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AdminLayout;
|
export default AdminLayout;
|
||||||
|
Loading…
Reference in New Issue
Block a user