lots of updates

This commit is contained in:
chenos 2021-07-08 14:21:18 +08:00
parent ed4397e112
commit e53a1edf7d
72 changed files with 893 additions and 387 deletions

View File

@ -1,283 +0,0 @@
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: [
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// ],
'x-component-props': {
rowKey: 'key',
// isRemoteDataSource: true,
},
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: {
[uid()]: {
name: 'form',
type: 'object',
'x-component': 'Form',
properties: {
filter: {
type: 'string',
title: '字段1',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
action: {
type: 'void',
title: '提交',
'x-component': 'Action',
'x-component-props': {
'useAction': '{{ useTableFilterAction }}'
},
}
}
},
},
},
},
},
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: {
[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': '{{ useTableCreateAction }}'
},
}
}
},
},
},
},
},
action3: {
type: 'void',
name: 'action1',
title: '删除',
'x-component': 'Action',
'x-component-props': {
'useAction': '{{ useTableDestroyAction }}'
},
},
},
},
[`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,
},
},
},
},
[`c_${uid()}`]: {
type: 'void',
title: '排序',
'x-component': 'Table.Column',
properties: {
sort: {
type: 'void',
'x-component': 'Table.SortHandle',
},
},
},
[`c_${uid()}`]: {
type: 'void',
title: '序号',
'x-component': 'Table.Column',
properties: {
index: {
type: 'void',
'x-component': 'Table.Index',
},
},
},
[`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-read-pretty': 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-read-pretty': true,
'x-decorator-props': {
feedbackLayout: 'popover',
},
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
[`col_${uid()}`]: {
type: 'void',
title: '操作',
'x-component': 'Table.Column',
properties: {
action3: {
type: 'void',
title: '删除',
'x-component': 'Action',
'x-component-props': {
'useAction': '{{ useTableDestroyAction }}'
},
},
action2: {
type: 'void',
name: 'action1',
title: '修改',
'x-component': 'Action',
'x-default-action': true,
'x-designable-bar': 'Action.DesignableBar',
properties: {
drawer1: {
type: 'void',
title: '编辑表单',
'x-component': 'Action.Drawer',
'x-component-props': {},
properties: {
[uid()]: {
name: 'form',
type: 'void',
'x-component': 'Form',
'x-component-props': {
'useValues': '{{ 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': '{{ useTableUpdateAction }}'
},
}
}
},
},
},
},
},
},
},
},
};
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

@ -4,7 +4,7 @@ import { useMouseEvents, useWillUnmount } from 'beautiful-react-hooks';
import { useField, useFieldSchema } from '@formily/react';
import { useMount } from 'ahooks';
import constate from 'constate';
import { useSchemaPath } from '../../blocks';
import { useSchemaPath } from '../../schemas';
export const DraggableBlockContext = createContext({
dragRef: null,

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# Action - 操作
@ -94,6 +94,107 @@ export default () => {
}
```
## Action.Dropdown - 下拉操作
```tsx
import React from 'react';
import { SchemaRenderer } from '../';
function useAction() {
return {
run() {
alert('这是自定义的操作逻辑');
}
}
}
const popoverSchema = {
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',
}
},
},
},
};
const drawerSchema = {
type: 'void',
title: '按钮',
'x-component': 'Action',
properties: {
drawer1: {
type: 'void',
title: '抽屉标题',
'x-component': 'Action.Drawer',
'x-component-props': {},
properties: {
input: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
action2: {
type: 'void',
title: '打开二级抽屉',
// 'x-decorator': 'FormItem',
'x-component': 'Action',
properties: {
drawer1: {
type: 'void',
title: '二级抽屉标题',
'x-component': 'Action.Drawer',
'x-component-props': {},
properties: {
input: {
type: 'string',
'x-component': 'Input',
},
},
},
},
}
},
},
},
};
const schema = {
type: 'void',
name: 'action1',
title: '下拉菜单',
'x-component': 'Action.Dropdown',
properties: {
action1: {
type: 'void',
name: 'action1',
title: '按钮',
'x-component': 'Action',
'x-component-props': {
useAction,
},
},
action2: drawerSchema,
action3: popoverSchema,
},
};
export default () => {
return <SchemaRenderer schema={schema} />
}
```
## Action.Popover - 打开气泡
```tsx

View File

@ -12,10 +12,10 @@ import {
} from '@formily/react';
import { Button, Dropdown, Menu, Popover, Space, Drawer, Modal } from 'antd';
import { Link, useHistory, LinkProps } from 'react-router-dom';
import { SchemaRenderer, useDesignable } from '../';
import { SchemaRenderer, useDesignable } from '..';
import ReactDOM from 'react-dom';
import get from 'lodash/get';
import { MenuOutlined } from '@ant-design/icons';
import { MenuOutlined, DownOutlined } from '@ant-design/icons';
import classNames from 'classnames';
import { useMount } from 'ahooks';
import { uid } from '@formily/shared';
@ -78,6 +78,7 @@ export type ActionType = React.FC<ActionProps> & {
Drawer?: React.FC<any>;
Modal?: React.FC<any>;
Dropdown?: React.FC<any>;
Group?: React.FC<any>;
DesignableBar?: React.FC<any>;
};
@ -115,7 +116,7 @@ const BaseAction = observer((props: any) => {
field.componentProps.setVisible = setVisible;
}, []);
console.log('BaseAction', { field, schema }, field.title)
console.log('BaseAction', { field, schema }, field.title);
const renderButton = () => (
<Button
@ -168,7 +169,10 @@ export const Action: ActionType = observer((props: any) => {
const schema = useFieldSchema();
if (schema.properties) {
return (
<VisibleProvider initialVisible={props.initialVisible} containerRef={props.containerRef}>
<VisibleProvider
initialVisible={props.initialVisible}
containerRef={props.containerRef}
>
<BaseAction {...props} />
</VisibleProvider>
);
@ -218,7 +222,7 @@ Action.Drawer = observer((props) => {
const { visible, setVisible } = useVisibleContext();
return (
<Drawer
onClick={e => {
onClick={(e) => {
e.stopPropagation();
}}
title={field.title}
@ -227,7 +231,7 @@ Action.Drawer = observer((props) => {
visible={visible}
onClose={(e) => {
e.stopPropagation();
setVisible(false)
setVisible(false);
}}
>
{props.children}
@ -240,17 +244,46 @@ Action.Modal = observer((props) => {
const { visible, setVisible } = useVisibleContext();
return (
<Modal
// onClick={e => {
// e.stopPropagation();
// }}
title={field.title}
width={'50%'}
{...props}
visible={visible}
onCancel={() => setVisible(false)}
closable
maskClosable
onCancel={(e) => {
e.stopPropagation();
setVisible(false);
}}
footer={null}
>
{props.children}
<div onClick={(e) => e.stopPropagation()}>{props.children}</div>
</Modal>
);
});
Action.Dropdown = observer((props) => {
// const { visible, setVisible } = useVisibleContext();
const schema = useFieldSchema();
return (
<Popover
// visible={visible}
// onVisibleChange={(visible) => {
// setVisible(visible);
// }}
{...props}
overlayClassName={'nb-action-group'}
// trigger={'click'}
content={props.children}
placement={'bottomLeft'}
>
<Button>{schema.title}</Button>
</Popover>
);
});
Action.DesignableBar = () => {
const field = useField();
const schema = useFieldSchema();

View File

@ -51,3 +51,32 @@
}
}
}
.ant-popover.nb-action-group {
z-index: 1000;
padding-top: 0;
.ant-popover-arrow {
display: none;
}
.ant-popover-inner-content {
padding: 4px 0;
.ant-btn {
display: block;
min-width: 100px;
width: 100%;
border: 0;
border-radius: 0;
text-align: left;
padding: 5px 12px;
color: #000;
&:hover {
background-color: #f5f5f5;
}
&[disabled] {
color: #00000040;
background-color:#fff;
cursor:not-allowed;
}
}
}
}

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# AddNew - 新增

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# Cascader - 级联选择

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# Checkbox - 多选框

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# ColorSelect - 颜色选择器

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# DatabaseField - 数据表字段

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# DatePicker - 日期选择器

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# DrawerSelect - 抽屉选择器

View File

@ -1,5 +1,5 @@
import React, { useMemo, useState } from 'react';
import { SchemaField } from '../';
import { SchemaField } from '..';
import { createForm, onFieldChange, onFieldReact } from '@formily/core'
import { FormProvider, FormConsumer } from '@formily/react'
import { Field } from '@formily/core/esm/models/Field';

View File

@ -5,6 +5,6 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# Form - 表单

View File

@ -8,7 +8,7 @@ import {
RecursionField,
useField,
} from '@formily/react';
import { SchemaRenderer } from '../../';
import { SchemaRenderer } from '../..';
const schema: ISchema = {
type: 'void',

View File

@ -6,7 +6,7 @@ nav:
group:
order: 2
title: Blocks
path: /client/components
path: /client/schemas
---
# Grid - 栅格

View File

@ -29,7 +29,7 @@ import {
useColResizer,
useDragDropUID,
DragDropManagerProvider,
} from './../../components/drag-and-drop';
} from '../../components/drag-and-drop';
const ColumnSizeContext = createContext(null);

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# IconPicker - 图标选择器

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# InputNumber - 数字框

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# Input - 输入框

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# Markdown 编辑器

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# Menu - 菜单

View File

@ -50,10 +50,11 @@ import { useHistory } from 'react-router-dom';
import cls from 'classnames';
import ReactDOM from 'react-dom';
import { useMount } from 'ahooks';
import { useDesignable, SchemaRenderer } from '../';
import { useDesignable, SchemaRenderer } from '..';
import { Router } from "react-router";
import { useLifecycle } from 'beautiful-react-hooks';
import { useDefaultAction } from '../action';
export type MenuType = React.FC<MenuProps & { hideSubMenu?: boolean }> & {
Item?: React.FC<MenuItemProps>;
@ -273,13 +274,18 @@ Menu.Link = observer((props) => {
);
});
Menu.Item = observer((props) => {
Menu.Item = observer((props: any) => {
const { useAction = useDefaultAction, ...others } = props;
const { run } = useAction();
const field = useField();
const schema = useFieldSchema();
const { DesignableBar } = useDesignableBar();
return (
<AntdMenu.Item
{...props}
{...others}
onClick={async () => {
await run();
}}
schema={schema}
// @ts-ignore
eventKey={schema.name}

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# Password - 密码

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# Radio - 单选框

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# Select - 选择器

View File

@ -0,0 +1,498 @@
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';
function useAction() {
return {
async run() {
alert('自定义')
}
}
}
const schema = {
name: `t_${uid()}`,
type: 'array',
'x-component': 'Table',
// default: [
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// { key: uid(), field1: uid(), field2: uid() },
// ],
'x-component-props': {
rowKey: 'key',
// isRemoteDataSource: true,
},
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: {
[uid()]: {
name: 'form',
type: 'object',
'x-component': 'Form',
properties: {
filter: {
type: 'string',
title: '字段1',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
action: {
type: 'void',
title: '提交',
'x-component': 'Action',
'x-component-props': {
useAction: '{{ useTableFilterAction }}',
},
},
},
},
},
},
},
},
action2: {
type: 'void',
name: 'action1',
title: '新增',
'x-component': 'Action',
'x-designable-bar': 'Action.DesignableBar',
properties: {
drawer1: {
type: 'void',
title: '抽屉标题',
'x-component': 'Action.Modal',
'x-component-props': {},
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: '{{ useTableCreateAction }}',
},
},
},
},
},
},
},
},
action3: {
type: 'void',
name: 'action1',
title: '删除',
'x-component': 'Action',
'x-component-props': {
useAction: '{{ useTableDestroyAction }}',
},
},
},
},
[`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',
'x-component-props': {
align: 'bottom',
},
properties: {
pagination: {
type: 'void',
title: '新增',
'x-component': 'Table.Addition',
'x-component-props': {},
},
},
},
[`c_${uid()}`]: {
type: 'void',
title: '排序',
'x-component': 'Table.Column',
properties: {
sort: {
type: 'void',
'x-component': 'Table.SortHandle',
},
},
},
[`c_${uid()}`]: {
type: 'void',
title: '序号',
'x-component': 'Table.Column',
properties: {
index: {
type: 'void',
'x-component': 'Table.Index',
},
},
},
[`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-read-pretty': 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-read-pretty': true,
'x-decorator-props': {
feedbackLayout: 'popover',
},
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
[`col_${uid()}`]: {
type: 'void',
title: '操作',
'x-component': 'Table.Column',
properties: {
action1: {
type: 'void',
name: 'action1',
title: '查看',
'x-component': 'Action',
'x-default-action': true,
'x-designable-bar': 'Action.DesignableBar',
properties: {
drawer1: {
type: 'void',
title: '查看',
'x-component': 'Action.Modal',
'x-component-props': {},
properties: {
[uid()]: {
type: 'void',
'x-component': 'Tabs',
properties: {
tab1: {
type: 'void',
'x-component': 'Tabs.TabPane',
'x-component-props': {
tab: 'Tab1',
},
properties: {
aaa: {
type: 'string',
title: 'AAA',
'x-decorator': 'FormItem',
required: true,
'x-component': 'Input',
},
},
},
tab2: {
type: 'void',
'x-component': 'Tabs.TabPane',
'x-component-props': {
tab: 'Tab2',
},
properties: {
bbb: {
type: 'string',
title: 'BBB',
'x-decorator': 'FormItem',
required: true,
'x-component': 'Input',
},
},
},
},
},
},
},
},
},
action2: {
type: 'void',
name: 'action1',
title: '修改',
'x-component': 'Action',
'x-default-action': true,
'x-designable-bar': 'Action.DesignableBar',
properties: {
drawer1: {
type: 'void',
title: '编辑表单',
'x-component': 'Action.Modal',
'x-component-props': {},
properties: {
[uid()]: {
name: 'form',
type: 'void',
'x-component': 'Form',
'x-component-props': {
useValues: '{{ 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: '{{ useTableUpdateAction }}',
},
},
},
},
},
},
},
},
action3: {
type: 'void',
title: '删除',
'x-component': 'Action',
'x-component-props': {
useAction: '{{ useTableDestroyAction }}',
},
},
action4: {
type: 'void',
title: '...',
'x-component': 'Action.Dropdown',
properties: {
action5: {
type: 'void',
title: '操作 1',
'x-component': 'Action',
'x-component-props': {
useAction,
disabled: true,
},
},
action4: {
type: 'void',
title: '操作 2',
'x-component': 'Action',
'x-component-props': {
useAction,
},
},
action1: {
type: 'void',
name: 'action1',
title: '查看',
'x-component': 'Action',
// 'x-default-action': true,
'x-designable-bar': 'Action.DesignableBar',
properties: {
drawer1: {
type: 'void',
title: '查看',
'x-component': 'Action.Modal',
'x-component-props': {},
properties: {
[uid()]: {
type: 'void',
'x-component': 'Tabs',
properties: {
tab1: {
type: 'void',
'x-component': 'Tabs.TabPane',
'x-component-props': {
tab: 'Tab1',
},
properties: {
aaa: {
type: 'string',
title: 'AAA',
'x-decorator': 'FormItem',
required: true,
'x-component': 'Input',
},
},
},
tab2: {
type: 'void',
'x-component': 'Tabs.TabPane',
'x-component-props': {
tab: 'Tab2',
},
properties: {
bbb: {
type: 'string',
title: 'BBB',
'x-decorator': 'FormItem',
required: true,
'x-component': 'Input',
},
},
},
},
},
},
},
},
},
action2: {
type: 'void',
name: 'action1',
title: '修改',
'x-component': 'Action',
'x-default-action': true,
'x-designable-bar': 'Action.DesignableBar',
properties: {
drawer1: {
type: 'void',
title: '编辑表单',
'x-component': 'Action.Modal',
'x-component-props': {},
properties: {
[uid()]: {
name: 'form',
type: 'void',
'x-component': 'Form',
'x-component-props': {
useValues: '{{ 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: '{{ useTableUpdateAction }}',
},
},
},
},
},
},
},
},
action3: {
type: 'void',
title: '删除',
'x-component': 'Action',
'x-component-props': {
useAction: '{{ useTableDestroyAction }}',
},
},
},
},
},
},
},
};
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

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# Table - 表格

View File

@ -1,4 +1,4 @@
import React, { createContext, useContext, useState } from 'react';
import React, { createContext, useContext, useRef, useState } from 'react';
import {
useFieldSchema,
Schema,
@ -24,12 +24,17 @@ import { BaseResult } from '@ahooksjs/use-request/lib/types';
import { uid, clone } from '@formily/shared';
import { MenuOutlined } from '@ant-design/icons';
import { useVisibleContext } from '../action';
import { SortableHandle, SortableContainer, SortableElement } from 'react-sortable-hoc'
import cls from 'classnames';
interface TableRowProps {
index: number;
data: any;
}
const SortableRow = SortableElement((props: any) => <tr {...props} />)
const SortableBody = SortableContainer((props: any) => <tbody {...props} />)
const TableRowContext = createContext<TableRowProps>(null);
function usePaginationProps() {
@ -55,6 +60,21 @@ function usePaginationProps() {
return false;
}
function useDefaultAction() {
const schema = useFieldSchema();
function findDefaultAction(schema: Schema): Schema[] {
return schema.reduceProperties((columns, current) => {
if (current['x-default-action']) {
return [...columns, current];
}
return [...columns, ...findDefaultAction(current)];
}, []);
}
return findDefaultAction(schema).shift();
}
function useTableActionBars() {
const schema = useFieldSchema();
@ -127,7 +147,9 @@ export function useTableRow() {
}
export function useTableIndex() {
const { params: { page, pageSize } } = useTableContext();
const {
params: { page, pageSize },
} = useTableContext();
const ctx = useContext(TableRowContext);
const field = useField();
if (!field.componentProps.isRemoteDataSource) {
@ -138,7 +160,8 @@ export function useTableIndex() {
export function useTableDestroyAction() {
const ctx = useContext(TableRowContext);
const { field, selectedRowKeys, setSelectedRowKeys, refresh } = useTableContext();
const { field, selectedRowKeys, setSelectedRowKeys, refresh } =
useTableContext();
const rowKey = field.componentProps.rowKey || 'id';
return {
@ -148,23 +171,19 @@ export function useTableDestroyAction() {
refresh();
return;
}
if (!selectedRowKeys.length) {
return;
}
while (selectedRowKeys.length) {
const key = selectedRowKeys.shift();
const index = findIndex(
field.value,
(item) => item[rowKey] === key,
);
const index = findIndex(field.value, (item) => item[rowKey] === key);
field.remove(index);
}
refresh();
setSelectedRowKeys([]);
}
}
},
};
}
export function useTableFilterAction() {
@ -175,8 +194,8 @@ export function useTableFilterAction() {
async run() {
setVisible && setVisible(false);
refresh();
}
}
},
};
}
export function useTableCreateAction() {
@ -186,11 +205,14 @@ export function useTableCreateAction() {
return {
async run() {
setVisible && setVisible(false);
field.unshift(clone(form.values));
field.push({
key: uid(),
...clone(form.values),
});
form.reset();
exec({...params, page: 1});
}
}
exec({ ...params, page: 1 });
},
};
}
export function useTableUpdateAction() {
@ -201,12 +223,13 @@ export function useTableUpdateAction() {
async run() {
setVisible && setVisible(false);
refresh();
}
}
},
};
}
const [TableContextProvider, useTableContext] = constate(() => {
const field = useField<Formily.Core.Models.ArrayField>();
const schema = useFieldSchema();
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const pagination = usePaginationProps();
const response = useRequest<{
@ -239,7 +262,14 @@ const [TableContextProvider, useTableContext] = constate(() => {
pageSize: pagination.defaultPageSize,
...(response.params[0] || {}),
};
return { ...response, params, field, selectedRowKeys, setSelectedRowKeys };
return {
...response,
params,
field,
schema,
selectedRowKeys,
setSelectedRowKeys,
};
});
export { TableContextProvider, useTableContext };
@ -251,12 +281,26 @@ const TableContainer = observer((props) => {
const { loading, data, refresh, selectedRowKeys, setSelectedRowKeys } =
useTableContext();
const rowKey = field.componentProps.rowKey || 'id';
console.log({ actionBars });
const dataSource = Array.isArray(field.value) ? field.value.slice() : []
const defaultAction = useDefaultAction();
console.log({ defaultAction });
const dataSource = Array.isArray(field.value) ? field.value.slice() : [];
const columns = useTableColumns({ dataSource });
const ref = useRef<HTMLDivElement>()
const addTdStyles = (node: HTMLElement) => {
const helper = document.body.querySelector(`.nb-table-sort-helper`)
if (helper) {
const tds = node.querySelectorAll('td')
requestAnimationFrame(() => {
helper.querySelectorAll('td').forEach((td, index) => {
if (tds[index]) {
td.style.width = getComputedStyle(tds[index]).width
}
})
})
}
}
return (
<div>
<div ref={ref} className={'nb-table'}>
{actionBars.top.map((actionBarSchema) => {
return (
<RecursionField
@ -286,14 +330,56 @@ const TableContainer = observer((props) => {
}}
dataSource={data?.list}
columns={columns}
components={{
body: {
wrapper: (props: any) => (
<SortableBody
useDragHandle
lockAxis="y"
// disableAutoscroll
helperClass={`nb-table-sort-helper`}
helperContainer={() => {
return ref.current?.querySelector('tbody')
}}
onSortStart={({ node }) => {
addTdStyles(node)
}}
onSortEnd={({ oldIndex, newIndex }) => {
field.move(oldIndex, newIndex);
refresh();
}}
{...props}
/>
),
row: (props: any) => {
const index = findIndex(field.value, item => item[rowKey] === props['data-row-key']);
return (
<SortableRow
index={index}
{...props}
/>
)
},
},
}}
onRow={(data) => {
const index = dataSource.indexOf(data);
return {
onClick(e) {
field.query(`.${schema.name}.${index}.action2`).take((f) => {
const setVisible = f.componentProps.setVisible;
setVisible && setVisible(true);
});
console.log('onRow', (e.target as HTMLElement), (e.target as HTMLElement).classList.contains('ant-table-cell'));
if (!(e.target as HTMLElement).classList.contains('ant-table-cell')) {
return;
}
if (!defaultAction) {
return;
}
// console.log('defaultAction');
field
.query(`.${schema.name}.${index}.${defaultAction.name}`)
.take((f) => {
const setVisible = f.componentProps.setVisible;
setVisible && setVisible(true);
});
},
};
}}
@ -337,28 +423,64 @@ Table.ActionBar = observer((props) => {
Table.Pagination = observer((props) => {
const { data, params, run } = useTableContext();
return data?.total > params?.pageSize && (
<Pagination
{...props}
defaultCurrent={1}
defaultPageSize={5}
current={params?.page}
pageSize={params?.pageSize}
total={data?.total}
onChange={(page, pageSize) => {
run({ page, pageSize });
}}
/>
return (
data?.total > params?.pageSize && (
<Pagination
{...props}
defaultCurrent={1}
defaultPageSize={5}
current={params?.page}
pageSize={params?.pageSize}
total={data?.total}
onChange={(page, pageSize) => {
run({ page, pageSize });
}}
/>
)
);
});
const SortHandle = SortableHandle((props: any) => {
return (
<MenuOutlined
{...props}
className={cls(`nb-table-sort-handle`, props.className)}
style={{ ...props.style }}
/>
)
}) as any
Table.SortHandle = observer((props) => {
const field = useField<Formily.Core.Models.Field>();
console.log('SortHandle', field.value);
return <MenuOutlined />;
return <SortHandle {...props}/>;
});
Table.Index = observer((props) => {
const index = useTableIndex();
return <div>#{index + 1}</div>;
});
Table.Addition = observer((props: any) => {
const { field, refresh } = useTableContext();
const current = useField();
return (
<Button
block
onClick={() => {
if (props.method === 'unshift') {
field.unshift({
key: uid(),
});
} else {
field.push({
key: uid(),
});
}
refresh();
}}
>
{current.title}
</Button>
);
});

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# Tabs - 标签页

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# TimePicker - 时间选择器

View File

@ -5,8 +5,8 @@ nav:
path: /client
group:
order: 1
title: Components - 组件
path: /client/components
title: Schemas
path: /client/schemas
---
# Upload - 上传

View File

@ -22,7 +22,7 @@ import {
refreshGlobalAction,
RouteComponentContext,
} from '../../';
import { SchemaRenderer } from '../../blocks';
import { SchemaRenderer } from '../../schemas';
import { useRequest } from 'ahooks';
import {
DatabaseOutlined,
@ -32,7 +32,7 @@ import {
import { Tabs } from 'antd';
import '@formily/antd/esm/array-collapse/style';
import './style.less';
import { MenuContainerContext } from '../../blocks/menu';
import { MenuContainerContext } from '../../schemas/menu';
function LogoutButton() {
const history = useHistory();

View File

@ -2,7 +2,7 @@ import React, { useContext, useEffect, useState } from 'react';
import { Spin } from 'antd';
import { Helmet } from 'react-helmet';
import { useRequest } from 'ahooks';
import { SchemaRenderer } from '../../blocks';
import { SchemaRenderer } from '../../schemas';
export function DefaultPage({ route }) {
const { data = {}, loading } = useRequest(