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 { useField, useFieldSchema } from '@formily/react';
import { useMount } from 'ahooks'; import { useMount } from 'ahooks';
import constate from 'constate'; import constate from 'constate';
import { useSchemaPath } from '../../blocks'; import { useSchemaPath } from '../../schemas';
export const DraggableBlockContext = createContext({ export const DraggableBlockContext = createContext({
dragRef: null, dragRef: null,

View File

@ -5,8 +5,8 @@ nav:
path: /client path: /client
group: group:
order: 1 order: 1
title: Components - 组件 title: Schemas
path: /client/components path: /client/schemas
--- ---
# Action - 操作 # 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 - 打开气泡 ## Action.Popover - 打开气泡
```tsx ```tsx

View File

@ -12,10 +12,10 @@ import {
} from '@formily/react'; } from '@formily/react';
import { Button, Dropdown, Menu, Popover, Space, Drawer, Modal } from 'antd'; import { Button, Dropdown, Menu, Popover, Space, Drawer, Modal } from 'antd';
import { Link, useHistory, LinkProps } from 'react-router-dom'; import { Link, useHistory, LinkProps } from 'react-router-dom';
import { SchemaRenderer, useDesignable } from '../'; import { SchemaRenderer, useDesignable } from '..';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import get from 'lodash/get'; import get from 'lodash/get';
import { MenuOutlined } from '@ant-design/icons'; import { MenuOutlined, DownOutlined } from '@ant-design/icons';
import classNames from 'classnames'; import classNames from 'classnames';
import { useMount } from 'ahooks'; import { useMount } from 'ahooks';
import { uid } from '@formily/shared'; import { uid } from '@formily/shared';
@ -78,6 +78,7 @@ export type ActionType = React.FC<ActionProps> & {
Drawer?: React.FC<any>; Drawer?: React.FC<any>;
Modal?: React.FC<any>; Modal?: React.FC<any>;
Dropdown?: React.FC<any>; Dropdown?: React.FC<any>;
Group?: React.FC<any>;
DesignableBar?: React.FC<any>; DesignableBar?: React.FC<any>;
}; };
@ -115,7 +116,7 @@ const BaseAction = observer((props: any) => {
field.componentProps.setVisible = setVisible; field.componentProps.setVisible = setVisible;
}, []); }, []);
console.log('BaseAction', { field, schema }, field.title) console.log('BaseAction', { field, schema }, field.title);
const renderButton = () => ( const renderButton = () => (
<Button <Button
@ -168,7 +169,10 @@ export const Action: ActionType = observer((props: any) => {
const schema = useFieldSchema(); const schema = useFieldSchema();
if (schema.properties) { if (schema.properties) {
return ( return (
<VisibleProvider initialVisible={props.initialVisible} containerRef={props.containerRef}> <VisibleProvider
initialVisible={props.initialVisible}
containerRef={props.containerRef}
>
<BaseAction {...props} /> <BaseAction {...props} />
</VisibleProvider> </VisibleProvider>
); );
@ -218,7 +222,7 @@ Action.Drawer = observer((props) => {
const { visible, setVisible } = useVisibleContext(); const { visible, setVisible } = useVisibleContext();
return ( return (
<Drawer <Drawer
onClick={e => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
}} }}
title={field.title} title={field.title}
@ -227,7 +231,7 @@ Action.Drawer = observer((props) => {
visible={visible} visible={visible}
onClose={(e) => { onClose={(e) => {
e.stopPropagation(); e.stopPropagation();
setVisible(false) setVisible(false);
}} }}
> >
{props.children} {props.children}
@ -240,17 +244,46 @@ Action.Modal = observer((props) => {
const { visible, setVisible } = useVisibleContext(); const { visible, setVisible } = useVisibleContext();
return ( return (
<Modal <Modal
// onClick={e => {
// e.stopPropagation();
// }}
title={field.title} title={field.title}
width={'50%'} width={'50%'}
{...props} {...props}
visible={visible} 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> </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 = () => { Action.DesignableBar = () => {
const field = useField(); const field = useField();
const schema = useFieldSchema(); 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 path: /client
group: group:
order: 1 order: 1
title: Components - 组件 title: Schemas
path: /client/components path: /client/schemas
--- ---
# AddNew - 新增 # AddNew - 新增

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,8 +5,8 @@ nav:
path: /client path: /client
group: group:
order: 1 order: 1
title: Components - 组件 title: Schemas
path: /client/components path: /client/schemas
--- ---
# Select - 选择器 # 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 path: /client
group: group:
order: 1 order: 1
title: Components - 组件 title: Schemas
path: /client/components path: /client/schemas
--- ---
# Table - 表格 # Table - 表格

View File

@ -1,4 +1,4 @@
import React, { createContext, useContext, useState } from 'react'; import React, { createContext, useContext, useRef, useState } from 'react';
import { import {
useFieldSchema, useFieldSchema,
Schema, Schema,
@ -24,12 +24,17 @@ import { BaseResult } from '@ahooksjs/use-request/lib/types';
import { uid, clone } from '@formily/shared'; import { uid, clone } from '@formily/shared';
import { MenuOutlined } from '@ant-design/icons'; import { MenuOutlined } from '@ant-design/icons';
import { useVisibleContext } from '../action'; import { useVisibleContext } from '../action';
import { SortableHandle, SortableContainer, SortableElement } from 'react-sortable-hoc'
import cls from 'classnames';
interface TableRowProps { interface TableRowProps {
index: number; index: number;
data: any; data: any;
} }
const SortableRow = SortableElement((props: any) => <tr {...props} />)
const SortableBody = SortableContainer((props: any) => <tbody {...props} />)
const TableRowContext = createContext<TableRowProps>(null); const TableRowContext = createContext<TableRowProps>(null);
function usePaginationProps() { function usePaginationProps() {
@ -55,6 +60,21 @@ function usePaginationProps() {
return false; 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() { function useTableActionBars() {
const schema = useFieldSchema(); const schema = useFieldSchema();
@ -127,7 +147,9 @@ export function useTableRow() {
} }
export function useTableIndex() { export function useTableIndex() {
const { params: { page, pageSize } } = useTableContext(); const {
params: { page, pageSize },
} = useTableContext();
const ctx = useContext(TableRowContext); const ctx = useContext(TableRowContext);
const field = useField(); const field = useField();
if (!field.componentProps.isRemoteDataSource) { if (!field.componentProps.isRemoteDataSource) {
@ -138,7 +160,8 @@ export function useTableIndex() {
export function useTableDestroyAction() { export function useTableDestroyAction() {
const ctx = useContext(TableRowContext); const ctx = useContext(TableRowContext);
const { field, selectedRowKeys, setSelectedRowKeys, refresh } = useTableContext(); const { field, selectedRowKeys, setSelectedRowKeys, refresh } =
useTableContext();
const rowKey = field.componentProps.rowKey || 'id'; const rowKey = field.componentProps.rowKey || 'id';
return { return {
@ -154,17 +177,13 @@ export function useTableDestroyAction() {
} }
while (selectedRowKeys.length) { while (selectedRowKeys.length) {
const key = selectedRowKeys.shift(); const key = selectedRowKeys.shift();
const index = findIndex( const index = findIndex(field.value, (item) => item[rowKey] === key);
field.value,
(item) => item[rowKey] === key,
);
field.remove(index); field.remove(index);
} }
refresh(); refresh();
setSelectedRowKeys([]); setSelectedRowKeys([]);
} },
} };
} }
export function useTableFilterAction() { export function useTableFilterAction() {
@ -175,8 +194,8 @@ export function useTableFilterAction() {
async run() { async run() {
setVisible && setVisible(false); setVisible && setVisible(false);
refresh(); refresh();
} },
} };
} }
export function useTableCreateAction() { export function useTableCreateAction() {
@ -186,11 +205,14 @@ export function useTableCreateAction() {
return { return {
async run() { async run() {
setVisible && setVisible(false); setVisible && setVisible(false);
field.unshift(clone(form.values)); field.push({
key: uid(),
...clone(form.values),
});
form.reset(); form.reset();
exec({...params, page: 1}); exec({ ...params, page: 1 });
} },
} };
} }
export function useTableUpdateAction() { export function useTableUpdateAction() {
@ -201,12 +223,13 @@ export function useTableUpdateAction() {
async run() { async run() {
setVisible && setVisible(false); setVisible && setVisible(false);
refresh(); refresh();
} },
} };
} }
const [TableContextProvider, useTableContext] = constate(() => { const [TableContextProvider, useTableContext] = constate(() => {
const field = useField<Formily.Core.Models.ArrayField>(); const field = useField<Formily.Core.Models.ArrayField>();
const schema = useFieldSchema();
const [selectedRowKeys, setSelectedRowKeys] = useState([]); const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const pagination = usePaginationProps(); const pagination = usePaginationProps();
const response = useRequest<{ const response = useRequest<{
@ -239,7 +262,14 @@ const [TableContextProvider, useTableContext] = constate(() => {
pageSize: pagination.defaultPageSize, pageSize: pagination.defaultPageSize,
...(response.params[0] || {}), ...(response.params[0] || {}),
}; };
return { ...response, params, field, selectedRowKeys, setSelectedRowKeys }; return {
...response,
params,
field,
schema,
selectedRowKeys,
setSelectedRowKeys,
};
}); });
export { TableContextProvider, useTableContext }; export { TableContextProvider, useTableContext };
@ -251,12 +281,26 @@ const TableContainer = observer((props) => {
const { loading, data, refresh, selectedRowKeys, setSelectedRowKeys } = const { loading, data, refresh, selectedRowKeys, setSelectedRowKeys } =
useTableContext(); useTableContext();
const rowKey = field.componentProps.rowKey || 'id'; const rowKey = field.componentProps.rowKey || 'id';
console.log({ actionBars }); const defaultAction = useDefaultAction();
const dataSource = Array.isArray(field.value) ? field.value.slice() : [] console.log({ defaultAction });
const dataSource = Array.isArray(field.value) ? field.value.slice() : [];
const columns = useTableColumns({ dataSource }); 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 ( return (
<div> <div ref={ref} className={'nb-table'}>
{actionBars.top.map((actionBarSchema) => { {actionBars.top.map((actionBarSchema) => {
return ( return (
<RecursionField <RecursionField
@ -286,14 +330,56 @@ const TableContainer = observer((props) => {
}} }}
dataSource={data?.list} dataSource={data?.list}
columns={columns} 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) => { onRow={(data) => {
const index = dataSource.indexOf(data); const index = dataSource.indexOf(data);
return { return {
onClick(e) { onClick(e) {
field.query(`.${schema.name}.${index}.action2`).take((f) => { console.log('onRow', (e.target as HTMLElement), (e.target as HTMLElement).classList.contains('ant-table-cell'));
const setVisible = f.componentProps.setVisible; if (!(e.target as HTMLElement).classList.contains('ant-table-cell')) {
setVisible && setVisible(true); 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) => { Table.Pagination = observer((props) => {
const { data, params, run } = useTableContext(); const { data, params, run } = useTableContext();
return data?.total > params?.pageSize && ( return (
<Pagination data?.total > params?.pageSize && (
{...props} <Pagination
defaultCurrent={1} {...props}
defaultPageSize={5} defaultCurrent={1}
current={params?.page} defaultPageSize={5}
pageSize={params?.pageSize} current={params?.page}
total={data?.total} pageSize={params?.pageSize}
onChange={(page, pageSize) => { total={data?.total}
run({ page, pageSize }); 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) => { Table.SortHandle = observer((props) => {
const field = useField<Formily.Core.Models.Field>(); const field = useField<Formily.Core.Models.Field>();
console.log('SortHandle', field.value); console.log('SortHandle', field.value);
return <MenuOutlined />; return <SortHandle {...props}/>;
}); });
Table.Index = observer((props) => { Table.Index = observer((props) => {
const index = useTableIndex(); const index = useTableIndex();
return <div>#{index + 1}</div>; 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 path: /client
group: group:
order: 1 order: 1
title: Components - 组件 title: Schemas
path: /client/components path: /client/schemas
--- ---
# Tabs - 标签页 # Tabs - 标签页

View File

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

View File

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

View File

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

View File

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