This commit is contained in:
chenos 2021-07-16 23:57:11 +08:00
parent c9aa381045
commit 78740f9905
18 changed files with 565 additions and 229 deletions

View File

@ -0,0 +1,56 @@
export const schema = {
type: 'void',
name: 'action1',
// title: 'ModalForm',
'x-component': 'Action',
'x-component-props': {
type: 'primary',
icon: 'DatabaseOutlined',
},
properties: {
modal1: {
type: 'void',
title: '对话框标题',
'x-decorator': 'Form',
'x-component': 'Action.Modal',
properties: {
title: {
type: 'string',
title: '数据表名称',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
fields: {
type: 'array',
title: '字段',
'x-decorator': 'FormItem',
'x-component': 'DatabaseField',
default: [
{
id: 1,
interface: 'string',
dataType: 'string',
name: 'title',
ui: {
title: '标题',
},
},
{
id: 2,
dataType: 'text',
interface: 'textarea',
name: 'content',
ui: {
title: '内容',
},
},
],
},
},
},
},
};
export default schema;

View File

@ -21,6 +21,10 @@ import { SchemaRenderer } from '../../schemas';
import { useRequest } from 'ahooks';
import './style.less';
import { uid } from '@formily/shared';
import { ISchema } from '@formily/react';
import datatable from './datatable';
function LayoutWithMenu({ schema }) {
const match = useRouteMatch<any>();
const location = useLocation();
@ -33,7 +37,7 @@ function LayoutWithMenu({ schema }) {
console.log({ match });
return (
<Layout>
<Layout.Header>
<Layout.Header style={{ display: 'flex' }}>
<SchemaRenderer
schema={schema}
scope={{
@ -42,6 +46,7 @@ function LayoutWithMenu({ schema }) {
selectedKeys: [activeKey].filter(Boolean),
}}
/>
<SchemaRenderer schema={datatable} />
</Layout.Header>
<Layout>
<Layout.Sider
@ -59,7 +64,7 @@ function LayoutWithMenu({ schema }) {
function Content({ activeKey }) {
const { data = {}, loading } = useRequest(
`ui_schemas:getTree/${activeKey}?filter[parentId]=${activeKey}`,
`ui_schemas:getTree?filter[parentKey]=${activeKey}`,
{
refreshDeps: [activeKey],
formatResult: (result) => result?.data,
@ -70,7 +75,31 @@ function Content({ activeKey }) {
return <Spin />;
}
return <SchemaRenderer schema={data} />;
const schema: ISchema = {
type: 'void',
name: uid(),
'x-component': 'Grid',
properties: {
[`row_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Row',
properties: {
[`col_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
[uid()]: {
type: 'void',
'x-component': 'AddNew.BlockItem',
},
},
},
},
},
},
};
return <SchemaRenderer schema={schema} />;
}
export function AdminLayout({ route }: any) {

View File

@ -6,6 +6,10 @@ import { SchemaRenderer } from '../../schemas';
import { useForm } from '@formily/react';
import { useHistory } from 'react-router-dom';
function Div(props) {
return <div {...props}></div>
}
export function useLogin() {
const form = useForm();
const history = useHistory();
@ -41,7 +45,7 @@ export function RouteSchemaRenderer({ route }) {
}
return (
<div>
<SchemaRenderer scope={{ useLogin, useRegister }} schema={data} />
<SchemaRenderer components={{ Div }} scope={{ useLogin, useRegister }} schema={data} />
</div>
);
}

View File

@ -161,8 +161,8 @@ export function useDesignable(path?: any) {
const fieldSchema = useFieldSchema();
const currentSchema =
findPropertyByPath(schema, schemaPath) || ({} as Schema);
console.log({ currentSchema })
// console.log('useDesignable', { schema, schemaPath, currentSchema });
console.log({ currentSchema });
// console.log('useDesignable', { schema, schemaPath, currentSchema });
const options = useContext(SchemaOptionsContext);
const DesignableBar =
get(options.components, currentSchema['x-designable-bar']) || (() => null);
@ -184,6 +184,9 @@ export function useDesignable(path?: any) {
}
if (target['key']) {
property['parentKey'] = target['key'];
if (!property['key']) {
property['key'] = uid();
}
}
const properties = {};
properties[property.name] = property;
@ -211,6 +214,9 @@ export function useDesignable(path?: any) {
}
if (target['key']) {
property['parentKey'] = target['key'];
if (!property['key']) {
property['key'] = uid();
}
}
target.addProperty(property.name, property);
// BUG: 空 properties 时addProperty 无反应。
@ -232,8 +238,12 @@ export function useDesignable(path?: any) {
if (!property.name) {
property.name = uid();
}
console.log('target.parentKey', target);
if (target['parentKey']) {
property['parentKey'] = target['parentKey'];
if (!property['key']) {
property['key'] = uid();
}
}
addPropertyAfter(target, property);
refresh();
@ -253,6 +263,9 @@ export function useDesignable(path?: any) {
}
if (target['parentKey']) {
property['parentKey'] = target['parentKey'];
if (!property['key']) {
property['key'] = uid();
}
}
addPropertyBefore(target, property);
refresh();

View File

@ -18,9 +18,11 @@ import './style.less';
import { uid } from '@formily/shared';
import cls from 'classnames';
import { MenuOutlined } from '@ant-design/icons';
import { FormLayout } from '@formily/antd';
import IconPicker from '../../components/icon-picker';
export const Action: any = observer((props: any) => {
const { useAction = useDefaultAction, ...others } = props;
const { useAction = useDefaultAction, icon, ...others } = props;
const { run } = useAction();
const { schema, DesignableBar } = useDesignable();
const [visible, setVisible] = useState(false);
@ -28,12 +30,13 @@ export const Action: any = observer((props: any) => {
const isDropdownOrPopover =
child &&
['Action.Dropdown', 'Action.Popover'].includes(child['x-component']);
console.log({ DesignableBar, schema })
return (
console.log({ DesignableBar, schema });
return (
<VisibleContext.Provider value={[visible, setVisible]}>
{!isDropdownOrPopover && (
<Button
{...others}
icon={<IconPicker type={icon} />}
onClick={async () => {
setVisible(true);
await run();
@ -48,10 +51,31 @@ export const Action: any = observer((props: any) => {
);
});
Action.Link = observer((props: any) => {
const { schema } = useDesignable();
return <Link {...props}>{schema.title}</Link>;
});
Action.URL = observer((props: any) => {
const { schema } = useDesignable();
return (
<a target={'_blank'} {...props}>
{schema.title}
</a>
);
});
Action.Modal = observer((props: any) => {
const {
useOkAction = useDefaultAction,
useCancelAction = useDefaultAction,
...others
} = props;
const { schema } = useDesignable();
const [visible, setVisible] = useContext(VisibleContext);
const form = useForm();
const { run: runOk } = useOkAction();
const { run: runCancel } = useCancelAction();
const isFormDecorator = schema['x-decorator'] === 'Form';
return (
<Modal
@ -66,6 +90,7 @@ Action.Modal = observer((props: any) => {
if (isFormDecorator) {
form.clearErrors();
}
runCancel && (await runCancel());
setVisible(false);
}}
>
@ -77,6 +102,7 @@ Action.Modal = observer((props: any) => {
if (isFormDecorator) {
await form.submit();
}
runOk && (await runOk());
setVisible(false);
}}
>
@ -85,24 +111,32 @@ Action.Modal = observer((props: any) => {
]
: null
}
{...props}
onCancel={() => {
{...others}
onCancel={async () => {
if (isFormDecorator) {
form.clearErrors();
}
runCancel && (await runCancel());
setVisible(false);
}}
visible={visible}
>
{props.children}
<FormLayout layout={'vertical'}>{props.children}</FormLayout>
</Modal>
);
});
Action.Drawer = observer((props: any) => {
const {
useOkAction = useDefaultAction,
useCancelAction = useDefaultAction,
...others
} = props;
const { schema } = useDesignable();
const [visible, setVisible] = useContext(VisibleContext);
const form = useForm();
const { run: runOk } = useOkAction();
const { run: runCancel } = useCancelAction();
const isFormDecorator = schema['x-decorator'] === 'Form';
return (
<Drawer
@ -117,6 +151,7 @@ Action.Drawer = observer((props: any) => {
onClick={async (e) => {
form.clearErrors();
props.onClose && (await props.onClose(e));
runCancel && (await runCancel());
setVisible(false);
}}
>
@ -126,6 +161,7 @@ Action.Drawer = observer((props: any) => {
onClick={async (e) => {
await form.submit();
props.onOk && (await props.onOk(e));
runOk && (await runOk());
setVisible(false);
}}
type={'primary'}
@ -135,14 +171,15 @@ Action.Drawer = observer((props: any) => {
</Space>
)
}
{...props}
{...others}
visible={visible}
onClose={async (e) => {
props.onClose && (await props.onClose(e));
runCancel && (await runCancel());
setVisible(false);
}}
>
{props.children}
<FormLayout layout={'vertical'}>{props.children}</FormLayout>
</Drawer>
);
});
@ -264,4 +301,4 @@ Action.DesignableBar = () => {
</span>
</div>
);
};
};

View File

@ -0,0 +1,44 @@
import React from 'react';
import { createForm } from '@formily/core';
import { SchemaRenderer } from '../../';
import { observer, connect, useField } from '@formily/react';
const schema = {
type: 'object',
properties: {
fields: {
type: 'array',
'x-component': 'DatabaseField',
default: [
{
id: 1,
interface: 'string',
dataType: 'string',
name: 'title',
ui: {
title: '标题',
},
},
{
id: 2,
dataType: 'text',
interface: 'textarea',
name: 'content',
ui: {
title: '内容',
},
},
],
},
},
};
const form = createForm();
export default observer(() => {
return (
<div>
<SchemaRenderer form={form} schema={schema} />
</div>
)
})

View File

@ -0,0 +1,63 @@
import React from 'react';
import { createForm } from '@formily/core';
import { SchemaRenderer } from '../../';
import { observer, connect, useField } from '@formily/react';
const schema = {
type: 'void',
name: 'action1',
title: 'ModalForm',
'x-component': 'Action',
'x-component-props': {},
properties: {
modal1: {
type: 'void',
title: '对话框标题',
'x-decorator': 'Form',
'x-component': 'Action.Modal',
properties: {
title: {
type: 'string',
title: '数据表名称',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
fields: {
type: 'array',
'x-component': 'DatabaseField',
default: [
{
id: 1,
interface: 'string',
dataType: 'string',
name: 'title',
ui: {
title: '标题',
},
},
{
id: 2,
dataType: 'text',
interface: 'textarea',
name: 'content',
ui: {
title: '内容',
},
},
],
},
},
},
},
};
const form = createForm();
export default observer(() => {
return (
<div>
<SchemaRenderer form={form} schema={schema} />
</div>
);
});

View File

@ -23,53 +23,6 @@ group:
## Examples
```tsx
import React from 'react';
import { createForm } from '@formily/core';
import { SchemaRenderer } from '../';
import { observer, connect, useField } from '@formily/react';
<code src="./demos/demo1.tsx"/>
const schema = {
type: 'object',
properties: {
array: {
type: 'array',
'x-component': 'DatabaseField',
default: [
{
id: 1,
interface: 'string',
dataType: 'string',
name: 'title',
ui: {
title: '标题',
},
},
{
id: 2,
dataType: 'text',
interface: 'textarea',
name: 'content',
ui: {
title: '内容',
},
},
],
},
// input: {
// type: 'string',
// 'x-component': 'Input',
// },
},
};
const form = createForm();
export default observer(() => {
return (
<div>
<SchemaRenderer form={form} schema={schema} />
</div>
)
})
```
<code src="./demos/demo2.tsx"/>

View File

@ -26,16 +26,19 @@ export const Form: any = observer((props: any) => {
}, []);
const { schema } = useDesignable();
const path = useSchemaPath();
const scope = useContext(SchemaExpressionScopeContext);
return (
<FormProvider form={form}>
{schema['x-decorator'] === 'Form' ? (
<SchemaField
scope={scope}
schema={{
type: 'object',
properties: {
[schema.name]: {
...schema.toJSON(),
'x-path': path,
// 避免死循环
'x-decorator': 'Form.__Decorator',
},
},
@ -44,6 +47,7 @@ export const Form: any = observer((props: any) => {
) : (
<FormLayout layout={'vertical'} {...others}>
<SchemaField
scope={scope}
schema={{
type: 'object',
properties: schema.properties,

View File

@ -9,3 +9,10 @@ export function useDefaultAction() {
async run () {}
}
}
import { extend } from 'umi-request';
export const request = extend({
prefix: 'http://localhost:23003/api/',
timeout: 1000,
});

View File

@ -3,11 +3,13 @@
*/
import React from 'react';
import { SchemaRenderer } from '@nocobase/client';
import { uid } from '@formily/shared';
import { ISchema } from '@formily/react';
const schema = {
const schema: ISchema = {
type: 'object',
properties: {
menu1: {
[uid()]: {
type: 'void',
'x-component': 'Menu',
'x-designable-bar': 'Menu.DesignableBar',
@ -16,37 +18,46 @@ const schema = {
theme: 'dark',
},
properties: {
item1: {
[uid()]: {
type: 'void',
title: `菜单1`,
'x-designable-bar': 'Menu.DesignableBar',
'x-component': 'Menu.Item',
},
item2: {
[uid()]: {
type: 'void',
title: `链接`,
'x-designable-bar': 'Menu.DesignableBar',
'x-component': 'Menu.Link',
"x-component-props": {
to: '/abc/def',
},
},
[uid()]: {
type: 'void',
title: `菜单2`,
'x-designable-bar': 'Menu.DesignableBar',
'x-component': 'Menu.Item',
},
item3: {
[uid()]: {
type: 'void',
title: '菜单组3',
'x-designable-bar': 'Menu.DesignableBar',
'x-component': 'Menu.SubMenu',
properties: {
item5: {
[uid()]: {
type: 'void',
title: `子菜单5`,
'x-designable-bar': 'Menu.DesignableBar',
'x-component': 'Menu.SubMenu',
properties: {
item8: {
[uid()]: {
type: 'void',
title: `子菜单8`,
'x-designable-bar': 'Menu.DesignableBar',
'x-component': 'Menu.Item',
},
item9: {
[uid()]: {
type: 'void',
title: `子菜单9`,
'x-designable-bar': 'Menu.DesignableBar',
@ -56,19 +67,19 @@ const schema = {
},
},
},
item4: {
[uid()]: {
type: 'void',
title: '菜单组4',
'x-designable-bar': 'Menu.DesignableBar',
'x-component': 'Menu.SubMenu',
properties: {
item6: {
[uid()]: {
type: 'void',
title: `子菜单6`,
'x-designable-bar': 'Menu.DesignableBar',
'x-component': 'Menu.Item',
},
item7: {
[uid()]: {
type: 'void',
title: `子菜单7`,
'x-designable-bar': 'Menu.DesignableBar',

View File

@ -1,11 +1,12 @@
import React, { useRef } from 'react';
import { SchemaRenderer } from '@nocobase/client';
import { Layout } from 'antd';
import { uid } from '@formily/shared';
const schema = {
type: 'object',
properties: {
menu1: {
[uid()]: {
type: 'void',
'x-component': 'Menu',
'x-designable-bar': 'Menu.DesignableBar',
@ -15,37 +16,37 @@ const schema = {
theme: 'dark',
},
properties: {
item1: {
[uid()]: {
type: 'void',
title: `菜单1`,
'x-designable-bar': 'Menu.DesignableBar',
'x-component': 'Menu.Item',
},
item2: {
[uid()]: {
type: 'void',
title: `菜单2`,
'x-designable-bar': 'Menu.DesignableBar',
'x-component': 'Menu.Item',
},
item3: {
[uid()]: {
type: 'void',
title: '菜单组3',
'x-designable-bar': 'Menu.DesignableBar',
'x-component': 'Menu.SubMenu',
properties: {
item5: {
[uid()]: {
type: 'void',
title: `子菜单5`,
'x-designable-bar': 'Menu.DesignableBar',
'x-component': 'Menu.SubMenu',
properties: {
item8: {
[uid()]: {
type: 'void',
title: `子菜单8`,
'x-designable-bar': 'Menu.DesignableBar',
'x-component': 'Menu.Item',
},
item9: {
[uid()]: {
type: 'void',
title: `子菜单9`,
'x-designable-bar': 'Menu.DesignableBar',
@ -55,19 +56,19 @@ const schema = {
},
},
},
item4: {
[uid()]: {
type: 'void',
title: '菜单组4',
'x-designable-bar': 'Menu.DesignableBar',
'x-component': 'Menu.SubMenu',
properties: {
item6: {
[uid()]: {
type: 'void',
title: `子菜单6`,
'x-designable-bar': 'Menu.DesignableBar',
'x-component': 'Menu.Item',
},
item7: {
[uid()]: {
type: 'void',
title: `子菜单7`,
'x-designable-bar': 'Menu.DesignableBar',
@ -86,6 +87,7 @@ export default () => {
<Layout>
<Layout.Header>
<SchemaRenderer
debug={true}
scope={{ sideMenuRef }}
schema={schema}
/>

View File

@ -33,16 +33,21 @@ import {
import { uid } from '@formily/shared';
import cls from 'classnames';
import { useDesignable } from '../../components/schema-renderer';
import { MenuOutlined } from '@ant-design/icons';
import { MenuOutlined, PlusOutlined } from '@ant-design/icons';
import { IconPicker } from '../../components/icon-picker';
import { VisibleContext } from '..';
import { useDefaultAction, VisibleContext } from '..';
import { useMount } from 'ahooks';
import './style.less';
import { Link } from 'react-router-dom';
import { useSchemaPath } from '@nocobase/client/lib';
import { request } from '../';
export const MenuModeContext = createContext(null);
function useSelectedKey() {}
const SideMenu = (props: any) => {
const { visible, selectedKey, onSelect } = props;
const { visible, selectedKey, onSelect, path } = props;
const { schema } = useDesignable();
if (!selectedKey || !visible) {
return null;
@ -53,10 +58,16 @@ const SideMenu = (props: any) => {
}
return (
<AntdMenu mode={'inline'} onSelect={onSelect}>
<RecursionField schema={child} onlyRenderProperties />
<AntdMenu.Item key={uid()}>{selectedKey}</AntdMenu.Item>
</AntdMenu>
<MenuModeContext.Provider value={'inline'}>
<AntdMenu mode={'inline'} onSelect={onSelect}>
<RecursionField schema={child} onlyRenderProperties />
<Menu.AddNew key={uid()} path={[...path, selectedKey]}>
<Button block type={'dashed'}>
<PlusOutlined className={'nb-add-new-icon'} />
</Button>
</Menu.AddNew>
</AntdMenu>
</MenuModeContext.Provider>
);
};
@ -65,6 +76,9 @@ export const Menu: any = observer((props: any) => {
const { schema } = useDesignable();
const [selectedKey, setSelectedKey] = useState(null);
const ref = useRef();
const path = useSchemaPath();
const child = schema.properties && schema.properties[selectedKey];
const isSubMenu = child && child['x-component'] === 'Menu.SubMenu';
useMount(() => {
if (mode !== 'mix') {
@ -74,8 +88,17 @@ export const Menu: any = observer((props: any) => {
if (sideMenuElement && ref.current) {
sideMenuElement.querySelector(':scope > div').appendChild(ref.current);
}
sideMenuElement.style.display = isSubMenu ? 'block' : 'none';
});
useEffect(() => {
const sideMenuElement = sideMenuRef && (sideMenuRef.current as HTMLElement);
if (!sideMenuElement) {
return;
}
sideMenuElement.style.display = isSubMenu ? 'block' : 'none';
}, [selectedKey]);
return (
<MenuModeContext.Provider value={mode}>
<AntdMenu
@ -89,13 +112,14 @@ export const Menu: any = observer((props: any) => {
}}
>
<RecursionField schema={schema} onlyRenderProperties />
<AntdMenu.ItemGroup title={
<div></div>
}></AntdMenu.ItemGroup>
<Menu.AddNew key={uid()} path={path}>
<PlusOutlined className={'nb-add-new-icon'} />
</Menu.AddNew>
</AntdMenu>
{mode === 'mix' && (
<div ref={ref}>
<SideMenu
path={path}
onSelect={onSelect}
visible={mode === 'mix'}
selectedKey={selectedKey}
@ -107,6 +131,62 @@ export const Menu: any = observer((props: any) => {
);
});
Menu.AddNew = observer((props: any) => {
const { appendChild } = useDesignable(props.path);
return (
<AntdMenu.ItemGroup
className={'nb-menu-add-new'}
title={
<Dropdown
overlay={
<AntdMenu>
<AntdMenu.Item
onClick={async () => {
const data = appendChild({
type: 'void',
title: uid(),
'x-component': 'Menu.Item',
'x-designable-bar': 'Menu.DesignableBar',
});
if (data['key']) {
await request('ui_schemas:create', {
method: 'post',
data: data.toJSON(),
});
}
}}
>
</AntdMenu.Item>
<AntdMenu.Item
onClick={async () => {
const data = appendChild({
type: 'void',
key: uid(),
title: uid(),
'x-component': 'Menu.SubMenu',
'x-designable-bar': 'Menu.DesignableBar',
});
if (data['key']) {
await request('ui_schemas:create', {
method: 'post',
data: data.toJSON(),
});
}
}}
>
</AntdMenu.Item>
</AntdMenu>
}
>
<a>{props.children}</a>
</Dropdown>
}
/>
);
});
Menu.Divider = observer(AntdMenu.Divider);
Menu.Item = observer((props: any) => {
@ -125,18 +205,54 @@ Menu.Item = observer((props: any) => {
);
});
Menu.Action = observer((props: any) => {
Menu.Link = observer((props: any) => {
const { icon } = props;
const { schema, DesignableBar } = useDesignable();
return (
<AntdMenu.Item
{...props}
icon={<IconPicker type={icon} />}
eventKey={schema.name}
key={schema.name}
>
<Link to={props.to}>{schema.title}</Link>
<DesignableBar />
</AntdMenu.Item>
);
});
Menu.URL = observer((props: any) => {
const { icon } = props;
const { schema, DesignableBar } = useDesignable();
return (
<AntdMenu.Item
{...props}
icon={<IconPicker type={icon} />}
eventKey={schema.name}
key={schema.name}
>
<a target={'_blank'} href={props.href}>
{schema.title}
</a>
<DesignableBar />
</AntdMenu.Item>
);
});
Menu.Action = observer((props: any) => {
const { icon, useAction = useDefaultAction, ...others } = props;
const { schema, DesignableBar } = useDesignable();
const [visible, setVisible] = useState(false);
const { run } = useAction();
return (
<VisibleContext.Provider value={[visible, setVisible]}>
<AntdMenu.Item
{...props}
{...others}
key={schema.name}
eventKey={schema.name}
icon={<IconPicker type={icon} />}
onClick={() => {
onClick={async () => {
await run();
setVisible(true);
}}
>
@ -210,37 +326,53 @@ Menu.DesignableBar = (props) => {
</AntdMenu.Item>
<AntdMenu.Item
onClick={() => {
onClick={async () => {
const s = insertAfter({
type: 'void',
key: uid(),
title: uid(),
'x-component': 'Menu.SubMenu',
});
console.log('s.s.s.s', s);
if (s['key']) {
await request('ui_schemas:create', {
method: 'post',
data: s.toJSON(),
});
}
}}
>
</AntdMenu.Item>
<AntdMenu.Item
onClick={() => {
onClick={async () => {
const s = insertAfter({
type: 'void',
key: uid(),
title: uid(),
'x-component': 'Menu.Item',
});
if (s['key']) {
await request('ui_schemas:create', {
method: 'post',
data: s.toJSON(),
});
}
}}
>
</AntdMenu.Item>
<AntdMenu.Item
onClick={() => {
onClick={async () => {
const s = appendChild({
type: 'void',
key: uid(),
title: uid(),
'x-component': 'Menu.Item',
});
if (s['key']) {
await request('ui_schemas:create', {
method: 'post',
data: s.toJSON(),
});
}
}}
>

View File

@ -74,4 +74,17 @@
height: 100%;
margin: 0 -20px;
}
}
.nb-menu-add-new {
.ant-menu-item-group-title {
line-height: 46px;
padding: 0;
cursor: pointer;
.nb-add-new-icon {
height: 46px;
line-height: 46px;
width: 46px;
}
}
}

View File

@ -1,6 +1,6 @@
import React from 'react';
import { SchemaRenderer } from '../..';
import { Table } from '../';
import { Table, useTableCreateAction } from '../';
import { uid } from '@formily/shared';
import { createForm } from '@formily/core';
import { ISchema, observer } from '@formily/react';
@ -8,9 +8,9 @@ import { ISchema, observer } from '@formily/react';
function useAction() {
return {
async run() {
alert('自定义')
}
}
alert('自定义');
},
};
}
const schema: ISchema = {
@ -93,8 +93,8 @@ const schema: ISchema = {
},
},
},
}
}
},
},
},
},
},
@ -109,35 +109,23 @@ const schema: ISchema = {
drawer1: {
type: 'void',
title: '抽屉标题',
'x-decorator': 'Form',
'x-component': 'Action.Modal',
'x-component-props': {},
'x-component-props': {
useOkAction: '{{ Table.useTableCreateAction }}',
},
properties: {
[uid()]: {
name: 'form',
type: 'object',
'x-component': 'Form',
properties: {
field1: {
type: 'string',
title: '字段1',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
field2: {
type: 'string',
title: '字段2',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
action: {
type: 'void',
title: '提交',
'x-component': 'Action',
'x-component-props': {
useAction: '{{ Table.useTableCreateAction }}',
},
},
},
field1: {
type: 'string',
title: '字段1',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
field2: {
type: 'string',
title: '字段2',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
@ -154,22 +142,6 @@ const schema: ISchema = {
},
},
},
// [`a_${uid()}`]: {
// type: 'void',
// 'x-component': 'Table.ActionBar',
// 'x-component-props': {
// align: 'bottom',
// },
// properties: {
// pagination: {
// type: 'void',
// 'x-component': 'Table.Pagination',
// 'x-component-props': {
// defaultPageSize: 5,
// },
// },
// },
// },
[`a_${uid()}`]: {
type: 'void',
'x-component': 'Table.ActionBar',
@ -256,7 +228,6 @@ const schema: ISchema = {
type: 'void',
name: 'action1',
title: '查看',
'x-component': 'Action',
'x-designable-bar': 'Table.Action.DesignableBar',
properties: {
@ -318,38 +289,26 @@ const schema: ISchema = {
drawer1: {
type: 'void',
title: '编辑表单',
"x-decorator": 'Form',
'x-decorator-props': {
useValues: '{{ Table.useTableRow }}',
},
'x-component': 'Action.Modal',
'x-component-props': {},
'x-component-props': {
useOkAction: '{{ Table.useTableUpdateAction }}',
},
properties: {
[uid()]: {
name: 'form',
type: 'void',
'x-component': 'Form',
'x-component-props': {
useValues: '{{ Table.useTableRow }}',
},
properties: {
field1: {
type: 'string',
title: '字段1',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
field2: {
type: 'string',
title: '字段2',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
action: {
type: 'void',
title: '提交',
'x-component': 'Action',
'x-component-props': {
useAction: '{{ Table.useTableUpdateAction }}',
},
},
},
field1: {
type: 'string',
title: '字段1',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
field2: {
type: 'string',
title: '字段2',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
@ -371,7 +330,7 @@ const schema: ISchema = {
[uid()]: {
type: 'void',
title: '操作 1',
'x-component': 'Action',
'x-component': 'Menu.Action',
'x-component-props': {
useAction,
disabled: true,
@ -380,7 +339,7 @@ const schema: ISchema = {
[uid()]: {
type: 'void',
title: '操作 2',
'x-component': 'Action',
'x-component': 'Menu.Action',
'x-default-action': true,
'x-component-props': {
useAction,
@ -390,8 +349,7 @@ const schema: ISchema = {
type: 'void',
name: 'action1',
title: '查看',
'x-component': 'Action',
// 'x-default-action': true,
'x-component': 'Menu.Action',
'x-designable-bar': 'Table.Action.DesignableBar',
properties: {
drawer1: {
@ -446,44 +404,32 @@ const schema: ISchema = {
type: 'void',
name: 'action1',
title: '修改',
'x-component': 'Action',
'x-component': 'Menu.Action',
'x-designable-bar': 'Table.Action.DesignableBar',
properties: {
drawer1: {
type: 'void',
title: '编辑表单',
"x-decorator": 'Form',
'x-decorator-props': {
useValues: '{{ Table.useTableRow }}',
},
'x-component': 'Action.Modal',
'x-component-props': {},
'x-component-props': {
useOkAction: '{{ Table.useTableUpdateAction }}',
},
properties: {
[uid()]: {
name: 'form',
type: 'void',
'x-component': 'Form',
'x-component-props': {
useValues: '{{ Table.useTableRow }}',
},
properties: {
field1: {
type: 'string',
title: '字段1',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
field2: {
type: 'string',
title: '字段2',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
action: {
type: 'void',
title: '提交',
'x-component': 'Action',
'x-component-props': {
useAction: '{{ Table.useTableUpdateAction }}',
},
},
},
field1: {
type: 'string',
title: '字段1',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
field2: {
type: 'string',
title: '字段2',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
@ -492,7 +438,7 @@ const schema: ISchema = {
[uid()]: {
type: 'void',
title: '删除',
'x-component': 'Action',
'x-component': 'Menu.Action',
'x-component-props': {
useAction: '{{ Table.useTableDestroyAction }}',
},
@ -511,7 +457,7 @@ const form = createForm({
export default observer(() => {
return (
<div>
<SchemaRenderer scope={{ Table }} form={form} schema={schema} />
<SchemaRenderer scope={{ Table, useTableCreateAction }} form={form} schema={schema} />
</div>
);
});

View File

@ -227,9 +227,11 @@ export function useTableCreateAction() {
export function useTableUpdateAction() {
const { field, refresh, params } = useTableContext();
const [,setVisible] = useContext(VisibleContext);
const ctx = useContext(TableRowContext);
const form = useForm();
return {
async run() {
field.value[ctx.index] = form.values;
setVisible && setVisible(false);
refresh();
},

View File

@ -2,14 +2,34 @@ import { Model, ModelCtor } from '@nocobase/database';
import { actions } from '@nocobase/actions';
export default async (ctx: actions.Context, next: actions.Next) => {
const { resourceKey } = ctx.action.params;
const { resourceKey, filter } = ctx.action.params;
const UISchema = ctx.db.getModel('ui_schemas');
const schema = await UISchema.findByPk(resourceKey);
const property = schema.toProperty();
const properties = await schema.getProperties();
if (Object.keys(properties).length) {
property.properties = properties;
if (resourceKey) {
const schema = await UISchema.findByPk(resourceKey);
const property = schema.toProperty();
const properties = await schema.getProperties();
if (Object.keys(properties).length) {
property.properties = properties;
}
ctx.body = property;
} else {
const schemas = await UISchema.findAll(UISchema.parseApiJson({
filter,
}));
let properties = {};
for (const schema of schemas) {
const property = schema.toProperty();
const properties = await schema.getProperties();
if (Object.keys(properties).length) {
property.properties = properties;
}
properties[property.name] = property;
}
ctx.body = {
type: 'object',
properties,
}
}
ctx.body = property;
await next();
}

View File

@ -42,7 +42,7 @@ export class UISchema extends Model {
toProperty() {
const options = this.get('options') || {};
const data = _.omit(this.toJSON(), ['created_at', 'updated_at', 'options', 'parentKey']);
const data = _.omit(this.toJSON(), ['created_at', 'updated_at', 'options']);
return { ...data, ...options };
}