improve action & database field & table

This commit is contained in:
chenos 2021-07-06 23:33:02 +08:00
parent b7a2d5d3e5
commit 14160761d3
7 changed files with 902 additions and 347 deletions

View File

@ -40,7 +40,7 @@ import { Menu } from '../menu';
import { Password } from '../password';
import { Radio } from '../radio';
import { Select } from '../select';
import { Table } from '../table';
import { Table, useTableCreateAction, useTableDestroyAction } from '../table';
import { Tabs } from '../tabs';
import { TimePicker } from '../time-picker';
import { Upload } from '../upload';
@ -59,6 +59,8 @@ export const scope = {
useLogin,
useRegister,
useSubmit,
useTableCreateAction,
useTableDestroyAction,
};
export const components = {

View File

@ -29,7 +29,7 @@ group:
import React from 'react';
import { SchemaRenderer, registerScope } from '../';
function useCustomAction() {
function useAction() {
return {
run() {
alert('这是自定义的操作逻辑');
@ -37,17 +37,13 @@ function useCustomAction() {
}
}
registerScope({
useCustomAction,
});
const schema = {
type: 'void',
name: 'action1',
title: '按钮',
'x-component': 'Action',
'x-component-props': {
useAction: '{{ useCustomAction }}'
useAction,
},
};
@ -188,6 +184,17 @@ export default () => {
```tsx
import React from 'react';
import { SchemaRenderer } from '../';
import { useVisibleContext } from './';
function useAction() {
const { visible, setVisible } = useVisibleContext();
return {
async run() {
console.log({ visible })
setVisible(false);
}
}
}
const schema = {
type: 'void',
@ -200,88 +207,21 @@ const schema = {
title: '弹窗标题',
'x-component': 'Action.Modal',
'x-component-props': {},
properties: {
},
},
},
};
export default () => {
return <SchemaRenderer schema={schema} />
}
```
## Action.Container - 指定容器内打开
```tsx
import React, { useRef } from 'react';
import { SchemaRenderer } from '../';
import { ActionContext } from './';
const schema = {
type: 'void',
name: 'action1',
title: '按钮',
'x-component': 'Action',
properties: {
container1: {
type: 'void',
title: '页面标题',
'x-component': 'Action.Container',
properties: {
input: {
type: 'string',
title: '字段',
'x-designable-bar': 'FormItem.DesignableBar',
'x-decorator': 'FormItem',
'x-component': 'Input',
}
},
},
},
};
export default () => {
const ref = useRef();
console.log('containerRef2222', ref)
return (
<div>
<ActionContext.Provider value={{
containerRef: ref,
}}>
<SchemaRenderer schema={schema} />
</ActionContext.Provider>
<div style={{padding: '8px 0'}}>目标容器:</div>
<div ref={ref} id={'container'} style={{ border: '1px dashed #ebedf1', background: '#fafafa', padding: 24 }}/>
</div>
)
}
```
## Action.DesignableBar - 按钮配置操作栏
```tsx
import React from 'react';
import { SchemaRenderer } from '../';
const schema = {
type: 'void',
name: 'action1',
title: '按钮',
'x-component': 'Action',
'x-designable-bar': 'Action.DesignableBar',
properties: {
drawer1: {
type: 'void',
title: '抽屉标题',
'x-component': 'Action.Drawer',
'x-component-props': {},
properties: {
input: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
close: {
type: 'void',
name: 'action1',
title: '关闭',
'x-component': 'Action',
'x-component-props': {
useAction,
},
},
action2: {
type: 'void',
title: '打开二级抽屉',
@ -291,7 +231,7 @@ const schema = {
drawer1: {
type: 'void',
title: '二级抽屉标题',
'x-component': 'Action.Drawer',
'x-component': 'Action.Modal',
'x-component-props': {},
properties: {
input: {
@ -311,3 +251,45 @@ export default () => {
return <SchemaRenderer schema={schema} />
}
```
## Action.Container - 指定容器内打开
```tsx
import React, { useRef } from 'react';
import { SchemaRenderer } from '../';
import { ActionContext } from './';
export default () => {
const containerRef = useRef();
const schema = {
type: 'void',
name: 'action1',
title: '按钮',
'x-component': 'Action',
'x-component-props': {
containerRef
},
properties: {
container1: {
type: 'void',
title: '页面标题',
'x-component': 'Action.Container',
properties: {
input: {
type: 'string',
title: '字段',
'x-designable-bar': 'FormItem.DesignableBar',
'x-decorator': 'FormItem',
'x-component': 'Input',
}
},
},
},
};
return (
<div>
<SchemaRenderer schema={schema} />
</div>
)
}
```

View File

@ -10,9 +10,8 @@ import {
Schema,
useField,
} from '@formily/react';
import { Button, Dropdown, Menu, Popover, Space } from 'antd';
import { Button, Dropdown, Menu, Popover, Space, Drawer, Modal } from 'antd';
import { Link, useHistory, LinkProps } from 'react-router-dom';
import Drawer from '../../components/Drawer';
import { SchemaRenderer, useDesignable } from '../';
import ReactDOM from 'react-dom';
import get from 'lodash/get';
@ -22,6 +21,7 @@ import { useMount } from 'ahooks';
import { uid } from '@formily/shared';
import './style.less';
import constate from 'constate';
export function useDefaultAction() {
return {
@ -95,92 +95,81 @@ function useDesignableBar() {
};
}
export const ActionContext = createContext({ containerRef: null });
const [VisibleProvider, useVisibleContext] = constate(
(props: any = {}) => {
const { initialVisible = false, containerRef = null } = props;
const [visible, setVisible] = useState(initialVisible);
export const Action: ActionType = observer((props) => {
return { containerRef, visible, setVisible };
},
);
export { VisibleProvider, useVisibleContext };
const BaseAction = observer((props: any) => {
const { useAction = useDefaultAction, ...others } = props;
const { containerRef } = useContext(ActionContext);
const field = useField();
const schema = useFieldSchema();
const { run } = useAction();
const { DesignableBar } = useDesignableBar();
const { setVisible } = useVisibleContext();
const schema = useFieldSchema();
const renderContainer = () => {
let childSchema = null;
if (schema.properties) {
const key = Object.keys(schema.properties).shift();
const current = schema.properties[key];
childSchema = current;
}
if (childSchema && childSchema['x-component'] === 'Action.Container') {
containerRef &&
ReactDOM.render(
<div>
<SchemaRenderer schema={childSchema} onlyRenderProperties />
</div>,
containerRef.current,
);
}
};
useMount(() => {
renderContainer();
});
let childSchema = null;
if (schema.properties) {
const key = Object.keys(schema.properties).shift();
const current = schema.properties[key];
childSchema = current;
}
console.log({ schema });
if (childSchema && childSchema['x-component'] === 'Action.Popover') {
return (
<Popover
trigger={['click']}
title={childSchema.title}
{...childSchema['x-component-props']}
content={
<div>
<SchemaRenderer schema={childSchema} onlyRenderProperties />
</div>
}
>
<Button {...others}>{field.title}</Button>
</Popover>
);
}
return (
const renderButton = () => (
<Button
{...others}
onClick={async () => {
if (!childSchema) {
return await run();
}
if (childSchema['x-component'] === 'Action.Drawer') {
Drawer.open({
title: childSchema.title,
...(childSchema['x-component-props'] || {}),
content: () => {
return (
<div>
<SchemaRenderer schema={childSchema} onlyRenderProperties />
</div>
);
},
});
}
if (childSchema['x-component'] === 'Action.Container') {
renderContainer();
}
setVisible && setVisible(true);
await run();
}}
>
{field.title}
<DesignableBar />
</Button>
);
const popover = schema.reduceProperties((items, current) => {
if (current['x-component'] === 'Action.Popover') {
return current;
}
return null;
}, null);
if (popover) {
return (
<RecursionField
schema={
new Schema({
type: 'object',
properties: {
[popover.name]: {
...popover.toJSON(),
'x-button': renderButton(),
},
},
})
}
/>
);
}
return (
<>
{renderButton()}
{props.children}
</>
);
});
export const Action: ActionType = observer((props: any) => {
const schema = useFieldSchema();
if (schema.properties) {
return (
<VisibleProvider containerRef={props.containerRef}>
<BaseAction {...props} />
</VisibleProvider>
);
}
return <BaseAction {...props} />;
});
Action.Link = observer((props) => {
@ -198,21 +187,51 @@ Action.URL = observer((props) => {
);
});
Action.Container = ({ children }) => {
return children;
};
Action.Container = observer((props) => {
const { visible, setVisible } = useVisibleContext();
return visible && <div>{props.children}</div>;
});
Action.Popover = ({ children }) => {
return children;
};
Action.Popover = observer((props) => {
const schema = useFieldSchema();
return (
<Popover {...props} content={props.children}>
{schema['x-button']}
</Popover>
);
});
Action.Drawer = ({ children }) => {
return children;
};
Action.Drawer = observer((props) => {
const field = useField();
const { visible, setVisible } = useVisibleContext();
return (
<Drawer
title={field.title}
width={'50%'}
{...props}
visible={visible}
onClose={() => setVisible(false)}
>
{props.children}
</Drawer>
);
});
Action.Modal = ({ children }) => {
return children;
};
Action.Modal = observer((props) => {
const field = useField();
const { visible, setVisible } = useVisibleContext();
return (
<Modal
title={field.title}
width={'50%'}
{...props}
visible={visible}
onCancel={() => setVisible(false)}
>
{props.children}
</Modal>
);
});
Action.DesignableBar = () => {
const field = useField();

View File

@ -16,6 +16,7 @@ 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',
@ -50,73 +51,13 @@ const form = createForm();
export default observer(() => {
return (
<div>
{JSON.stringify(form.values, null, 2)}
<SchemaRenderer form={form} schema={schema} />
<Editor
height="200px"
defaultLanguage="json"
value={JSON.stringify(form.values, null, 2)}
/>
</div>
)
})
```
```tsx
import React from 'react';
import { SchemaRenderer } from '../';
const schema = {
type: 'object',
properties: {
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',
},
},
},
}
};
export default () => {
return <SchemaRenderer schema={schema} />
}
```

View File

@ -11,13 +11,7 @@ import {
import { ArrayCollapse } from '@formily/antd';
import { uid } from '@formily/shared';
import '@formily/antd/lib/form-tab/style';
import { Collapse, Button } from 'antd';
const text = `
A dog is a type of domesticated animal.
Known for its loyalty and faithfulness,
it can be found as a welcome guest in many households across the world.
`;
import { Collapse, Button, Dropdown, Menu } from 'antd';
const interfaces = new Map<string, ISchema>();
@ -33,6 +27,21 @@ interfaces.set('string', {
} as ISchema,
},
properties: {
'ui.title': {
type: 'string',
title: '字段名称',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
name: {
type: 'string',
title: '字段标识',
required: true,
'x-disabled': true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
type: {
type: 'string',
title: '数据类型',
@ -44,20 +53,6 @@ interfaces.set('string', {
{ label: 'Text', value: 'text' },
],
},
name: {
type: 'string',
title: '字段标识',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
'ui.title': {
type: 'string',
title: '字段名称',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
'ui.required': {
type: 'string',
title: '必填',
@ -79,6 +74,21 @@ interfaces.set('textarea', {
} as ISchema,
},
properties: {
'ui.title': {
type: 'string',
required: true,
title: '字段名称',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
name: {
type: 'string',
required: true,
title: '字段标识',
'x-disabled': true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
type: {
type: 'string',
title: '数据类型',
@ -90,13 +100,28 @@ interfaces.set('textarea', {
{ label: 'Text', value: 'text' },
],
},
name: {
'ui.required': {
type: 'string',
required: true,
title: '字段标识',
title: '必填',
'x-decorator': 'FormItem',
'x-component': 'Input.TextArea',
'x-component': 'Checkbox',
},
},
});
interfaces.set('subTable', {
type: 'object',
default: {
type: 'string',
// name,
ui: {
type: 'string',
// title,
'x-component': 'Select',
enum: [],
} as ISchema,
},
properties: {
'ui.title': {
type: 'string',
required: true,
@ -104,6 +129,170 @@ interfaces.set('textarea', {
'x-decorator': 'FormItem',
'x-component': 'Input',
},
name: {
type: 'string',
required: true,
title: '字段标识',
'x-disabled': true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
type: {
type: 'string',
title: '数据类型',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Select',
enum: [
{ label: 'String', value: 'string' },
{ label: 'Text', value: 'text' },
{ label: 'HasMany', value: 'hasMany' },
],
},
'children': {
type: 'array',
title: '子表格字段',
'x-decorator': 'FormItem',
'x-component': 'DatabaseField',
},
// 'ui.required': {
// type: 'string',
// title: '必填',
// 'x-decorator': 'FormItem',
// 'x-component': 'Checkbox',
// },
},
});
interfaces.set('select', {
type: 'object',
default: {
type: 'string',
// name,
ui: {
type: 'string',
// title,
'x-component': 'Select',
enum: [],
} as ISchema,
},
properties: {
'ui.title': {
type: 'string',
required: true,
title: '字段名称',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
name: {
type: 'string',
required: true,
title: '字段标识',
'x-disabled': true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
type: {
type: 'string',
title: '数据类型',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Select',
enum: [
{ label: 'String', value: 'string' },
{ label: 'Text', value: 'text' },
],
},
'ui.enum': {
type: 'array',
title: '可选项',
'x-decorator': 'FormItem',
'x-component': 'ArrayTable',
'x-component-props': {
pagination: false,
// scroll: { x: '100%' },
},
items: {
type: 'object',
properties: {
column1: {
type: 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': { width: 50, title: '', align: 'center' },
properties: {
sort: {
type: 'void',
'x-component': 'ArrayTable.SortHandle',
},
},
},
column2: {
type: 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': { title: '选项值' },
properties: {
value: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
column3: {
type: 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': { title: '选项' },
properties: {
label: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
column4: {
type: 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': { title: '颜色' },
properties: {
color: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'ColorSelect',
},
},
},
column5: {
type: 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': {
title: '',
dataIndex: 'operations',
fixed: 'right',
},
properties: {
item: {
type: 'void',
'x-component': 'FormItem',
properties: {
remove: {
type: 'void',
'x-component': 'ArrayTable.Remove',
},
},
},
},
},
},
},
properties: {
add: {
type: 'void',
'x-component': 'ArrayTable.Addition',
title: '添加可选项',
},
},
},
'ui.required': {
type: 'string',
title: '必填',
@ -116,65 +305,101 @@ interfaces.set('textarea', {
export const DatabaseField: any = observer((props) => {
const field = useField<Formily.Core.Models.ArrayField>();
console.log('DatabaseField', field.componentProps);
const [activeKey, setActiveKey] = useState(null);
return (
<div>
<Collapse
// activeKey={field.componentProps.activeKey}
{...props}
onChange={(key) => {
field.setComponentProps({
activeKey: key,
});
}}
accordion
>
<Collapse activeKey={activeKey} onChange={(key) => {
setActiveKey(key);
}} accordion>
{field.value?.map((item, index) => {
const schema = interfaces.get(item.interface);
console.log({ schema });
return (
<Collapse.Panel
extra={[
<Button onClick={() => field.moveUp(index)}>Up</Button>,
<Button
onClick={() => {
field.remove(index);
field.insert(index, {
...item,
interface: 'textarea',
});
}}
>
Update
</Button>,
]}
header={item.name}
// extra={[
// <Button onClick={() => field.moveUp(index)}>Up</Button>,
// <Button
// onClick={() => {
// field.remove(index);
// field.insert(index, {
// ...item,
// interface: 'textarea',
// });
// }}
// >
// Update
// </Button>,
// ]}
header={
<>
{item.ui && item.ui.title}
{' '}
{item.name}
</>
}
key={item.id}
>
<RecursionField
key={`${item.id}_${index}`}
name={index}
schema={new Schema(schema)}
schema={new Schema({
type: 'object',
properties: {
layout: {
type: 'void',
'x-component': 'FormLayout',
'x-component-props': {
layout: 'vertical',
// labelCol: 4,
// wrapperCol: 20,
},
properties: schema.properties,
},
},
})}
/>
</Collapse.Panel>
);
})}
</Collapse>
<Button
block
type={'dashed'}
style={{ marginTop: 10 }}
onClick={() => {
field.push({
id: uid(),
type: 'string',
interface: 'string',
name: uid(),
});
console.log('field.value', field.value);
<Dropdown
placement={'bottomCenter'}
overlayStyle={{
minWidth: 200,
}}
overlay={
<Menu
onClick={info => {
const schema = interfaces.get(info.key);
if (!schema) {
return;
}
const data = {
id: uid(),
name: uid(),
interface: info.key,
...schema.default
};
field.push(data);
setActiveKey(data.id);
console.log('info.key', info.key, schema);
}}
>
<Menu.Item key={'string'}></Menu.Item>
<Menu.Item key={'textarea'}></Menu.Item>
<Menu.Item key={'select'}></Menu.Item>
<Menu.Item key={'subTable'}></Menu.Item>
</Menu>
}
>
</Button>
<Button
block
type={'dashed'}
style={{ marginTop: 10 }}
>
</Button>
</Dropdown>
</div>
);
});

View File

@ -9,18 +9,26 @@ group:
path: /client/components
---
# Table - 表格
### Table.View
只做数据展示,不能用作表单字段
```tsx
import React from 'react';
import { SchemaRenderer } from '../';
import { uid } from '@formily/shared';
import Editor from '@monaco-editor/react';
import { createForm } from '@formily/core';
import { observer } from '@formily/react';
const schema = {
name: `t_${uid()}`,
type: 'array',
'x-component': 'Table',
default: [{ field1: 'a1', field2: 'b1' }, { field1: 'a2', field2: 'b2' }],
type: 'void',
'x-component': 'Table.View',
// default: [
// { key: uid(), field1: 'a1', field2: 'b1' },
// { key: uid(), field1: 'a2', field2: 'b2' },
// ],
'x-component-props': {},
properties: {
[`a_${uid()}`]: {
@ -28,17 +36,64 @@ const schema = {
'x-component': 'Table.ActionBar',
properties: {
action1: {
type: 'object',
title: '按钮1',
'x-component': 'Table.BulkAction.Popover',
'x-component-props': {},
properties: {},
type: 'void',
name: 'action1',
title: '筛选',
'x-component': 'Action',
properties: {
popover1: {
type: 'void',
title: '弹窗标题',
'x-component': 'Action.Popover',
'x-component-props': {},
properties: {
input: {
type: 'string',
'x-component': 'Input',
}
},
},
},
},
action2: {
type: 'void',
title: '按钮2',
'x-component': 'Table.BulkAction',
'x-component-props': {},
name: 'action1',
title: '新增',
'x-component': 'Action',
'x-designable-bar': 'Action.DesignableBar',
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',
name: 'action1',
title: '提交',
'x-component': 'Action',
'x-component-props': {
'useAction': '{{ useTableCreateAction }}'
},
}
},
},
},
},
action3: {
type: 'void',
name: 'action1',
title: '删除',
'x-component': 'Action',
'x-component-props': {
'useAction': '{{ useTableDestroyAction }}'
},
},
},
},
@ -65,8 +120,6 @@ const schema = {
'x-component': 'Table.Column',
'x-component-props': {
title: 'z1',
width: 100,
// DesignableBar: 'Table.Column.DesignableBar',
},
'x-designable-bar': 'Table.Column.DesignableBar',
properties: {
@ -101,9 +154,172 @@ const schema = {
},
};
export default () => {
const form = createForm();
export default observer(() => {
return (
<SchemaRenderer schema={schema} />
);
<div>
<SchemaRenderer form={form} schema={schema} />
<Editor
height="200px"
defaultLanguage="json"
value={JSON.stringify(form.values, null, 2)}
/>
</div>
)
});
```
### Table.Field
可用作表单字段
```tsx
import React from 'react';
import { SchemaRenderer } from '../';
import { uid } from '@formily/shared';
import Editor from '@monaco-editor/react';
import { createForm } from '@formily/core';
import { observer } from '@formily/react';
const schema = {
name: `t_${uid()}`,
type: 'array',
'x-component': 'Table.Field',
// default: [
// { key: uid(), field1: 'a1', field2: 'b1' },
// { key: uid(), field1: 'a2', field2: 'b2' },
// ],
'x-component-props': {},
properties: {
[`a_${uid()}`]: {
type: 'void',
'x-component': 'Table.ActionBar',
properties: {
action1: {
type: 'void',
name: 'action1',
title: '筛选',
'x-component': 'Action',
properties: {
popover1: {
type: 'void',
title: '弹窗标题',
'x-component': 'Action.Popover',
'x-component-props': {},
properties: {
input: {
type: 'string',
'x-component': 'Input',
}
},
},
},
},
action2: {
type: 'void',
name: 'action1',
title: '新增',
'x-component': 'Action',
'x-designable-bar': 'Action.DesignableBar',
properties: {
drawer1: {
type: 'void',
title: '抽屉标题',
'x-component': 'Action.Drawer',
'x-component-props': {},
properties: {
action2: {
type: 'void',
name: 'action1',
title: '提交',
'x-component': 'Action',
'x-component-props': {
},
}
},
},
},
},
action3: {
type: 'void',
name: 'action1',
title: '删除',
'x-component': 'Action',
'x-component-props': {
},
},
},
},
[`a_${uid()}`]: {
type: 'void',
'x-component': 'Table.ActionBar',
'x-component-props': {
align: 'bottom',
},
properties: {
pagination: {
type: 'void',
'x-component': 'Table.Pagination',
'x-component-props': {
defaultCurrent: 1,
total: 50,
},
},
},
},
[`c_${uid()}`]: {
type: 'void',
title: '字段1',
'x-component': 'Table.Column',
'x-component-props': {
title: 'z1',
},
'x-designable-bar': 'Table.Column.DesignableBar',
properties: {
field1: {
type: 'string',
required: true,
'x-decorator-props': {
feedbackLayout: 'popover',
},
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
[`c_${uid()}`]: {
type: 'void',
title: '字段2',
'x-component': 'Table.Column',
properties: {
field2: {
type: 'string',
title: '字段2',
required: true,
'x-decorator-props': {
feedbackLayout: 'popover',
},
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
};
const form = createForm();
export default observer(() => {
return (
<div>
<SchemaRenderer form={form} schema={schema} />
<Editor
height="200px"
defaultLanguage="json"
value={JSON.stringify(form.values, null, 2)}
/>
</div>
)
});
```

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { createContext, useContext, useState } from 'react';
import {
useFieldSchema,
Schema,
@ -6,13 +6,18 @@ import {
RecursionField,
useField,
} from '@formily/react';
import { Table as AntdTable } from 'antd';
import { Button, Pagination, Space, Table as AntdTable } from 'antd';
import { DesignableBar } from './DesignableBar';
import { ColumnDesignableBar } from './ColumnDesignableBar';
import { uid } from '@formily/shared';
import useRequest from '@ahooksjs/use-request';
import constate from 'constate';
import { SchemaRenderer } from '../';
import { useVisibleContext } from '../action';
function useTableColumns(props) {
function useTableColumns(props?: any) {
const schema = useFieldSchema();
const { dataSource } = props;
const { dataSource } = props || {};
function findColumns(schema: Schema): Schema[] {
return schema.reduceProperties((columns, current) => {
@ -29,14 +34,14 @@ function useTableColumns(props) {
title: item.title,
dataIndex: item.name,
...columnProps,
render(value, record) {
console.log({ item });
const index = dataSource.indexOf(record);
render(value, record, index) {
// console.log({ item });
// const index = dataSource.indexOf(record);
item.mapProperties((s) => {
s['title'] = undefined;
s['x-read-pretty'] = true;
return s;
})
});
return (
<RecursionField schema={item} name={index} onlyRenderProperties />
);
@ -74,35 +79,200 @@ function useTableActionBars() {
return findActionBars(schema);
}
function useTableDetails() {
const schema = useFieldSchema();
function findDetails(schema: Schema) {
return schema.reduceProperties((items, current) => {
if (current['x-component'] === 'Table.Details') {
return [...items, current];
}
return [...items, ...findDetails(current)];
}, []);
}
return findDetails(schema);
function useTable() {
const field = useField<Formily.Core.Models.ArrayField>();
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const response = useRequest<any>(
() => {
return new Promise((resolve) => {
console.log('useRequest');
setTimeout(() => {
resolve([
{ key: uid(), field1: uid(), field2: uid() },
{ key: uid(), field1: uid(), field2: uid() },
]);
}, 500);
});
},
{
onSuccess(data) {
field.setValue(data);
},
},
);
return {
selectedRowKeys,
setSelectedRowKeys,
mutate: response.mutate,
response,
async create() {
response.refresh()
},
async update() {
response.refresh()
},
async destroy() {
response.refresh()
},
} as any;
}
export const Table: any = observer((props) => {
const [TableContextProvider, useTableContext] = constate(useTable);
export function useTableCreateAction() {
const { visible, setVisible } = useVisibleContext()
const { create } = useTableContext();
return {
run: async () => {
setVisible(false);
await create();
console.log({ visible })
},
}
}
export function useTableDestroyAction() {
// const { setVisible } = useVisibleContext()
const { destroy } = useTableContext();
return {
run: async () => {
// setVisible(false);
await destroy();
},
}
}
const ArrayTable = (props) => {
const field = useField<Formily.Core.Models.ArrayField>();
const dataSource = field.value;
const columns = useTableColumns({ dataSource });
// const dataSource = Array.isArray(field.value) ? field.value.slice() : [];
const columns = useTableColumns();
const actionBars = useTableActionBars();
const details = useTableDetails();
console.log({ columns, actionBars, details });
const {
selectedRowKeys,
setSelectedRowKeys,
response = {},
create,
} = useTableContext();
console.log({ response });
const { data = [], loading, mutate } = response as any;
return (
<>
<AntdTable columns={columns} dataSource={dataSource} />
</>
<div>
{actionBars.top.map((actionBarSchema) => {
return (
<RecursionField
onlyRenderProperties
schema={
new Schema({
type: 'object',
properties: {
[actionBarSchema.name]: actionBarSchema,
},
})
}
/>
);
})}
<Button
onClick={() => {
create();
// field.unshift({ key: uid(), field1: uid(), field2: uid() });
// const dataSource = Array.isArray(field.value)
// ? field.value.slice()
// : [];
// mutate(dataSource);
// response.refresh();
// console.log({ selectedRowKeys })
}}
>
</Button>
<AntdTable
rowKey={'key'}
loading={loading}
pagination={false}
columns={columns}
dataSource={data}
rowSelection={{
type: 'checkbox',
selectedRowKeys,
onChange: (keys) => {
setSelectedRowKeys(keys);
},
}}
/>
{actionBars.bottom.map((actionBarSchema) => {
return (
<RecursionField
onlyRenderProperties
schema={
new Schema({
type: 'object',
properties: {
[actionBarSchema.name]: actionBarSchema,
},
})
}
/>
);
})}
</div>
);
};
export const Table: any = observer((props) => {
return (
<TableContextProvider>
<ArrayTable {...props} />
</TableContextProvider>
);
});
Table.Column = () => null;
Table.Column.DesignableBar = ColumnDesignableBar;
Table.DesignableBar = DesignableBar;
Table.ActionBar = observer((props) => {
return (
<div className={'action-bar'}>
<Space>{props.children}</Space>
</div>
);
});
Table.Pagination = observer((props) => {
const { response } = useTableContext();
return (
<Pagination
{...props}
onChange={(page, pageSize) => {
response.refresh();
// console.log('useRequest', { page, pageSize });
}}
/>
);
});
Table.View = observer(() => {
const schema = useFieldSchema();
console.log('Table.View', schema);
return (
<SchemaRenderer
schema={{
type: 'object',
properties: {
[schema.name]: {
...schema.toJSON(),
type: 'array',
'x-component': 'Table.Field',
},
},
}}
/>
);
});
Table.Field = observer((props) => {
return (
<TableContextProvider>
<ArrayTable {...props} />
</TableContextProvider>
);
});