mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 14:16:26 +00:00
update docs
This commit is contained in:
parent
e50267d64c
commit
03da153052
@ -1,27 +0,0 @@
|
||||
---
|
||||
title: API - 接口
|
||||
nav:
|
||||
title: 组件
|
||||
path: /client
|
||||
order: 99
|
||||
group:
|
||||
order: 3
|
||||
title: 其他
|
||||
path: /client/others
|
||||
---
|
||||
|
||||
# API - 接口
|
||||
|
||||
## setGlobalState
|
||||
|
||||
## setGlobalActionState
|
||||
|
||||
## useGlobalState
|
||||
|
||||
## useGlobalAction
|
||||
|
||||
## refreshGlobalAction
|
||||
|
||||
## useAction
|
||||
|
||||
## doAction
|
@ -1,110 +0,0 @@
|
||||
---
|
||||
title: Drawer - 抽屉
|
||||
nav:
|
||||
title: 组件
|
||||
path: /client
|
||||
group:
|
||||
order: 3
|
||||
title: 其他
|
||||
path: /client/others
|
||||
---
|
||||
|
||||
# Drawer - 抽屉
|
||||
|
||||
通过 `Drawer.open(props)` 方法打开抽屉,无需预渲染,可在触发事件中使用。
|
||||
|
||||
### 基础抽屉
|
||||
|
||||
```tsx
|
||||
/**
|
||||
* title: 基础抽屉
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Button } from 'antd';
|
||||
import Drawer from './index.tsx';
|
||||
|
||||
export default () => (
|
||||
<Button onClick={() => {
|
||||
Drawer.open({
|
||||
title: 'Basic Drawer',
|
||||
content: () => {
|
||||
return (
|
||||
<div>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
});
|
||||
}}>Open Drawer</Button>
|
||||
);
|
||||
```
|
||||
|
||||
### 多层抽屉
|
||||
|
||||
```tsx
|
||||
/**
|
||||
* title: 多层抽屉
|
||||
* desc: 有多少层就执行多少个 `Drawer.open(props)`
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Button } from 'antd';
|
||||
import Drawer from './index.tsx';
|
||||
|
||||
export default () => (
|
||||
<Button onClick={() => {
|
||||
Drawer.open({
|
||||
title: 'Multi-level drawer',
|
||||
content: () => {
|
||||
return (
|
||||
<div>
|
||||
<Button onClick={() => {
|
||||
Drawer.open({
|
||||
title: 'Two-level Drawer',
|
||||
content: () => {
|
||||
return (
|
||||
<div>This is two-level drawer</div>
|
||||
)
|
||||
},
|
||||
});
|
||||
}}>Two-level Drawer</Button>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
});
|
||||
}}>Open drawer</Button>
|
||||
);
|
||||
```
|
||||
|
||||
### 关闭时提示
|
||||
|
||||
```tsx
|
||||
/**
|
||||
* title: 关闭时提示
|
||||
* desc: 通过 `closeWithConfirm(props)` 方法触发,更多参数请参考 `Modal.confirm`
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Button } from 'antd';
|
||||
import Drawer from './index.tsx';
|
||||
|
||||
export default () => (
|
||||
<Button onClick={() => {
|
||||
Drawer.open({
|
||||
title: 'Basic Drawer',
|
||||
content: ({ closeWithConfirm }) => {
|
||||
closeWithConfirm({
|
||||
title: '您确定关闭抽屉吗?'
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
});
|
||||
}}>Open Drawer</Button>
|
||||
);
|
||||
```
|
@ -1,206 +0,0 @@
|
||||
import React, { Fragment, useLayoutEffect, useRef, useState } from 'react';
|
||||
import ReactDOM, { createPortal } from 'react-dom';
|
||||
import { Modal, Drawer as AntdDrawer } from 'antd';
|
||||
import { DrawerProps } from 'antd/lib/drawer';
|
||||
import { useContext } from 'react';
|
||||
import { ConfigProvider } from 'antd';
|
||||
import zhCN from 'antd/lib/locale/zh_CN';
|
||||
import { ModalFuncProps } from 'antd/lib/modal/Modal'
|
||||
import isNum from 'lodash/isNumber';
|
||||
import isBool from 'lodash/isBoolean';
|
||||
import isStr from 'lodash/isString';
|
||||
import './style.less';
|
||||
|
||||
export const usePrefixCls = (
|
||||
tag?: string,
|
||||
props?: {
|
||||
prefixCls?: string;
|
||||
},
|
||||
) => {
|
||||
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
|
||||
return getPrefixCls(tag, props?.prefixCls);
|
||||
};
|
||||
|
||||
type DrawerTitle = string | number | React.ReactElement;
|
||||
|
||||
const isDrawerTitle = (props: any): props is DrawerTitle => {
|
||||
return (
|
||||
isNum(props) || isStr(props) || isBool(props) || React.isValidElement(props)
|
||||
);
|
||||
};
|
||||
|
||||
const getDrawerProps = (props: any): DrawerProps => {
|
||||
if (isDrawerTitle(props)) {
|
||||
return {
|
||||
title: props,
|
||||
};
|
||||
} else {
|
||||
return props;
|
||||
}
|
||||
};
|
||||
|
||||
const createElement = (content, props?: any) => {
|
||||
if (!content) {
|
||||
return null;
|
||||
}
|
||||
if (typeof content === 'string') {
|
||||
return content;
|
||||
}
|
||||
if (React.isValidElement(content)) {
|
||||
return content;
|
||||
}
|
||||
return React.createElement(content, props);
|
||||
};
|
||||
|
||||
export interface IDrawer {
|
||||
open(props?: any): void;
|
||||
close(): void;
|
||||
}
|
||||
|
||||
export function Drawer(title: DrawerProps, content: any): IDrawer;
|
||||
export function Drawer(title: DrawerTitle, content: any): IDrawer;
|
||||
export function Drawer(title: any, content: any): IDrawer {
|
||||
document.querySelectorAll('.env-root').forEach((el) => {
|
||||
el.className = 'env-root env-root-push';
|
||||
});
|
||||
const env = {
|
||||
root: document.createElement('div'),
|
||||
promise: null,
|
||||
};
|
||||
env.root.className = 'env-root';
|
||||
const props = getDrawerProps(title);
|
||||
const drawerProps = {
|
||||
width: '75%',
|
||||
...props,
|
||||
onClose: (e: any) => {
|
||||
props?.onClose?.(e);
|
||||
drawer.close();
|
||||
},
|
||||
afterVisibleChange: (visible: boolean) => {
|
||||
props?.afterVisibleChange?.(visible);
|
||||
if (visible) return;
|
||||
ReactDOM.unmountComponentAtNode(env.root);
|
||||
env.root?.parentNode?.removeChild(env.root);
|
||||
env.root = undefined;
|
||||
},
|
||||
};
|
||||
|
||||
const drawer: any = {
|
||||
open: (props: any) => {
|
||||
render(
|
||||
false,
|
||||
() => {
|
||||
drawer.closeWithConfirm = null;
|
||||
drawer.close();
|
||||
},
|
||||
() => {
|
||||
drawer.close();
|
||||
},
|
||||
);
|
||||
setTimeout(() => {
|
||||
render(
|
||||
true,
|
||||
() => {
|
||||
drawer.closeWithConfirm = null;
|
||||
drawer.close();
|
||||
},
|
||||
() => {
|
||||
drawer.close();
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
close: () => {
|
||||
if (!env.root) return;
|
||||
if (drawer.closeWithConfirm) {
|
||||
Modal.confirm({
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
...drawer.closeWithConfirm,
|
||||
onOk() {
|
||||
drawer.closeWithConfirm = null;
|
||||
const els = document.querySelectorAll('.env-root-push');
|
||||
if (els.length) {
|
||||
const last = els[els.length - 1];
|
||||
last.className = 'env-root';
|
||||
}
|
||||
render(false);
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const els = document.querySelectorAll('.env-root-push');
|
||||
if (els.length) {
|
||||
const last = els[els.length - 1];
|
||||
last.className = 'env-root';
|
||||
}
|
||||
render(false);
|
||||
}
|
||||
},
|
||||
closeWithConfirm: null,
|
||||
};
|
||||
|
||||
const closeWithConfirm = (props) => {
|
||||
drawer.closeWithConfirm = props;
|
||||
};
|
||||
|
||||
const render = (visible = true, resolve?: () => any, reject?: () => any) => {
|
||||
ReactDOM.render(
|
||||
<ConfigProvider locale={zhCN}>
|
||||
<AntdDrawer {...drawerProps} className={'nb-drawer'} visible={visible}>
|
||||
{createElement(content, {
|
||||
resolve,
|
||||
reject,
|
||||
closeWithConfirm,
|
||||
})}
|
||||
</AntdDrawer>
|
||||
</ConfigProvider>,
|
||||
env.root,
|
||||
);
|
||||
};
|
||||
document.body.appendChild(env.root);
|
||||
|
||||
return drawer;
|
||||
}
|
||||
|
||||
const DrawerFooter: React.FC = (props) => {
|
||||
const ref = useRef<HTMLDivElement>();
|
||||
const [footer, setFooter] = useState<HTMLDivElement>();
|
||||
const footerRef = useRef<HTMLDivElement>();
|
||||
const prefixCls = usePrefixCls('drawer');
|
||||
useLayoutEffect(() => {
|
||||
const content = ref.current?.closest(`.${prefixCls}-wrapper-body`);
|
||||
if (content) {
|
||||
if (!footerRef.current) {
|
||||
footerRef.current = content.querySelector(`.${prefixCls}-footer`);
|
||||
if (!footerRef.current) {
|
||||
footerRef.current = document.createElement('div');
|
||||
footerRef.current.classList.add(`${prefixCls}-footer`);
|
||||
content.appendChild(footerRef.current);
|
||||
}
|
||||
}
|
||||
setFooter(footerRef.current);
|
||||
}
|
||||
});
|
||||
|
||||
footerRef.current = footer;
|
||||
|
||||
return (
|
||||
<div ref={ref} style={{ display: 'none' }}>
|
||||
{footer && createPortal(props.children, footer)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface ContentPorps {
|
||||
resolve?: () => any;
|
||||
closeWithConfirm?: (props: ModalFuncProps) => any;
|
||||
}
|
||||
|
||||
Drawer.open = (props: DrawerProps & { content: (contentPorps?: ContentPorps) => any }) => {
|
||||
const { content, visible, ...others } = props;
|
||||
return Drawer(others, content).open({ visible });
|
||||
};
|
||||
|
||||
Drawer.Footer = DrawerFooter;
|
||||
|
||||
export default Drawer;
|
@ -1,18 +0,0 @@
|
||||
.env-root-push + style + div > div {
|
||||
transform: translateX(-10%);
|
||||
}
|
||||
.env-root-push + div > div {
|
||||
transform: translateX(-10%);
|
||||
}
|
||||
.ant-drawer.nb-drawer .ant-drawer-content-wrapper {
|
||||
width: 75%;
|
||||
max-width: 1000px;
|
||||
}
|
||||
@media only screen and (max-width: 800px) {
|
||||
.ant-drawer.nb-drawer .ant-drawer-content-wrapper {
|
||||
width: 100% !important;
|
||||
}
|
||||
.ant-drawer.noco-drawer .ant-drawer-content-wrapper {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
---
|
||||
title: AdminLayout - 后台布局
|
||||
nav:
|
||||
title: 组件
|
||||
path: /client
|
||||
group:
|
||||
order: 2
|
||||
title: Templates
|
||||
path: /client/templates
|
||||
---
|
||||
|
||||
# AdminLayout - 后台布局
|
||||
|
||||
内置的后台布局模板,提供了基础的菜单和路由切换。
|
@ -1,14 +0,0 @@
|
||||
---
|
||||
title: AuthLayout - 登录布局
|
||||
nav:
|
||||
title: 组件
|
||||
path: /client
|
||||
group:
|
||||
order: 2
|
||||
title: Templates
|
||||
path: /client/templates
|
||||
---
|
||||
|
||||
# AuthLayout - 登录布局
|
||||
|
||||
内置的登录、注册页布局
|
14
packages/client/src/demo.md
Normal file
14
packages/client/src/demo.md
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
title: 全站演示
|
||||
nav:
|
||||
title: 组件
|
||||
path: /client
|
||||
toc: menu
|
||||
group:
|
||||
order: 0
|
||||
title: 概念
|
||||
path: /client
|
||||
---
|
||||
|
||||
<code src="./demos/demo1.tsx"/>
|
||||
|
@ -1,14 +1,14 @@
|
||||
---
|
||||
title: 全站演示
|
||||
title: 介绍
|
||||
nav:
|
||||
title: 组件
|
||||
path: /client
|
||||
toc: menu
|
||||
group:
|
||||
order: 0
|
||||
title: 全站演示
|
||||
title: 概念
|
||||
path: /client
|
||||
---
|
||||
|
||||
<code src="./demos/demo1.tsx"/>
|
||||
# 介绍
|
||||
|
||||
|
@ -23,7 +23,7 @@ import { uid, clone } from '@formily/shared';
|
||||
import { ArrayCollapse, ArrayTable, FormLayout } from '@formily/antd';
|
||||
|
||||
import { Space, Card, Modal, Spin } from 'antd';
|
||||
import { Action, useLogin, useRegister, useSubmit } from '../action';
|
||||
import { Action, useLogin, useRegister, useSubmit, useDesignableValues } from '../action';
|
||||
import { AddNew } from '../add-new';
|
||||
import { Cascader } from '../cascader';
|
||||
import { Checkbox } from '../checkbox';
|
||||
@ -73,6 +73,7 @@ export const scope = {
|
||||
useTableFilterAction,
|
||||
useTableRow,
|
||||
useTableUpdateAction,
|
||||
useDesignableValues,
|
||||
};
|
||||
|
||||
export const components = {
|
||||
@ -363,8 +364,8 @@ export const createDesignableSchemaField = (options) => {
|
||||
}}
|
||||
>
|
||||
<SchemaField scope={props.scope} components={props.components} schema={schema} />
|
||||
<CodePreview schema={schema} />
|
||||
<FormValues />
|
||||
{props.debug && <CodePreview schema={schema} />}
|
||||
{props.debug && <FormValues />}
|
||||
</DesignableContext.Provider>
|
||||
);
|
||||
};
|
||||
@ -435,6 +436,7 @@ export interface SchemaRendererProps {
|
||||
onlyRenderProperties?: boolean;
|
||||
scope?: any;
|
||||
components?: any;
|
||||
debug?: boolean;
|
||||
}
|
||||
|
||||
export const SchemaRenderer = (props: SchemaRendererProps) => {
|
||||
@ -463,6 +465,7 @@ export const SchemaRenderer = (props: SchemaRendererProps) => {
|
||||
return (
|
||||
<FormProvider form={form}>
|
||||
<DesignableSchemaField
|
||||
debug={props.debug}
|
||||
scope={props.scope}
|
||||
components={props.components}
|
||||
onRefresh={props.onRefresh}
|
||||
|
61
packages/client/src/schemas/action/designableBar.tsx
Normal file
61
packages/client/src/schemas/action/designableBar.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
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,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
@ -11,8 +11,7 @@ group:
|
||||
|
||||
# Action - 操作
|
||||
|
||||
|
||||
## 组件节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
////////// 单节点 //////////
|
||||
@ -67,17 +66,16 @@ group:
|
||||
</Action.Dropdown>
|
||||
</pre>
|
||||
|
||||
操作有三类:
|
||||
## Designable Bar
|
||||
|
||||
- 常规操作:Action
|
||||
- 弹出层操作,弹出层可以是 Action.Drawer、Action.Modal、Action.Popover
|
||||
- 指定容器内打开: Action.Container
|
||||
- 下拉菜单:Action.Dropdown,用于收纳多种操作
|
||||
- 跳转操作:Action.Link、Action.URL
|
||||
- Action.DesignableBar
|
||||
- Action.Modal.DesignableBar
|
||||
- Action.Drawer.DesignableBar
|
||||
- Action.Popover.DesignableBar
|
||||
|
||||
Action.Drawer、Action.Modal、Action.Popover 和 Action.Container 需要和 Action 搭配使用
|
||||
## Examples
|
||||
|
||||
## Action - 常规操作
|
||||
### Action - 常规操作
|
||||
|
||||
```tsx
|
||||
/**
|
||||
@ -100,17 +98,18 @@ const schema = {
|
||||
name: 'action1',
|
||||
title: '按钮',
|
||||
'x-component': 'Action',
|
||||
'x-designable-bar': 'Action.DesignableBar',
|
||||
'x-component-props': {
|
||||
useAction,
|
||||
useAction: '{{ useAction }}',
|
||||
},
|
||||
};
|
||||
|
||||
export default () => {
|
||||
return <SchemaRenderer schema={schema} />
|
||||
return <SchemaRenderer debug={true} scope={{ useAction }} schema={schema} />
|
||||
}
|
||||
```
|
||||
|
||||
## Action.Link - 内链跳转
|
||||
### Action.Link - 内链跳转
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
@ -131,7 +130,7 @@ export default () => {
|
||||
}
|
||||
```
|
||||
|
||||
## Action.URL - 外链跳转
|
||||
### Action.URL - 外链跳转
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
@ -152,7 +151,7 @@ export default () => {
|
||||
}
|
||||
```
|
||||
|
||||
## Action.Dropdown - 下拉操作
|
||||
### Action.Dropdown - 下拉操作
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
@ -253,7 +252,7 @@ export default () => {
|
||||
}
|
||||
```
|
||||
|
||||
## Action.Popover - 打开气泡
|
||||
### Action.Popover - 打开气泡
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
@ -285,7 +284,7 @@ export default () => {
|
||||
}
|
||||
```
|
||||
|
||||
## Action.Drawer - 打开抽屉
|
||||
### Action.Drawer - 打开抽屉
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
@ -338,10 +337,13 @@ export default () => {
|
||||
}
|
||||
```
|
||||
|
||||
## Action.Modal - 打开对话框
|
||||
### Action.Modal - 打开对话框
|
||||
|
||||
`x-decorator='Form'` 时,Model 为 Form,并自带按钮
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
import { Space } from 'antd';
|
||||
import { SchemaRenderer } from '../';
|
||||
import { useVisibleContext } from './';
|
||||
|
||||
@ -406,12 +408,40 @@ const schema = {
|
||||
},
|
||||
};
|
||||
|
||||
const schema2 = {
|
||||
type: 'void',
|
||||
name: 'action1',
|
||||
title: 'x-decorator=Form',
|
||||
'x-component': 'Action',
|
||||
properties: {
|
||||
drawer1: {
|
||||
type: 'void',
|
||||
title: '弹窗标题',
|
||||
'x-component': 'Action.Modal',
|
||||
'x-component-props': {},
|
||||
'x-decorator': 'Form',
|
||||
properties: {
|
||||
input: {
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default () => {
|
||||
return <SchemaRenderer schema={schema} />
|
||||
return (
|
||||
<Space>
|
||||
<SchemaRenderer schema={schema} />
|
||||
<SchemaRenderer schema={schema2} />
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Action.Container - 指定容器内打开
|
||||
### Action.Container - 指定容器内打开
|
||||
|
||||
```tsx
|
||||
import React, { useRef } from 'react';
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
SchemaOptionsContext,
|
||||
Schema,
|
||||
useField,
|
||||
SchemaExpressionScopeContext,
|
||||
} from '@formily/react';
|
||||
import { Button, Dropdown, Menu, Popover, Space, Drawer, Modal } from 'antd';
|
||||
import { Link, useHistory, LinkProps } from 'react-router-dom';
|
||||
@ -22,6 +23,9 @@ import { uid } from '@formily/shared';
|
||||
|
||||
import './style.less';
|
||||
import constate from 'constate';
|
||||
import { Icon } from '../icon-picker';
|
||||
import { useMemo } from 'react';
|
||||
import { createForm } from '@formily/core';
|
||||
|
||||
export function useDefaultAction() {
|
||||
return {
|
||||
@ -109,6 +113,7 @@ const BaseAction = observer((props: any) => {
|
||||
const {
|
||||
ButtonComponent = Button,
|
||||
className,
|
||||
icon,
|
||||
useAction = useDefaultAction,
|
||||
...others
|
||||
} = props;
|
||||
@ -122,11 +127,10 @@ const BaseAction = observer((props: any) => {
|
||||
field.componentProps.setVisible = setVisible;
|
||||
}, []);
|
||||
|
||||
console.log('BaseAction', { field, schema, fieldSchema }, field.title);
|
||||
|
||||
const renderButton = () => (
|
||||
<ButtonComponent
|
||||
{...others}
|
||||
icon={<Icon type={icon} />}
|
||||
className={classNames(className, `name-${schema.name}`)}
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation && e.stopPropagation();
|
||||
@ -227,7 +231,24 @@ Action.Popover = observer((props) => {
|
||||
|
||||
Action.Drawer = observer((props) => {
|
||||
const field = useField();
|
||||
// const { visible, setVisible } = useVisibleContext();
|
||||
const { useAction = () => ({ async run() {} }), ...others } = props;
|
||||
const { schema } = useDesignable();
|
||||
const { visible, setVisible } = useVisibleContext();
|
||||
const { run } = useAction();
|
||||
const form = useForm();
|
||||
console.log(`schema['x-decorator']`, schema['x-decorator'])
|
||||
if (schema['x-decorator'] === 'Form.Decorator') {
|
||||
Object.assign(others, {
|
||||
footer: (
|
||||
<Space>
|
||||
<Button type={'primary'}>Ok</Button>
|
||||
<Button>Cancel</Button>
|
||||
</Space>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
onClick={(e) => {
|
||||
@ -235,7 +256,7 @@ Action.Drawer = observer((props) => {
|
||||
}}
|
||||
title={field.title}
|
||||
width={'50%'}
|
||||
{...props}
|
||||
{...others}
|
||||
visible={visible}
|
||||
onClose={(e) => {
|
||||
e.stopPropagation();
|
||||
@ -247,103 +268,142 @@ Action.Drawer = observer((props) => {
|
||||
);
|
||||
});
|
||||
|
||||
const [DesignableContextProvider, useDesignableContext] = constate(() => {
|
||||
return useDesignable();
|
||||
});
|
||||
|
||||
import { DesignableBar } from './designableBar';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { clone } from '@formily/shared';
|
||||
|
||||
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 field = useField();
|
||||
const { useAction = () => ({ async run() {} }), ...others } = props;
|
||||
const { schema } = useDesignable();
|
||||
const { setVisible: setDropdownVisible } = useDropdownVisibleContext();
|
||||
const { visible, setVisible } = useVisibleContext();
|
||||
const { run } = useAction();
|
||||
const form = useForm();
|
||||
if (schema['x-decorator'] !== 'Form.Decorator') {
|
||||
Object.assign(others, { footer: null });
|
||||
}
|
||||
return (
|
||||
<Modal
|
||||
// onClick={e => {
|
||||
// e.stopPropagation();
|
||||
// }}
|
||||
title={field.title}
|
||||
title={schema.title}
|
||||
width={'50%'}
|
||||
{...props}
|
||||
{...others}
|
||||
visible={visible}
|
||||
closable
|
||||
maskClosable
|
||||
onCancel={(e) => {
|
||||
destroyOnClose
|
||||
onCancel={async (e) => {
|
||||
e.stopPropagation();
|
||||
setVisible(false);
|
||||
setDropdownVisible && setDropdownVisible(false);
|
||||
}}
|
||||
onOk={async (e) => {
|
||||
console.log('onOk', form.values);
|
||||
// await form.submit();
|
||||
await run();
|
||||
// e.stopPropagation();
|
||||
setVisible(false);
|
||||
setDropdownVisible && setDropdownVisible(false);
|
||||
}}
|
||||
footer={null}
|
||||
>
|
||||
<div onClick={(e) => e.stopPropagation()}>{props.children}</div>
|
||||
<div
|
||||
//onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
});
|
||||
|
||||
Action.Dropdown = observer((props) => {
|
||||
// const { visible, setVisible } = useVisibleContext();
|
||||
const [DropdownVisibleProvider, useDropdownVisibleContext] = constate((props: any = {}) => {
|
||||
const { initialVisible = false } = props;
|
||||
const [visible, setVisible] = useState(initialVisible);
|
||||
|
||||
return { visible, setVisible };
|
||||
});
|
||||
|
||||
const ActionDropdown = observer((props: any) => {
|
||||
const { icon, ...others } = props;
|
||||
const schema = useFieldSchema();
|
||||
const { visible, setVisible } = useDropdownVisibleContext();
|
||||
return (
|
||||
<>
|
||||
<Popover
|
||||
// visible={visible}
|
||||
// onVisibleChange={(visible) => {
|
||||
// setVisible(visible);
|
||||
// }}
|
||||
{...props}
|
||||
visible={visible}
|
||||
onVisibleChange={(visible) => {
|
||||
setVisible(visible);
|
||||
}}
|
||||
// {...props}
|
||||
overlayClassName={'nb-action-group'}
|
||||
// trigger={'click'}
|
||||
content={props.children}
|
||||
placement={'bottomLeft'}
|
||||
>
|
||||
<Button>{schema.title}</Button>
|
||||
<Button icon={<Icon type={icon} />} {...others}>
|
||||
{schema.title}
|
||||
</Button>
|
||||
</Popover>
|
||||
{/* popover 的按钮初始化时并未渲染,暂时先这么处理 */}
|
||||
<div style={{display: 'none'}}>{props.children}</div>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
Action.Dropdown = observer((props) => {
|
||||
return (
|
||||
<DropdownVisibleProvider>
|
||||
<ActionDropdown {...props} />
|
||||
<div style={{ display: 'none' }}>{props.children}</div>
|
||||
</DropdownVisibleProvider>
|
||||
);
|
||||
});
|
||||
|
||||
Action.DesignableBar = () => {
|
||||
const field = useField();
|
||||
// const schema = useFieldSchema();
|
||||
const { schema, insertAfter } = useDesignable();
|
||||
const { schema, insertAfter, refresh } = useDesignable();
|
||||
const [visible, setVisible] = useState(false);
|
||||
return (
|
||||
<div className={classNames('designable-bar', { active: visible })}>
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
className={classNames('designable-bar-actions', { active: visible })}
|
||||
>
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
visible={visible}
|
||||
onVisibleChange={(visible) => {
|
||||
setVisible(visible);
|
||||
<DesignableContextProvider>
|
||||
<div className={classNames('designable-bar', { active: visible })}>
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
overlay={
|
||||
<Menu>
|
||||
<Menu.Item
|
||||
onClick={(e) => {
|
||||
console.log({ field, schema });
|
||||
schema.title = '按钮文案被修改了';
|
||||
field.setTitle('按钮文案被修改了');
|
||||
schema.properties.drawer1.title = '抽屉标题文案被修改了';
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
点击修改按钮文案
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
insertAfter({
|
||||
name: uid(),
|
||||
'x-component': 'Input',
|
||||
});
|
||||
}}
|
||||
>
|
||||
insertAfter
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
}
|
||||
className={classNames('designable-bar-actions', { active: visible })}
|
||||
>
|
||||
<MenuOutlined />
|
||||
</Dropdown>
|
||||
</span>
|
||||
</div>
|
||||
<SchemaRenderer schema={DesignableBar.Action} />
|
||||
</span>
|
||||
</div>
|
||||
</DesignableContextProvider>
|
||||
);
|
||||
};
|
||||
|
@ -80,3 +80,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.designable-bar-actions {
|
||||
.ant-btn {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
background: none;
|
||||
height: auto;
|
||||
line-height: 1em;
|
||||
width: auto;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ group:
|
||||
|
||||
# AddNew - 新增
|
||||
|
||||
## 组件节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
// 简单场景,只支持上下拖拽布局
|
||||
@ -24,7 +24,7 @@ group:
|
||||
|
||||
// 栅格布局,支持行列等复杂的拖拽操作
|
||||
<Grid>
|
||||
<Grid.Row>
|
||||
<Grid.Row locked>
|
||||
<Grid.Col>
|
||||
// 常规区块
|
||||
<AddNew.BlockItem/>
|
||||
@ -35,7 +35,11 @@ group:
|
||||
</Grid>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
暂无
|
||||
|
||||
## Examples
|
||||
|
||||
### AddNew.BlockItem
|
||||
|
||||
|
26
packages/client/src/schemas/block-item/index.md
Normal file
26
packages/client/src/schemas/block-item/index.md
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
title: BlockItem - 区块项
|
||||
nav:
|
||||
title: 组件
|
||||
path: /client
|
||||
group:
|
||||
order: 1
|
||||
title: Schemas
|
||||
path: /client/schemas
|
||||
---
|
||||
|
||||
# BlockItem - 区块项
|
||||
|
||||
## Node Tree
|
||||
|
||||
通常 BlockItem 并不单独占用一个节点,而是节点的 x-decorator
|
||||
|
||||
<pre lang="tsx">
|
||||
<Table x-decorator={'BlockItem'}/>
|
||||
</pre>
|
||||
|
||||
BlockItem 的作用主要用于处理 DesignableBar,如:
|
||||
|
||||
<pre lang="tsx">
|
||||
<Table x-decorator={'BlockItem'} x-designable-bar={'Table.DesignableBar'}/>
|
||||
</pre>
|
@ -11,13 +11,17 @@ group:
|
||||
|
||||
# Cascader - 级联选择
|
||||
|
||||
## 组件节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
<Cascader/>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
- Cascader.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
### 省市区级联
|
||||
|
||||
|
@ -11,7 +11,7 @@ group:
|
||||
|
||||
# Checkbox - 多选框
|
||||
|
||||
## 组件节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
// 勾选
|
||||
@ -20,7 +20,11 @@ group:
|
||||
<Checkbox.Group/>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
- Checkbox.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
### 勾选
|
||||
|
||||
|
@ -11,13 +11,17 @@ group:
|
||||
|
||||
# ColorSelect - 颜色选择器
|
||||
|
||||
## 组件节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
<ColorSelect/>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
- ColorSelect.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
### 颜色选择器
|
||||
|
||||
|
@ -11,20 +11,23 @@ group:
|
||||
|
||||
# DatabaseField - 数据表字段
|
||||
|
||||
## 组件节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
<DatabaseField/>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
暂无
|
||||
|
||||
## Examples
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
import { createForm } from '@formily/core';
|
||||
import { SchemaRenderer } from '../';
|
||||
import { observer, connect, useField } from '@formily/react';
|
||||
import Editor from '@monaco-editor/react';
|
||||
|
||||
const schema = {
|
||||
type: 'object',
|
||||
@ -66,11 +69,6 @@ export default observer(() => {
|
||||
return (
|
||||
<div>
|
||||
<SchemaRenderer form={form} schema={schema} />
|
||||
<Editor
|
||||
height="200px"
|
||||
defaultLanguage="json"
|
||||
value={JSON.stringify(form.values, null, 2)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
@ -11,13 +11,17 @@ group:
|
||||
|
||||
# DatePicker - 日期选择器
|
||||
|
||||
## 组件节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
<DatePicker/>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
- DatePicker.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
### 日期选择器
|
||||
|
||||
|
25
packages/client/src/schemas/designable-bar/index.md
Normal file
25
packages/client/src/schemas/designable-bar/index.md
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
title: DesignableBar - 配置操作栏
|
||||
nav:
|
||||
title: 组件
|
||||
path: /client
|
||||
group:
|
||||
order: 1
|
||||
title: Schemas
|
||||
path: /client/schemas
|
||||
---
|
||||
|
||||
# DesignableBar - 配置操作栏
|
||||
|
||||
用于修改节点的参数配置,DesignableBar 是所有配置操作栏的统称,不是固定的,可以根据不同节点定义不同的 `**.DesignableBar`。
|
||||
|
||||
## Node Tree
|
||||
|
||||
通常 DesignableBar 并不单独占用一个节点,而是节点的 x-designable-bar
|
||||
|
||||
<pre lang="tsx">
|
||||
<Table x-decorator={'BlockItem'} x-designable-bar={'Table.DesignableBar'}/>
|
||||
<Action x-designable-bar={'Action.DesignableBar'}/>
|
||||
</pre>
|
||||
|
||||
数据组件的 DesignableBar 通常与 FormItem 搭配使用,非数据组件的 DesignableBar 通常与 BlockItem 搭配使用,也有少量如 Action、Menu 无需 BlockItem
|
@ -11,7 +11,7 @@ group:
|
||||
|
||||
# Filter - 筛选器
|
||||
|
||||
## 组件节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
<Filter>
|
||||
@ -24,16 +24,11 @@ group:
|
||||
</Filter>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
<!-- <Filter>
|
||||
<Filter.Column title={'字段1'} operations={[]}>
|
||||
<Input/>
|
||||
</Filter.Column>
|
||||
<Filter.Column title={'字段2'}>
|
||||
<Input/>
|
||||
</Filter.Column>
|
||||
</Filter> -->
|
||||
- Filter.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
|
26
packages/client/src/schemas/form-item/index.md
Normal file
26
packages/client/src/schemas/form-item/index.md
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
title: FormItem - 表单项
|
||||
nav:
|
||||
title: 组件
|
||||
path: /client
|
||||
group:
|
||||
order: 1
|
||||
title: Schemas
|
||||
path: /client/schemas
|
||||
---
|
||||
|
||||
# FormItem - 表单项
|
||||
|
||||
## Node Tree
|
||||
|
||||
通常 FormItem 并不单独占用一个节点,而是节点的 x-decorator
|
||||
|
||||
<pre lang="tsx">
|
||||
<Input x-decorator={'FormItem'}/>
|
||||
</pre>
|
||||
|
||||
FormItem 的作用主要用于处理表单项(控件)的校验、布局等
|
||||
|
||||
<pre lang="tsx">
|
||||
<Input x-decorator={'FormItem'} x-designable-bar={'Input.DesignableBar'}/>
|
||||
</pre>
|
@ -11,7 +11,7 @@ group:
|
||||
|
||||
# Form - 表单
|
||||
|
||||
## 组件节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
<Form>
|
||||
@ -19,9 +19,15 @@ group:
|
||||
<Select/>
|
||||
// 添加其他节点
|
||||
</Form>
|
||||
|
||||
<Table x-decorator={'Form'}/>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
- Form.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
### 常规表单
|
||||
|
||||
@ -161,7 +167,7 @@ import { uid } from '@formily/shared';
|
||||
const schema = {
|
||||
type: 'void',
|
||||
name: uid(),
|
||||
'x-decorator': 'Card',
|
||||
'x-decorator': 'BlockItem',
|
||||
'x-component': 'Form',
|
||||
'x-designable-bar': 'Form.DesignableBar',
|
||||
properties: {
|
||||
|
@ -6,6 +6,8 @@ import {
|
||||
useFieldSchema,
|
||||
observer,
|
||||
SchemaExpressionScopeContext,
|
||||
FormProvider,
|
||||
ISchema,
|
||||
} from '@formily/react';
|
||||
import { SchemaRenderer, useDesignable } from '../DesignableSchemaField';
|
||||
import get from 'lodash/get';
|
||||
@ -42,7 +44,6 @@ function useDefaultValues() {
|
||||
|
||||
export const Form: any = observer((props: any) => {
|
||||
const scope = useContext(SchemaExpressionScopeContext);
|
||||
|
||||
const { useValues = useDefaultValues } = props;
|
||||
const values = useValues();
|
||||
const form = useMemo(() => {
|
||||
@ -54,37 +55,37 @@ export const Form: any = observer((props: any) => {
|
||||
}, [values]);
|
||||
const schema = useFieldSchema();
|
||||
const { schema: designableSchema, refresh } = useDesignable();
|
||||
const { DesignableBar } = useDesignableBar();
|
||||
const ref = useRef();
|
||||
const [active, setActive] = useState(false);
|
||||
const { onMouseEnter, onMouseLeave, onMouseMove } = useMouseEvents(ref);
|
||||
onMouseEnter((e: React.MouseEvent) => {
|
||||
setActive(true);
|
||||
});
|
||||
|
||||
onMouseLeave((e: React.MouseEvent) => {
|
||||
setActive(false);
|
||||
});
|
||||
|
||||
onMouseMove((e: React.MouseEvent) => {});
|
||||
const formSchema: ISchema = schema['x-decorator'] === 'Form' ? {
|
||||
type: 'void',
|
||||
"x-component": 'Blank',
|
||||
properties: {
|
||||
[schema.name]: {
|
||||
...schema.toJSON(),
|
||||
"x-decorator": 'Form.Decorator',
|
||||
}
|
||||
}
|
||||
} : {
|
||||
...schema.toJSON(),
|
||||
'x-component': 'Form.Blank',
|
||||
'x-component-props': {},
|
||||
};
|
||||
return (
|
||||
<div ref={ref} className={'nb-form'}>
|
||||
<SchemaRenderer
|
||||
scope={scope}
|
||||
// components={options.components}
|
||||
onRefresh={(subSchema: Schema) => {
|
||||
designableSchema.properties = subSchema.properties;
|
||||
refresh();
|
||||
}}
|
||||
form={form}
|
||||
schema={schema.toJSON()}
|
||||
onlyRenderProperties
|
||||
/>
|
||||
<DesignableBar active={active} />
|
||||
</div>
|
||||
<SchemaRenderer
|
||||
scope={scope}
|
||||
// components={options.components}
|
||||
onRefresh={(subSchema: Schema) => {
|
||||
designableSchema.properties = subSchema.properties;
|
||||
refresh();
|
||||
}}
|
||||
form={form}
|
||||
schema={formSchema}
|
||||
onlyRenderProperties
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
Form.Decorator = ({children}) => children;
|
||||
|
||||
Form.DesignableBar = (props) => {
|
||||
const { active } = props;
|
||||
return (
|
||||
|
@ -13,21 +13,6 @@ group:
|
||||
|
||||
基于行(Row)和列(Col)来定义区块(Block)的外部框架。
|
||||
|
||||
## 组件节点树
|
||||
|
||||
<pre lang="tsx">
|
||||
<Grid>
|
||||
<Grid.Row>
|
||||
<Grid.Col>
|
||||
// 添加其他节点
|
||||
</Grid.Col>
|
||||
<Grid.Col>
|
||||
// 添加其他节点
|
||||
</Grid.Col>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</pre>
|
||||
|
||||
## 列宽说明
|
||||
|
||||
最大支持四列,如果要支持越多列,需要处理的比例也越多。
|
||||
@ -88,4 +73,26 @@ group:
|
||||
|
||||
- 100%
|
||||
|
||||
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
<Grid>
|
||||
<Grid.Row>
|
||||
<Grid.Col>
|
||||
// 添加其他节点
|
||||
</Grid.Col>
|
||||
<Grid.Col>
|
||||
// 添加其他节点
|
||||
</Grid.Col>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</pre>
|
||||
|
||||
## Designable Bar
|
||||
|
||||
暂无
|
||||
|
||||
## Examples
|
||||
|
||||
<code src="./demos/demo1.tsx"/>
|
||||
|
@ -11,13 +11,17 @@ group:
|
||||
|
||||
# IconPicker - 图标选择器
|
||||
|
||||
## 节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
<IconPicker/>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
- IconPicker.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
```tsx
|
||||
/**
|
||||
|
@ -11,13 +11,17 @@ group:
|
||||
|
||||
# InputNumber - 数字框
|
||||
|
||||
## 节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
<InputNumber/>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
- InputNumber.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
### 数字框
|
||||
|
||||
|
@ -13,14 +13,19 @@ group:
|
||||
|
||||
输入框是非常常用的控件,参数可以组合成许多字段,如单行文本、多行文本、手机号、邮箱、网址等。
|
||||
|
||||
## 节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
<Input/>
|
||||
<Input.TextArea/>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
- Input.DesignableBar
|
||||
- Input.TextArea.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
### 单行文本
|
||||
|
||||
|
@ -11,13 +11,17 @@ group:
|
||||
|
||||
# Markdown 编辑器
|
||||
|
||||
## 节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
<Markdown/>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
- Markdown.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
### Markdown 编辑器
|
||||
|
||||
|
@ -13,7 +13,7 @@ group:
|
||||
|
||||
需要 antd v4.16+ 支持,在此之前的 Menu.Item 不支持 Fragment 包裹。
|
||||
|
||||
## 组件树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
<Menu>
|
||||
@ -43,7 +43,11 @@ group:
|
||||
</Menu>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
- Menu.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
### 横向菜单
|
||||
|
||||
|
@ -11,14 +11,18 @@ group:
|
||||
|
||||
# Password - 密码
|
||||
|
||||
## 节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
// 单选框
|
||||
<Password/>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
- Password.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
### 密码框
|
||||
|
||||
|
@ -11,7 +11,7 @@ group:
|
||||
|
||||
# Radio - 单选框
|
||||
|
||||
## 节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
// 单选框
|
||||
@ -20,7 +20,11 @@ group:
|
||||
<Radio.Group/>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
- Radio.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
### 单选框
|
||||
|
||||
|
@ -11,7 +11,7 @@ group:
|
||||
|
||||
# Select - 选择器
|
||||
|
||||
## 节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
// 常规选择器
|
||||
@ -37,7 +37,14 @@ group:
|
||||
</Select.Drawer>
|
||||
</pre>
|
||||
|
||||
## Select
|
||||
## Designable Bar
|
||||
|
||||
- Select.DesignableBar
|
||||
- Select.Drawer.DesignableBar
|
||||
- Select.Options.DesignableBar
|
||||
- Select.OptionTag.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
### 单选
|
||||
|
||||
@ -62,13 +69,11 @@ const schema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
input: {
|
||||
interface: 'string',
|
||||
type: 'string',
|
||||
type: 'number',
|
||||
title: `编辑模式`,
|
||||
name: 'name1',
|
||||
enum: options,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-component': 'Select.Object',
|
||||
'x-reactions': {
|
||||
target: 'read',
|
||||
fulfill: {
|
||||
@ -79,14 +84,12 @@ const schema = {
|
||||
},
|
||||
},
|
||||
read: {
|
||||
interface: 'string',
|
||||
type: 'string',
|
||||
type: 'number',
|
||||
title: `阅读模式`,
|
||||
enum: options,
|
||||
name: 'name2',
|
||||
'x-read-pretty': true,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-component': 'Select.Object',
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
}
|
||||
};
|
||||
@ -257,8 +260,7 @@ const schema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
input: {
|
||||
interface: 'select',
|
||||
type: 'string',
|
||||
type: 'object',
|
||||
title: `编辑模式`,
|
||||
enum: dataSource,
|
||||
'x-decorator': 'FormItem',
|
||||
@ -281,8 +283,7 @@ const schema = {
|
||||
},
|
||||
},
|
||||
read: {
|
||||
interface: 'select',
|
||||
type: 'string',
|
||||
type: 'object',
|
||||
title: `阅读模式`,
|
||||
enum: dataSource,
|
||||
'x-read-pretty': true,
|
||||
@ -332,8 +333,7 @@ const schema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
input: {
|
||||
interface: 'select',
|
||||
type: 'string',
|
||||
type: 'array',
|
||||
title: `编辑模式`,
|
||||
enum: dataSource,
|
||||
'x-decorator': 'FormItem',
|
||||
@ -356,8 +356,7 @@ const schema = {
|
||||
},
|
||||
},
|
||||
read: {
|
||||
interface: 'select',
|
||||
type: 'string',
|
||||
type: 'array',
|
||||
title: `阅读模式`,
|
||||
enum: dataSource,
|
||||
'x-read-pretty': true,
|
||||
@ -571,22 +570,44 @@ export default () => {
|
||||
};
|
||||
```
|
||||
|
||||
<!--
|
||||
<Select />
|
||||
<Select.Object />
|
||||
<Select.Drawer>
|
||||
<Select.Options>
|
||||
<Table />
|
||||
<Action />
|
||||
<Select.Options>
|
||||
<Select.ItemDetails>
|
||||
<Form/>
|
||||
</Select.ItemDetails>
|
||||
<Select.Drawer>
|
||||
## Schema API
|
||||
|
||||
<Action />
|
||||
### Select
|
||||
|
||||
<Action>
|
||||
<Drawer />
|
||||
</Action>
|
||||
-->
|
||||
type 支持 string、number、object 和 array,array 元素可以是 string、number 或 object。如:
|
||||
|
||||
```ts
|
||||
{
|
||||
properties: {
|
||||
select: {
|
||||
type: 'string', // 也可以是 number、object、array
|
||||
'x-component': 'Select',
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
可以配置 dataSource(enum)的 fieldNames,如:
|
||||
|
||||
```ts
|
||||
{
|
||||
properties: {
|
||||
select: {
|
||||
type: 'string', // 也可以是 number、object、array
|
||||
'x-component': 'Select',
|
||||
'x-component-props': {
|
||||
fieldNames: {
|
||||
label: 'label', // 标签文案
|
||||
value: 'value',
|
||||
color: 'color', // 标签颜色
|
||||
children: 'children', // 选项分组时,选项字段对应的 key
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Select.Drawer
|
||||
|
||||
type 支持 object 和 array,Select.Drawer 的可选项是以抽屉方式展开,提供了 Select.Options 和 Select.OptionTag 用于配置可选项界面和选中项详情界面。
|
||||
|
@ -18,6 +18,8 @@ import { useDesignable } from '../DesignableSchemaField';
|
||||
import { createContext } from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { SelectedRowKeysContext, useTableContext } from '../table';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { isField } from '@formily/core';
|
||||
|
||||
export const Select: any = connect(
|
||||
(props) => {
|
||||
@ -189,10 +191,13 @@ Select.Object = connect(
|
||||
),
|
||||
mapReadPretty(
|
||||
observer((props: any) => {
|
||||
const { value, fieldNames = { label: 'label' }, ...others } = props;
|
||||
const { value, fieldNames = { label: 'label', color: 'color' }, ...others } = props;
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
if (isEmpty(value)) {
|
||||
return null;
|
||||
}
|
||||
const values = toArr(value);
|
||||
return (
|
||||
<div>
|
||||
|
@ -11,7 +11,7 @@ group:
|
||||
|
||||
# Table - 表格
|
||||
|
||||
## 组件节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
<Table>
|
||||
@ -53,6 +53,11 @@ group:
|
||||
</Table>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
- Table.DesignableBar
|
||||
- Table.Column.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
<code src="./demos/demo1.tsx"/>
|
||||
|
@ -11,7 +11,7 @@ group:
|
||||
|
||||
# Tabs - 标签页
|
||||
|
||||
## 组件节点树
|
||||
## Node
|
||||
|
||||
<pre lang="tsx">
|
||||
<Tabs>
|
||||
@ -24,7 +24,11 @@ group:
|
||||
</Tabs>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
- Tabs.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
### 基本使用
|
||||
|
||||
|
@ -11,13 +11,17 @@ group:
|
||||
|
||||
# TimePicker - 时间选择器
|
||||
|
||||
## 节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
<TimePicker/>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
- TimePicker.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
### 日期选择器
|
||||
|
||||
|
@ -11,13 +11,17 @@ group:
|
||||
|
||||
# Upload - 上传
|
||||
|
||||
## 节点树
|
||||
## Node Tree
|
||||
|
||||
<pre lang="tsx">
|
||||
<Upload/>
|
||||
</pre>
|
||||
|
||||
## 代码演示
|
||||
## Designable Bar
|
||||
|
||||
- Upload.DesignableBar
|
||||
|
||||
## Examples
|
||||
|
||||
### 上传
|
||||
|
||||
|
@ -1,14 +0,0 @@
|
||||
---
|
||||
title: AdminLayout - 后台布局
|
||||
nav:
|
||||
title: 组件
|
||||
path: /client
|
||||
group:
|
||||
order: 2
|
||||
title: Templates
|
||||
path: /client/templates
|
||||
---
|
||||
|
||||
# AdminLayout - 后台布局
|
||||
|
||||
内置的后台布局模板,提供了基础的菜单和路由切换。
|
@ -1,229 +0,0 @@
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Spin,
|
||||
Layout,
|
||||
PageHeader,
|
||||
Modal,
|
||||
Menu,
|
||||
Collapse,
|
||||
Dropdown,
|
||||
} from 'antd';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import {
|
||||
Link,
|
||||
useLocation,
|
||||
useRouteMatch,
|
||||
useHistory,
|
||||
Redirect,
|
||||
} from 'react-router-dom';
|
||||
import {
|
||||
useGlobalAction,
|
||||
refreshGlobalAction,
|
||||
RouteComponentContext,
|
||||
} from '../../';
|
||||
import { SchemaRenderer } from '../../schemas';
|
||||
import { useRequest } from 'ahooks';
|
||||
import {
|
||||
DatabaseOutlined,
|
||||
PlusOutlined,
|
||||
DownOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Tabs } from 'antd';
|
||||
import '@formily/antd/esm/array-collapse/style';
|
||||
import './style.less';
|
||||
import { MenuContainerContext } from '../../schemas/menu';
|
||||
|
||||
function LogoutButton() {
|
||||
const history = useHistory();
|
||||
return (
|
||||
<Button
|
||||
onClick={async () => {
|
||||
history.push('/login');
|
||||
await refreshGlobalAction('routes:getAccessible');
|
||||
}}
|
||||
>
|
||||
注销
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
function Database() {
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
const schema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
layout: {
|
||||
type: 'void',
|
||||
'x-component': 'FormLayout',
|
||||
'x-component-props': {
|
||||
layout: 'vertical',
|
||||
},
|
||||
properties: {
|
||||
input: {
|
||||
type: 'string',
|
||||
title: '数据表名称',
|
||||
required: true,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
},
|
||||
array: {
|
||||
type: 'array',
|
||||
title: '数据表字段',
|
||||
'x-component': 'ArrayCollapse',
|
||||
'x-component-props': {
|
||||
accordion: true,
|
||||
},
|
||||
// maxItems: 3,
|
||||
'x-decorator': 'FormItem',
|
||||
items: {
|
||||
type: 'object',
|
||||
'x-component': 'ArrayCollapse.CollapsePanel',
|
||||
'x-component-props': {
|
||||
header: '字段',
|
||||
},
|
||||
properties: {
|
||||
index: {
|
||||
type: 'void',
|
||||
'x-component': 'ArrayCollapse.Index',
|
||||
},
|
||||
input: {
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
title: 'Input',
|
||||
required: true,
|
||||
'x-component': 'Input',
|
||||
},
|
||||
remove: {
|
||||
type: 'void',
|
||||
'x-component': 'ArrayCollapse.Remove',
|
||||
},
|
||||
moveUp: {
|
||||
type: 'void',
|
||||
'x-component': 'ArrayCollapse.MoveUp',
|
||||
},
|
||||
moveDown: {
|
||||
type: 'void',
|
||||
'x-component': 'ArrayCollapse.MoveDown',
|
||||
},
|
||||
},
|
||||
},
|
||||
properties: {
|
||||
addition: {
|
||||
type: 'void',
|
||||
title: '添加字段',
|
||||
'x-component': 'ArrayCollapse.Addition',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title={
|
||||
<Dropdown
|
||||
overlay={
|
||||
<Menu>
|
||||
<Menu.Item key={'1'}>数据表1</Menu.Item>
|
||||
<Menu.Item key={'2'}>数据表2</Menu.Item>
|
||||
<Menu.Item key={'3'}>数据表3</Menu.Item>
|
||||
<Menu.Item key={'4'}>数据表4</Menu.Item>
|
||||
<Menu.Divider></Menu.Divider>
|
||||
<Menu.Item key={'5'}>新增数据表</Menu.Item>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
数据表1 <DownOutlined />
|
||||
</span>
|
||||
</Dropdown>
|
||||
}
|
||||
// width={'800px'}
|
||||
visible={visible}
|
||||
onCancel={() => setVisible(false)}
|
||||
onOk={() => setVisible(false)}
|
||||
// bodyStyle={{ padding: 0 }}
|
||||
// footer={null}
|
||||
>
|
||||
<SchemaRenderer schema={schema} />
|
||||
</Modal>
|
||||
<DatabaseOutlined
|
||||
onClick={() => setVisible(true)}
|
||||
style={{ color: '#fff', lineHeight: '48px', width: 48 }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function LayoutWithMenu({ schema }) {
|
||||
const match = useRouteMatch<any>();
|
||||
const location = useLocation();
|
||||
const sideMenuRef = useRef();
|
||||
const [activeKey, setActiveKey] = useState(match.params.name);
|
||||
const onSelect = (info) => {
|
||||
console.log('LayoutWithMenu', schema);
|
||||
setActiveKey(info.key);
|
||||
};
|
||||
console.log({ match });
|
||||
return (
|
||||
<Layout>
|
||||
<Layout.Header>
|
||||
<SchemaRenderer
|
||||
schema={schema}
|
||||
scope={{ sideMenuRef, onSelect, selectedKeys: [activeKey].filter(Boolean) }}
|
||||
/>
|
||||
</Layout.Header>
|
||||
<Layout>
|
||||
<Layout.Sider
|
||||
ref={sideMenuRef}
|
||||
theme={'light'}
|
||||
width={200}
|
||||
></Layout.Sider>
|
||||
<Layout.Content>
|
||||
{location.pathname}
|
||||
<Content activeKey={activeKey} />
|
||||
</Layout.Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
function Content({ activeKey }) {
|
||||
const { data = {}, loading } = useRequest(
|
||||
`/api/ui-schemas:getTree/${activeKey}?filter[parentId]=${activeKey}`,
|
||||
{
|
||||
refreshDeps: [activeKey],
|
||||
formatResult: (result) => result?.data,
|
||||
},
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
return <Spin />;
|
||||
}
|
||||
|
||||
return <SchemaRenderer schema={data} />;
|
||||
}
|
||||
|
||||
export function AdminLayout({ route, children }: any) {
|
||||
|
||||
const { data = {}, loading } = useRequest(
|
||||
`/api/ui-schemas:getTree/${route.schemaName}`,
|
||||
{
|
||||
refreshDeps: [route],
|
||||
formatResult: (result) => result?.data,
|
||||
},
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
return <Spin />;
|
||||
}
|
||||
|
||||
return <LayoutWithMenu schema={data} />;
|
||||
}
|
||||
|
||||
export default AdminLayout;
|
@ -1,49 +0,0 @@
|
||||
// .fields-collapse {
|
||||
// border: 1px solid #f0f0f0;
|
||||
// border-bottom: 0;
|
||||
// > .ant-collapse-item {
|
||||
// border-bottom: 1px solid #f0f0f0;
|
||||
// }
|
||||
// .ant-collapse-content {
|
||||
// &.ant-collapse-content-active {
|
||||
// border-top: 1px solid #f0f0f0;
|
||||
// }
|
||||
// }
|
||||
// > .ant-collapse-item > .ant-collapse-content {
|
||||
// background: #fff;
|
||||
// }
|
||||
// > .ant-collapse-item > .ant-collapse-content > .ant-collapse-content-box {
|
||||
// padding: 16px;
|
||||
// padding-bottom: 1px;
|
||||
// }
|
||||
// }
|
||||
|
||||
.ant-collapse {
|
||||
border: 1px solid #d9d9d9 !important;
|
||||
border-bottom: 0 !important;
|
||||
> .ant-collapse-item {
|
||||
border-bottom: 1px solid #d9d9d9 !important;
|
||||
}
|
||||
.ant-collapse-content {
|
||||
border-top: 1px solid #d9d9d9 !important;
|
||||
}
|
||||
.ant-collapse-content > .ant-collapse-content-box {
|
||||
padding: 24px !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.database-sider {
|
||||
background: #fafafa;
|
||||
padding-top: 16px;
|
||||
.ant-menu {
|
||||
background: #fafafa;
|
||||
border-right: 0;
|
||||
}
|
||||
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
|
||||
background: #fff;
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
---
|
||||
title: AuthLayout - 登录布局
|
||||
nav:
|
||||
title: 组件
|
||||
path: /client
|
||||
group:
|
||||
order: 2
|
||||
title: Templates
|
||||
path: /client/templates
|
||||
---
|
||||
|
||||
# AuthLayout - 登录布局
|
||||
|
||||
内置的登录、注册页布局
|
@ -1,15 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useLocation, useHistory } from 'react-router-dom';
|
||||
|
||||
export function AuthLayout({ children, route }: any) {
|
||||
const location = useLocation();
|
||||
const history = useHistory();
|
||||
return (
|
||||
<div style={{ maxWidth: 320, margin: '0 auto' }}>
|
||||
<h1>NocoBase</h1>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AuthLayout;
|
@ -1,14 +0,0 @@
|
||||
---
|
||||
title: PageTemplate - 页面模板
|
||||
nav:
|
||||
title: 组件
|
||||
path: /client
|
||||
group:
|
||||
order: 2
|
||||
title: Templates
|
||||
path: /client/templates
|
||||
---
|
||||
|
||||
# PageTemplate - 页面模板
|
||||
|
||||
常规的页面模板,支持区块配置。
|
@ -1,25 +0,0 @@
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { Spin } from 'antd';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { useRequest } from 'ahooks';
|
||||
import { SchemaRenderer } from '../../schemas';
|
||||
|
||||
export function DefaultPage({ route }) {
|
||||
const { data = {}, loading } = useRequest(
|
||||
`/api/ui-schemas:getTree/${route.schemaName}`,
|
||||
{
|
||||
refreshDeps: [route],
|
||||
formatResult: (result) => result?.data,
|
||||
},
|
||||
);
|
||||
if (loading) {
|
||||
return <Spin />;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<SchemaRenderer schema={data} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DefaultPage;
|
@ -1,3 +0,0 @@
|
||||
export * from './auth-layout';
|
||||
export * from './admin-layout';
|
||||
export * from './default-page';
|
Loading…
Reference in New Issue
Block a user