refactor code

This commit is contained in:
chenos 2021-07-30 00:03:10 +08:00
parent 1007324b09
commit 27b61bd571
30 changed files with 1180 additions and 1672 deletions

View File

@ -19,6 +19,8 @@
"gitHead": "f0b335ac30f29f25c95d7d137655fa64d8d67f1e", "gitHead": "f0b335ac30f29f25c95d7d137655fa64d8d67f1e",
"dependencies": { "dependencies": {
"@antv/g2plot": "^2.3.27", "@antv/g2plot": "^2.3.27",
"@dnd-kit/core": "^3.1.1",
"@dnd-kit/sortable": "^4.0.0",
"@formily/antd": "^2.0.0-beta.72", "@formily/antd": "^2.0.0-beta.72",
"@formily/core": "^2.0.0-beta.72", "@formily/core": "^2.0.0-beta.72",
"@formily/react": "^2.0.0-beta.72", "@formily/react": "^2.0.0-beta.72",

View File

@ -1,8 +1,8 @@
import { SchemaRenderer } from '../../'; import { SchemaRenderer } from '../../';
import React from 'react'; import React from 'react';
import { FormItem } from '@formily/antd'; import { FormItem } from '@formily/antd';
import { useCollectionContext } from '../../schemas';
import { action } from '@formily/reactive'; import { action } from '@formily/reactive';
import { useCollectionsContext } from '../../constate/Collections';
const useAsyncDataSource = (service: any) => (field: any) => { const useAsyncDataSource = (service: any) => (field: any) => {
field.loading = true; field.loading = true;
@ -15,10 +15,10 @@ const useAsyncDataSource = (service: any) => (field: any) => {
}; };
export default () => { export default () => {
const { data, loading } = useCollectionContext(); const { collections = [], loading } = useCollectionsContext();
const loadCollections = async (field: any) => { const loadCollections = async (field: any) => {
return data.map((item: any) => ({ return collections.map((item: any) => ({
label: item.title, label: item.title,
value: item.name, value: item.name,
})); }));

View File

@ -17,26 +17,24 @@ import {
useHistory, useHistory,
Redirect, Redirect,
} from 'react-router-dom'; } from 'react-router-dom';
import { import { SchemaRenderer } from '../../schemas';
CollectionContextProvider,
PageTitleContextProvider,
SchemaRenderer,
SwithDesignableContextProvider,
usePageTitleContext,
useSwithDesignableContext,
} from '../../schemas';
import { useRequest } from 'ahooks'; import { useRequest } from 'ahooks';
import './style.less'; import './style.less';
import { uid } from '@formily/shared';
import { ISchema, Schema } from '@formily/react'; import { ISchema, Schema } from '@formily/react';
import Database from './datatable'; import Database from './datatable';
import { HighlightOutlined } from '@ant-design/icons'; import { HighlightOutlined } from '@ant-design/icons';
import { useCookieState } from 'ahooks';
import cls from 'classnames'; import cls from 'classnames';
import {
DesignableSwitchProvider,
useDesignableSwitchContext,
PageTitleProvider,
usePageTitleContext,
CollectionsProvider,
} from '../../constate';
function DesignableToggle() { function DesignableToggle() {
const { designable, setDesignable } = useSwithDesignableContext(); const { designable, setDesignable } = useDesignableSwitchContext();
return ( return (
<Button <Button
className={cls('nb-designable-toggle', { active: designable })} className={cls('nb-designable-toggle', { active: designable })}
@ -147,7 +145,7 @@ export function AdminLayout({ route }: any) {
} }
return [...items, ...findProperties(current)]; return [...items, ...findProperties(current)];
}, []); }, []);
} };
const current = findProperties(new Schema(data)).shift(); const current = findProperties(new Schema(data)).shift();
const defaultSelectedKeys = [current?.name]; const defaultSelectedKeys = [current?.name];
let parent = current?.parent; let parent = current?.parent;
@ -162,14 +160,17 @@ export function AdminLayout({ route }: any) {
console.log('current?.title', current, current?.title, defaultSelectedKeys); console.log('current?.title', current, current?.title, defaultSelectedKeys);
return ( return (
<SwithDesignableContextProvider> <DesignableSwitchProvider>
<CollectionContextProvider> <CollectionsProvider>
{/* @ts-ignore */} <PageTitleProvider defaultPageTitle={current?.title}>
<PageTitleContextProvider defaultPageTitle={current?.title}> <LayoutWithMenu
<LayoutWithMenu defaultSelectedKeys={defaultSelectedKeys} current={current} schema={data} /> defaultSelectedKeys={defaultSelectedKeys}
</PageTitleContextProvider> current={current}
</CollectionContextProvider> schema={data}
</SwithDesignableContextProvider> />
</PageTitleProvider>
</CollectionsProvider>
</DesignableSwitchProvider>
); );
} }

View File

@ -52,7 +52,7 @@ import { DragAndDrop } from '../../schemas/drag-and-drop';
import { TreeSelect } from '../../schemas/tree-select'; import { TreeSelect } from '../../schemas/tree-select';
import { Page } from '../../schemas/page'; import { Page } from '../../schemas/page';
import { Chart } from '../../schemas/chart'; import { Chart } from '../../schemas/chart';
import { useCollectionContext, useSwithDesignableContext } from '../../schemas'; import { useDesignableSwitchContext } from '../../constate/DesignableSwitch';
export const BlockContext = createContext({ dragRef: null }); export const BlockContext = createContext({ dragRef: null });
@ -193,7 +193,7 @@ export function useSchemaComponent(component: string) {
} }
export function useDesignable(path?: any) { export function useDesignable(path?: any) {
const { designable } = useSwithDesignableContext(); let { designable = true } = useDesignableSwitchContext();
const { schema = new Schema({}), refresh } = useContext(DesignableContext); const { schema = new Schema({}), refresh } = useContext(DesignableContext);
const schemaPath = path || useSchemaPath(); const schemaPath = path || useSchemaPath();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
@ -453,7 +453,7 @@ export interface SchemaRendererProps {
} }
export const SchemaRenderer = (props: SchemaRendererProps) => { export const SchemaRenderer = (props: SchemaRendererProps) => {
const { designable } = useSwithDesignableContext(); const { designable = true } = useDesignableSwitchContext();
const [, refresh] = useState(uid()); const [, refresh] = useState(uid());
const form = useMemo(() => props.form || createForm(), []); const form = useMemo(() => props.form || createForm(), []);
const schema = useMemo(() => { const schema = useMemo(() => {

View File

@ -0,0 +1,44 @@
import { useRequest } from 'ahooks';
import constate from 'constate';
const [CollectionsProvider, useCollectionsContext] = constate(() => {
const result = useRequest('collections:findAll', {
formatResult: (result) => result?.data,
});
return { ...result, collections: result.data || [] };
});
export { CollectionsProvider, useCollectionsContext };
export interface CollectionProviderProps {
collectionName?: string;
}
const [CollectionProvider, useCollectionContext] = constate(
(props: CollectionProviderProps) => {
const { collectionName } = props;
const { data = [], loading, refresh } = useCollectionsContext();
let collection: any;
let fields = [];
if (collectionName) {
collection = data.find((item) => item.name === collectionName);
}
if (collection) {
fields = collection?.fields || [];
}
return {
collection,
fields,
loading,
refresh,
getField(name: string) {
if (!name) {
return null;
}
return fields.find((item) => item.name === name);
},
};
},
);
export { CollectionProvider, useCollectionContext };

View File

@ -0,0 +1,15 @@
import { useCookieState } from "ahooks";
import constate from 'constate';
const [DesignableSwitchProvider, useDesignableSwitchContext] = constate(() => {
const [active, setActive] = useCookieState('useCookieDesignable');
return {
designable: active === 'true',
setDesignable(value) {
setActive(value ? 'true' : 'false');
}
}
});
export { DesignableSwitchProvider, useDesignableSwitchContext };

View File

@ -0,0 +1,9 @@
import { useMap } from 'ahooks';
import constate from 'constate';
const [DisplayedMapProvider, useDisplayedMapContext] = constate(() => {
const [map, { set, remove, get }] = useMap([]);
return { map, set, remove, get, has: (key: any) => !!get(key) };
});
export { DisplayedMapProvider, useDisplayedMapContext };

View File

@ -0,0 +1,12 @@
import constate from 'constate';
import { useState } from 'react';
export interface PageTitleProviderProps {
defaultPageTitle?: string;
}
const [PageTitleProvider, usePageTitleContext] = constate(({ defaultPageTitle }: PageTitleProviderProps) => {
return useState(defaultPageTitle);
});
export { PageTitleProvider, usePageTitleContext };

View File

@ -0,0 +1,4 @@
export * from './Collections';
export * from './DesignableSwitch';
export * from './DisplayedMap';
export * from './PageTitle';

View File

@ -0,0 +1,3 @@
import { createContext } from "react";
export const VisibleContext = createContext<any>(null);

View File

@ -21,7 +21,7 @@ export interface ListOptions {
export class Resource { export class Resource {
protected options: ResourceOptions; public options: ResourceOptions;
constructor(options: string | ResourceOptions) { constructor(options: string | ResourceOptions) {
if (typeof options === 'string') { if (typeof options === 'string') {
@ -45,6 +45,15 @@ export class Resource {
return request(`${resourceName}:get/${resourceKey}`); return request(`${resourceName}:get/${resourceKey}`);
} }
create(values: any) {
const { resourceName } = this.options;
const url = `${resourceName}:create`;
return request(url, {
method: 'post',
data: values,
});
}
save(values: any, options: SaveOptions = {}) { save(values: any, options: SaveOptions = {}) {
const resourceKey = options.resourceKey || this.options.resourceKey; const resourceKey = options.resourceKey || this.options.resourceKey;
const { resourceName } = this.options; const { resourceName } = this.options;
@ -66,7 +75,7 @@ export class Resource {
}); });
} }
static make(options: string | Resource | ResourceOptions) { static make(options: null | string | Resource | ResourceOptions): Resource | null {
if (typeof options === 'string') { if (typeof options === 'string') {
return new Resource({ resourceName: options }); return new Resource({ resourceName: options });
} }
@ -76,6 +85,7 @@ export class Resource {
if (typeof options === 'object' && options.resourceName) { if (typeof options === 'object' && options.resourceName) {
return new Resource(options); return new Resource(options);
} }
throw 'resource 参数错误'; console.warn('resource 初始化参数错误');
return null;
} }
} }

View File

@ -281,7 +281,7 @@ const schema = {
item1: { item1: {
type: 'void', type: 'void',
title: `操作1`, title: `操作1`,
'x-designable-bar': 'Menu.DesignableBar', 'x-designable-bar': 'Action.DesignableBar',
'x-component': 'Menu.Action', 'x-component': 'Menu.Action',
properties: { properties: {
modal1: { modal1: {
@ -291,7 +291,17 @@ const schema = {
properties: { properties: {
input: { input: {
type: 'string', type: 'string',
title: '输入框',
'x-decorator': 'FormItem',
'x-component': 'Input', 'x-component': 'Input',
'x-designable-bar': 'Input.DesignableBar',
},
grid: {
type: 'void',
'x-component': 'Grid',
'x-component-props': {
addNewComponent: 'AddNew.FormItem',
},
}, },
}, },
}, },

View File

@ -13,7 +13,7 @@ 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 { useDesignable, VisibleContext, useDefaultAction, useVisible } from '..'; import { useDesignable, useDefaultAction } from '..';
import './style.less'; import './style.less';
import { uid } from '@formily/shared'; import { uid } from '@formily/shared';
import cls from 'classnames'; import cls from 'classnames';
@ -21,18 +21,18 @@ import { MenuOutlined } from '@ant-design/icons';
import { FormLayout } from '@formily/antd'; import { FormLayout } from '@formily/antd';
import IconPicker from '../../components/icon-picker'; import IconPicker from '../../components/icon-picker';
import { useSchemaComponent } from '../../components/schema-renderer'; import { useSchemaComponent } from '../../components/schema-renderer';
import { VisibleContext } from '../../context';
export const Action: any = observer((props: any) => { export const Action: any = observer((props: any) => {
const { useAction = useDefaultAction, icon, ...others } = props; const { useAction = useDefaultAction, icon, ...others } = props;
const { run } = useAction(); const { run } = useAction();
const field = useField(); const field = useField();
const { schema, DesignableBar } = useDesignable(); const { schema, DesignableBar } = useDesignable();
const { visible, setVisible } = useVisible(field.address.entire); const [visible, setVisible] = useState(false);
const child = Object.values(schema.properties || {}).shift(); const child = Object.values(schema.properties || {}).shift();
const isDropdownOrPopover = const isDropdownOrPopover =
child && child &&
['Action.Dropdown', 'Action.Popover'].includes(child['x-component']); ['Action.Dropdown', 'Action.Popover'].includes(child['x-component']);
// console.log({ DesignableBar, schema });
return ( return (
<VisibleContext.Provider value={[visible, setVisible]}> <VisibleContext.Provider value={[visible, setVisible]}>
{!isDropdownOrPopover && ( {!isDropdownOrPopover && (
@ -75,7 +75,6 @@ Action.Modal = observer((props: any) => {
} = props; } = props;
const { schema } = useDesignable(); const { schema } = useDesignable();
const [visible, setVisible] = useContext(VisibleContext); const [visible, setVisible] = useContext(VisibleContext);
console.log('VisibleContext', visible);
const form = useForm(); const form = useForm();
const { run: runOk } = useOkAction(); const { run: runOk } = useOkAction();
const { run: runCancel } = useCancelAction(); const { run: runCancel } = useCancelAction();
@ -190,8 +189,13 @@ Action.Drawer = observer((props: any) => {
Action.Dropdown = observer((props: any) => { Action.Dropdown = observer((props: any) => {
const { buttonProps = {}, ...others } = props; const { buttonProps = {}, ...others } = props;
const { schema } = useDesignable(); const { schema } = useDesignable();
const componentProps = schema.parent['x-component-props'] || {}; let componentProps = {...buttonProps};
const icon = buttonProps.icon || componentProps['icon']; let title = schema.title;
if (schema.parent['x-component'] === 'Action') {
title = schema.parent.title;
componentProps = {...buttonProps, ...(schema.parent['x-component-props'] || {})}
}
const icon = componentProps['icon'];
return ( return (
<Dropdown <Dropdown
trigger={['click']} trigger={['click']}
@ -207,7 +211,7 @@ Action.Dropdown = observer((props: any) => {
{...componentProps} {...componentProps}
icon={<IconPicker type={icon} />} icon={<IconPicker type={icon} />}
> >
{schema.title || schema.parent.title} {title}
</Button> </Button>
</Dropdown> </Dropdown>
); );

View File

@ -52,23 +52,23 @@ import {
createOrUpdateCollection, createOrUpdateCollection,
createSchema, createSchema,
removeSchema, removeSchema,
request,
useCollectionContext,
useDesignable, useDesignable,
useSchemaPath, useSchemaPath,
} from '../'; } from '../';
import { uid } from '@formily/shared'; import { uid } from '@formily/shared';
import { useRequest } from 'ahooks';
import { getSchemaPath, SchemaField } from '../../components/schema-renderer'; import { getSchemaPath, SchemaField } from '../../components/schema-renderer';
import { useResourceContext } from '../';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
import { options } from '../database-field/interfaces'; import { options } from '../database-field/interfaces';
import { useDisplayFieldsContext } from '../form';
import './style.less'; import './style.less';
import IconPicker from '../../components/icon-picker'; import IconPicker from '../../components/icon-picker';
import { barChartConfig, columnChartConfig } from './chart'; import { barChartConfig, columnChartConfig } from './chart';
import { isGridRowOrCol } from '../grid'; import { isGridRowOrCol } from '../grid';
import {
useCollectionContext,
useCollectionsContext,
useDisplayedMapContext,
} from '../../constate';
const generateGridBlock = (schema: ISchema) => { const generateGridBlock = (schema: ISchema) => {
const name = schema.name || uid(); const name = schema.name || uid();
@ -136,6 +136,10 @@ function generateCardItemSchema(component) {
rowKey: 'id', rowKey: 'id',
dragSort: true, dragSort: true,
showIndex: true, showIndex: true,
refreshRequestOnChange: true,
pagination: {
pageSize: 10,
},
}, },
properties: { properties: {
[uid()]: { [uid()]: {
@ -192,20 +196,7 @@ function generateCardItemSchema(component) {
}, },
[uid()]: { [uid()]: {
type: 'void', type: 'void',
'x-component': 'Table.ActionBar', title: '操作',
'x-component-props': {
align: 'bottom',
},
properties: {
[uid()]: {
type: 'void',
'x-component': 'Table.Pagination',
'x-component-props': {},
},
},
},
[uid()]: {
type: 'void',
'x-component': 'Table.Column', 'x-component': 'Table.Column',
'x-component-props': { 'x-component-props': {
className: 'nb-table-operation', className: 'nb-table-operation',
@ -286,7 +277,7 @@ function generateCardItemSchema(component) {
title: '编辑数据', title: '编辑数据',
'x-decorator': 'Form', 'x-decorator': 'Form',
'x-decorator-props': { 'x-decorator-props': {
useValues: '{{ Table.useTableRow }}', useValues: '{{ Table.useTableRowRecord }}',
}, },
'x-component': 'Action.Modal', 'x-component': 'Action.Modal',
'x-component-props': { 'x-component-props': {
@ -406,15 +397,7 @@ AddNew.CardItem = observer((props: any) => {
const { ghost, defaultAction } = props; const { ghost, defaultAction } = props;
const { schema, insertBefore, insertAfter, appendChild } = useDesignable(); const { schema, insertBefore, insertAfter, appendChild } = useDesignable();
const path = useSchemaPath(); const path = useSchemaPath();
// const { const { collections = [], loading, refresh } = useCollectionsContext();
// data: collections = [],
// loading,
// run,
// } = useRequest('collections:findAll', {
// formatResult: (result) => result?.data,
// });
const { data: collections = [], loading, refresh } = useCollectionContext();
console.log({ collections });
return ( return (
<Dropdown <Dropdown
trigger={['click']} trigger={['click']}
@ -424,12 +407,11 @@ AddNew.CardItem = observer((props: any) => {
onVisibleChange={(visible) => { onVisibleChange={(visible) => {
console.log('onVisibleChange', visible); console.log('onVisibleChange', visible);
}} }}
placement={'bottomCenter'}
overlay={ overlay={
<Menu <Menu
onClick={async (info) => { onClick={async (info) => {
let data: ISchema; let data: ISchema;
let resourceName = null; let collectionName = null;
if (['addNewTable', 'addNewForm'].includes(info.key)) { if (['addNewTable', 'addNewForm'].includes(info.key)) {
const values = await FormDialog(`新建数据表`, () => { const values = await FormDialog(`新建数据表`, () => {
return ( return (
@ -448,12 +430,12 @@ AddNew.CardItem = observer((props: any) => {
data = generateCardItemSchema( data = generateCardItemSchema(
info.key === 'addNewTable' ? 'Table' : 'Form', info.key === 'addNewTable' ? 'Table' : 'Form',
); );
resourceName = values.name; collectionName = values.name;
} else if (info.key.startsWith('collection.')) { } else if (info.key.startsWith('collection.')) {
const keys = info.key.split('.'); const keys = info.key.split('.');
const component = keys.pop(); const component = keys.pop();
const tableName = keys.pop(); const tableName = keys.pop();
resourceName = tableName; collectionName = tableName;
data = generateCardItemSchema(component); data = generateCardItemSchema(component);
console.log('info.keyPath', component, tableName); console.log('info.keyPath', component, tableName);
} else { } else {
@ -463,9 +445,10 @@ AddNew.CardItem = observer((props: any) => {
if (schema['key']) { if (schema['key']) {
data['key'] = uid(); data['key'] = uid();
} }
if (resourceName) { if (collectionName) {
data['x-component-props'] = data['x-component-props'] || {}; data['x-component-props'] = data['x-component-props'] || {};
data['x-component-props']['resourceName'] = resourceName; data['x-component-props']['resource'] = collectionName;
data['x-component-props']['collectionName'] = collectionName;
} }
if (isGridBlock(schema)) { if (isGridBlock(schema)) {
path.pop(); path.pop();
@ -511,7 +494,7 @@ AddNew.CardItem = observer((props: any) => {
title={view.title} title={view.title}
> >
<Menu.ItemGroup key={`${view.key}-select`} title={'所属数据表'}> <Menu.ItemGroup key={`${view.key}-select`} title={'所属数据表'}>
{collections.map((item) => ( {collections?.map((item) => (
<Menu.Item <Menu.Item
style={{ minWidth: 150 }} style={{ minWidth: 150 }}
key={`collection.${item.name}.${view.key}`} key={`collection.${item.name}.${view.key}`}
@ -586,76 +569,9 @@ AddNew.CardItem = observer((props: any) => {
{ghost ? ( {ghost ? (
<PlusOutlined /> <PlusOutlined />
) : ( ) : (
<Button block type={'dashed'} icon={<PlusOutlined />}></Button> <Button type={'dashed'} icon={<PlusOutlined />}>
)}
</Dropdown> </Button>
);
});
AddNew.BlockItem = observer((props: any) => {
const { ghost, defaultAction } = props;
const { schema, insertBefore, insertAfter } = useDesignable();
const path = useSchemaPath();
return (
<Dropdown
overlayStyle={{
minWidth: 200,
}}
placement={'bottomCenter'}
overlay={
<Menu>
<Menu.SubMenu title={'新建表格'}>
<Menu.ItemGroup title={'选择数据表'}>
<Menu.Item>1</Menu.Item>
</Menu.ItemGroup>
<Menu.Divider></Menu.Divider>
<Menu.Item></Menu.Item>
</Menu.SubMenu>
<Menu.SubMenu title={'新建表单'}>
<Menu.ItemGroup title={'选择数据表'}>
<Menu.Item>1</Menu.Item>
</Menu.ItemGroup>
<Menu.Divider></Menu.Divider>
<Menu.Item></Menu.Item>
</Menu.SubMenu>
<Menu.Item
onClick={async () => {
let data: ISchema = {
type: 'void',
title: uid(),
'x-designable-bar': 'BlockItem.DesignableBar',
'x-decorator': 'CardItem',
'x-component': 'Markdown.Void',
};
if (schema['key']) {
data['key'] = uid();
}
console.log('isGridBlock(schema)', isGridBlock(schema));
if (isGridBlock(schema)) {
path.pop();
path.pop();
data = generateGridBlock(data);
}
if (data) {
let s;
if (defaultAction === 'insertAfter') {
s = insertAfter(data, [...path]);
} else {
s = insertBefore(data, [...path]);
}
await createSchema(s);
}
}}
>
Markdown
</Menu.Item>
</Menu>
}
>
{ghost ? (
<PlusOutlined />
) : (
<Button block type={'dashed'} icon={<PlusOutlined />}></Button>
)} )}
</Dropdown> </Dropdown>
); );
@ -692,15 +608,9 @@ AddNew.FormItem = observer((props: any) => {
const { schema, insertBefore, insertAfter, appendChild, deepRemove } = const { schema, insertBefore, insertAfter, appendChild, deepRemove } =
useDesignable(); useDesignable();
const path = useSchemaPath(); const path = useSchemaPath();
const { resource = {}, refresh } = useResourceContext(); const { collection, fields, refresh } = useCollectionContext();
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const { const displayed = useDisplayedMapContext();
displayFields,
hasDisplayField,
getDisplayField,
removeDisplayField,
} = useDisplayFieldsContext();
console.log({ displayFields });
return ( return (
<Dropdown <Dropdown
trigger={['click']} trigger={['click']}
@ -709,18 +619,17 @@ AddNew.FormItem = observer((props: any) => {
overlayStyle={{ overlayStyle={{
minWidth: 200, minWidth: 200,
}} }}
placement={'bottomCenter'}
overlay={ overlay={
<Menu> <Menu>
<Menu.ItemGroup className={'display-fields'} title={`要展示的字段`}> <Menu.ItemGroup className={'display-fields'} title={`要展示的字段`}>
{resource?.fields?.map((field) => ( {fields?.map((field) => (
<Menu.Item key={field.key}> <Menu.Item key={field.key}>
<SwitchField <SwitchField
field={field} field={field}
checked={hasDisplayField(field.name)} checked={displayed.has(field.name)}
onChange={async (checked) => { onChange={async (checked) => {
if (!checked) { if (!checked) {
const s: any = getDisplayField(field.name); const s: any = displayed.get(field.name);
const p = getSchemaPath(s); const p = getSchemaPath(s);
const removed = deepRemove(p); const removed = deepRemove(p);
if (!removed) { if (!removed) {
@ -728,7 +637,7 @@ AddNew.FormItem = observer((props: any) => {
return; return;
} }
const last = removed.pop(); const last = removed.pop();
removeDisplayField(field.name); displayed.remove(field.name);
if (isGridRowOrCol(last)) { if (isGridRowOrCol(last)) {
await removeSchema(last); await removeSchema(last);
} }
@ -797,7 +706,7 @@ AddNew.FormItem = observer((props: any) => {
name: `f_${uid()}`, name: `f_${uid()}`,
}, },
}); });
await createCollectionField(resource.name, values); await createCollectionField(collection?.name, values);
await refresh(); await refresh();
let data: ISchema = cloneDeep(values.uiSchema); let data: ISchema = cloneDeep(values.uiSchema);
data['name'] = values.name; data['name'] = values.name;
@ -834,7 +743,6 @@ AddNew.FormItem = observer((props: any) => {
<Menu.Item <Menu.Item
onClick={async () => { onClick={async () => {
let data: ISchema = { let data: ISchema = {
key: uid(),
type: 'void', type: 'void',
default: '这是一段演示文字,**支持使用 Markdown 语法**', default: '这是一段演示文字,**支持使用 Markdown 语法**',
'x-designable-bar': 'Markdown.Void.DesignableBar', 'x-designable-bar': 'Markdown.Void.DesignableBar',
@ -842,6 +750,9 @@ AddNew.FormItem = observer((props: any) => {
'x-read-pretty': true, 'x-read-pretty': true,
'x-component': 'Markdown.Void', 'x-component': 'Markdown.Void',
}; };
if (schema['key']) {
data['key'] = uid();
}
if (isGridBlock(schema)) { if (isGridBlock(schema)) {
path.pop(); path.pop();
path.pop(); path.pop();
@ -858,6 +769,7 @@ AddNew.FormItem = observer((props: any) => {
} else { } else {
s = insertBefore(data, [...path]); s = insertBefore(data, [...path]);
} }
console.log('ISchema', schema, data, path);
await createSchema(s); await createSchema(s);
} }
setVisible(false); setVisible(false);
@ -871,7 +783,9 @@ AddNew.FormItem = observer((props: any) => {
{ghost ? ( {ghost ? (
<PlusOutlined /> <PlusOutlined />
) : ( ) : (
<Button block type={'dashed'} icon={<PlusOutlined />}></Button> <Button type={'dashed'} icon={<PlusOutlined />}>
</Button>
)} )}
</Dropdown> </Dropdown>
); );
@ -881,10 +795,8 @@ AddNew.PaneItem = observer((props: any) => {
const { ghost, defaultAction } = props; const { ghost, defaultAction } = props;
const { schema, insertBefore, insertAfter, appendChild } = useDesignable(); const { schema, insertBefore, insertAfter, appendChild } = useDesignable();
const path = useSchemaPath(); const path = useSchemaPath();
const { resource = {}, refresh } = useResourceContext();
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const { displayFields } = useDisplayFieldsContext();
console.log({ displayFields });
return ( return (
<Dropdown <Dropdown
trigger={['click']} trigger={['click']}
@ -893,7 +805,7 @@ AddNew.PaneItem = observer((props: any) => {
overlayStyle={{ overlayStyle={{
minWidth: 200, minWidth: 200,
}} }}
placement={'bottomCenter'} // placement={'bottomCenter'}
overlay={ overlay={
<Menu> <Menu>
<Menu.ItemGroup title={'数据区块'}> <Menu.ItemGroup title={'数据区块'}>
@ -907,7 +819,7 @@ AddNew.PaneItem = observer((props: any) => {
'x-component': 'Form', 'x-component': 'Form',
'x-read-pretty': true, 'x-read-pretty': true,
'x-component-props': { 'x-component-props': {
useValues: '{{ Table.useTableRow }}', useValues: '{{ Table.useTableRowRecord }}',
}, },
'x-designable-bar': 'Form.DesignableBar', 'x-designable-bar': 'Form.DesignableBar',
properties: { properties: {
@ -953,7 +865,7 @@ AddNew.PaneItem = observer((props: any) => {
'x-decorator': 'CardItem', 'x-decorator': 'CardItem',
'x-component': 'Form', 'x-component': 'Form',
'x-component-props': { 'x-component-props': {
useValues: '{{ Table.useTableRow }}', useValues: '{{ Table.useTableRowRecord }}',
showDefaultButtons: true, showDefaultButtons: true,
}, },
'x-designable-bar': 'Form.DesignableBar', 'x-designable-bar': 'Form.DesignableBar',
@ -1045,7 +957,9 @@ AddNew.PaneItem = observer((props: any) => {
{ghost ? ( {ghost ? (
<PlusOutlined /> <PlusOutlined />
) : ( ) : (
<Button block type={'dashed'} icon={<PlusOutlined />}></Button> <Button block type={'dashed'} icon={<PlusOutlined />}>
</Button>
)} )}
</Dropdown> </Dropdown>
); );

View File

@ -39,11 +39,8 @@ import Modal from 'antd/lib/modal/Modal';
import { clone, cloneDeep, get } from 'lodash'; import { clone, cloneDeep, get } from 'lodash';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useRequest } from 'ahooks'; import { useRequest } from 'ahooks';
import { import { createOrUpdateCollection, deleteCollection } from '..';
createOrUpdateCollection, import { useCollectionsContext } from '../../constate/Collections';
deleteCollection,
useCollectionContext,
} from '..';
export const DatabaseCollection = observer((props) => { export const DatabaseCollection = observer((props) => {
const field = useField<Formily.Core.Models.ArrayField>(); const field = useField<Formily.Core.Models.ArrayField>();
@ -53,21 +50,12 @@ export const DatabaseCollection = observer((props) => {
const [activeIndex, setActiveIndex] = useState(0); const [activeIndex, setActiveIndex] = useState(0);
const form = useForm(); const form = useForm();
const [newValue, setNewValue] = useState(''); const [newValue, setNewValue] = useState('');
const { loading, refresh, data } = useCollectionContext(); const { loading, refresh, collections = [] } = useCollectionsContext();
useEffect(() => { useEffect(() => {
field.setValue(data); field.setValue(collections);
console.log('onSuccess', data); console.log('onSuccess', collections);
}, [data]); }, [collections]);
// const { run, loading } = useRequest('collections:findAll', {
// formatResult: (result) => result?.data,
// onSuccess(data) {
// // field.setValue(data);
// // console.log('onSuccess', data);
// },
// manual: true,
// });
return ( return (
<div> <div>

View File

@ -17,7 +17,6 @@ import {
SchemaField, SchemaField,
useDesignable, useDesignable,
removeSchema, removeSchema,
useCollectionContext,
} from '../'; } from '../';
import get from 'lodash/get'; import get from 'lodash/get';
import { Button, Dropdown, Menu, Space } from 'antd'; import { Button, Dropdown, Menu, Space } from 'antd';

View File

@ -17,7 +17,6 @@ import {
SchemaField, SchemaField,
useDesignable, useDesignable,
removeSchema, removeSchema,
useCollectionContext,
updateSchema, updateSchema,
} from '../'; } from '../';
import get from 'lodash/get'; import get from 'lodash/get';
@ -33,7 +32,8 @@ import constate from 'constate';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { uid } from '@formily/shared'; import { uid } from '@formily/shared';
import { getSchemaPath } from '../../components/schema-renderer'; import { getSchemaPath } from '../../components/schema-renderer';
import { FormFieldUIDContext, useDisplayFieldsContext } from '.'; import { RandomNameContext } from '.';
import { useDisplayedMapContext } from '../../constate';
export const FieldDesignableBar = observer((props) => { export const FieldDesignableBar = observer((props) => {
const field = useField(); const field = useField();
@ -41,8 +41,8 @@ export const FieldDesignableBar = observer((props) => {
const { designable, schema, deepRemove } = useDesignable(); const { designable, schema, deepRemove } = useDesignable();
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const { dragRef } = useContext(DraggableBlockContext); const { dragRef } = useContext(DraggableBlockContext);
const fieldUid = useContext(FormFieldUIDContext); const randomName = useContext(RandomNameContext);
const { removeDisplayField } = useDisplayFieldsContext(); const displayed = useDisplayedMapContext();
const fieldName = schema['x-component-props']?.['fieldName']; const fieldName = schema['x-component-props']?.['fieldName'];
if (!designable) { if (!designable) {
return null; return null;
@ -71,13 +71,13 @@ export const FieldDesignableBar = observer((props) => {
onClick={async () => { onClick={async () => {
const title = uid(); const title = uid();
field field
.query(field.address.concat(fieldUid, fieldName)) .query(field.address.concat(randomName, fieldName))
.take((f) => { .take((f) => {
f.title = title; f.title = title;
}); });
console.log( console.log(
'fieldfieldfieldfieldfield', 'fieldfieldfieldfieldfield',
field.address.concat(fieldUid, fieldName).entire, field.address.concat(randomName, fieldName).entire,
schema, schema,
); );
schema['title'] = title; schema['title'] = title;
@ -98,7 +98,7 @@ export const FieldDesignableBar = observer((props) => {
schema['x-component-props']?.['fieldName']; schema['x-component-props']?.['fieldName'];
console.log({ schema, removed, fieldName }); console.log({ schema, removed, fieldName });
const last = removed.pop(); const last = removed.pop();
removeDisplayField(fieldName); displayed.remove(fieldName);
if (isGridRowOrCol(last)) { if (isGridRowOrCol(last)) {
await removeSchema(last); await removeSchema(last);
} }

View File

@ -12,15 +12,7 @@ import {
useForm, useForm,
RecursionField, RecursionField,
} from '@formily/react'; } from '@formily/react';
import { import { useSchemaPath, SchemaField, useDesignable, removeSchema } from '../';
useSchemaPath,
SchemaField,
useDesignable,
removeSchema,
useCollectionContext,
useResourceContext,
ResourceContextProvider,
} from '../';
import get from 'lodash/get'; import get from 'lodash/get';
import { Button, Dropdown, Menu, message, Space } from 'antd'; import { Button, Dropdown, Menu, message, Space } from 'antd';
import { MenuOutlined, DragOutlined } from '@ant-design/icons'; import { MenuOutlined, DragOutlined } from '@ant-design/icons';
@ -39,41 +31,22 @@ import { FieldDesignableBar } from './Field.DesignableBar';
import { createContext } from 'react'; import { createContext } from 'react';
import { BlockItem } from '../block-item'; import { BlockItem } from '../block-item';
import { Resource } from '../../resource'; import { Resource } from '../../resource';
import { useMap } from 'ahooks'; import {
CollectionProvider,
const [DisplayFieldsContextProvider, useDisplayFieldsContext] = constate(() => { useCollectionContext,
const [displayFields, { set, remove, get }] = useMap([]); DisplayedMapProvider,
useDisplayedMapContext,
return { } from '../../constate';
displayFields,
hasDisplayField(fieldName) {
return !!get(fieldName);
},
addDisplayField(fieldName, schema) {
set(fieldName, schema);
},
getDisplayField(fieldName) {
return get(fieldName);
},
removeDisplayField(fieldName) {
remove(fieldName);
},
};
});
export { DisplayFieldsContextProvider, useDisplayFieldsContext };
export const Form: any = observer((props: any) => { export const Form: any = observer((props: any) => {
const { const {
useValues = () => ({}), useValues = () => ({}),
showDefaultButtons = false, showDefaultButtons = false,
resourceName,
...others ...others
} = props; } = props;
const { schema } = useDesignable(); const { schema } = useDesignable();
const initialValues = useValues(); const initialValues = useValues();
const form = useMemo(() => { const form = useMemo(() => {
console.log('Form.useMemo', initialValues);
return createForm({ return createForm({
initialValues, initialValues,
readPretty: schema['x-read-pretty'], readPretty: schema['x-read-pretty'],
@ -81,6 +54,7 @@ export const Form: any = observer((props: any) => {
}, []); }, []);
const path = useSchemaPath(); const path = useSchemaPath();
const scope = useContext(SchemaExpressionScopeContext); const scope = useContext(SchemaExpressionScopeContext);
const { collection } = useCollectionContext();
const content = ( const content = (
<FormProvider form={form}> <FormProvider form={form}>
{schema['x-decorator'] === 'Form' ? ( {schema['x-decorator'] === 'Form' ? (
@ -115,7 +89,7 @@ export const Form: any = observer((props: any) => {
onClick={async () => { onClick={async () => {
const values = await form.submit(); const values = await form.submit();
console.log({ values }); console.log({ values });
await Resource.make(resourceName).save(values); await Resource.make(props.resource).save(values);
message.success('保存成功'); message.success('保存成功');
await form.reset(); await form.reset();
}} }}
@ -134,49 +108,47 @@ export const Form: any = observer((props: any) => {
)} )}
</FormProvider> </FormProvider>
); );
return resourceName ? ( return (
// @ts-ignore <CollectionProvider
<ResourceContextProvider resourceName={resourceName}> collectionName={props.collectionName || collection?.name}
<DisplayFieldsContextProvider>{content}</DisplayFieldsContextProvider> >
</ResourceContextProvider> <DisplayedMapProvider>{content}</DisplayedMapProvider>
) : ( </CollectionProvider>
<DisplayFieldsContextProvider>{content}</DisplayFieldsContextProvider>
); );
}); });
export const FormFieldUIDContext = createContext(null); export const RandomNameContext = createContext(null);
Form.Field = observer((props: any) => { Form.Field = observer((props: any) => {
const { fieldName } = props; const { fieldName } = props;
const { schema } = useDesignable(); const { schema } = useDesignable();
const path = getSchemaPath(schema); const path = getSchemaPath(schema);
console.log('getSchemaPath', path); const { getField } = useCollectionContext();
const { addDisplayField } = useDisplayFieldsContext(); const displayed = useDisplayedMapContext();
const { resource = {} } = useResourceContext();
useEffect(() => { useEffect(() => {
if (fieldName) { if (fieldName) {
addDisplayField && addDisplayField(fieldName, schema); displayed.set(fieldName, schema);
} }
}, [fieldName, schema]); }, [fieldName, schema]);
if (!fieldName) { if (!fieldName) {
return null; return null;
} }
const field = resource?.fields?.find((item) => item.name === fieldName); const collectionField = getField(fieldName);
if (!field) { if (!collectionField) {
return null; return null;
} }
const name = useContext(FormFieldUIDContext); const randomName = useContext(RandomNameContext);
const title = schema['title'] || field.uiSchema['title']; const title = schema['title'] || collectionField?.uiSchema?.title;
return ( return (
<RecursionField <RecursionField
name={name} name={randomName}
schema={ schema={
new Schema({ new Schema({
'x-path': path, 'x-path': path,
type: 'void', type: 'void',
properties: { properties: {
[field.name]: { [collectionField.name]: {
...field.uiSchema, ...collectionField.uiSchema,
title, title,
'x-decorator': 'FormilyFormItem', 'x-decorator': 'FormilyFormItem',
}, },
@ -188,11 +160,10 @@ Form.Field = observer((props: any) => {
}); });
Form.Field.Item = observer((props) => { Form.Field.Item = observer((props) => {
const name = uid();
return ( return (
<FormFieldUIDContext.Provider value={name}> <RandomNameContext.Provider value={uid()}>
<BlockItem>{props.children}</BlockItem> <BlockItem>{props.children}</BlockItem>
</FormFieldUIDContext.Provider> </RandomNameContext.Provider>
); );
}); });

View File

@ -10,51 +10,6 @@ import { useCookieState, useRequest } from 'ahooks';
export * from '../components/schema-renderer'; export * from '../components/schema-renderer';
export function mapReadPretty(component: any, readPrettyProps?: any) {
return function (target) {
return observer(
(props) => {
const field = useField();
// if (!isVoidField(field) && field?.pattern === 'readPretty') {
if (field?.pattern === 'readPretty') {
return React.createElement(component, {
...readPrettyProps,
...props,
});
}
return React.createElement(target, props);
},
{
forwardRef: true,
},
);
};
}
export const VisibleContext = createContext(null);
export function useVisible(name, defaultValue = false) {
const [visible, setVisible] = useState(defaultValue);
return { visible, setVisible };
// const [visible, setVisible] = useCookieState(`${name}-visible`, {
// defaultValue: defaultValue ? 'true' : null,
// });
// return {
// visible: visible === 'true' ? true : false,
// setVisible: (value) => {
// setVisible(value ? 'true' : null);
// },
// }
}
export const DesignableBarContext = createContext(null);
const [PageTitleContextProvider, usePageTitleContext] = constate(({ defaultPageTitle }) => {
return useState(defaultPageTitle);
});
export { PageTitleContextProvider, usePageTitleContext };
export function useDefaultAction() { export function useDefaultAction() {
return { return {
async run() {}, async run() {},
@ -93,6 +48,9 @@ export async function deleteCollection(name) {
} }
export async function createSchema(schema: ISchema) { export async function createSchema(schema: ISchema) {
if (!schema) {
return;
}
if (!schema['key']) { if (!schema['key']) {
return; return;
} }
@ -125,36 +83,3 @@ export async function removeSchema(schema: ISchema) {
}, },
}); });
} }
const [CollectionContextProvider, useCollectionContext] = constate(() => {
return useRequest('collections:findAll', {
formatResult: (result) => result?.data,
});
});
export { CollectionContextProvider, useCollectionContext };
const [SwithDesignableContextProvider, useSwithDesignableContext] = constate(() => {
const [active, setActive] = useCookieState('useCookieDesignable');
return {
designable: active === 'true',
setDesignable(value) {
setActive(value ? 'true' : 'false');
}
}
});
export { SwithDesignableContextProvider, useSwithDesignableContext };
const [ResourceContextProvider, useResourceContext] = constate(
({ resourceName }) => {
// const schema = useFieldSchema();
const { data: collections = [], loading, refresh } = useCollectionContext();
const resource = collections.find((item) => item.name === resourceName);
console.log({ resource });
return { resource, refresh };
},
);
export { ResourceContextProvider, useResourceContext };

View File

@ -46,7 +46,7 @@ Input.URL = connect(AntdInput, mapReadPretty(Display.URL))
Input.DesignableBar = observer((props) => { Input.DesignableBar = observer((props) => {
const field = useField(); const field = useField();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const { root, currentSchema, schema, refresh, deepRemove } = useDesignable(); const { root, currentSchema, schema, refresh, deepRemove, remove } = useDesignable();
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const { dragRef } = useContext(DraggableBlockContext); const { dragRef } = useContext(DraggableBlockContext);
return ( return (
@ -71,6 +71,10 @@ Input.DesignableBar = observer((props) => {
<Menu.Item <Menu.Item
key={'update'} key={'update'}
onClick={() => { onClick={() => {
const title = uid();
schema['title'] = title;
field.title = title;
refresh();
console.log({ root, currentSchema, schema, fieldSchema }, getSchemaPath(fieldSchema)); console.log({ root, currentSchema, schema, fieldSchema }, getSchemaPath(fieldSchema));
}} }}
> >
@ -80,12 +84,13 @@ Input.DesignableBar = observer((props) => {
<Menu.Item <Menu.Item
key={'delete'} key={'delete'}
onClick={async () => { onClick={async () => {
const removed = deepRemove(); remove();
// console.log({ removed }) // const removed = deepRemove();
const last = removed.pop(); // // console.log({ removed })
if (isGridRowOrCol(last)) { // const last = removed.pop();
await removeSchema(last); // if (isGridRowOrCol(last)) {
} // await removeSchema(last);
// }
}} }}
> >

View File

@ -50,24 +50,16 @@ import {
DragOutlined, DragOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { IconPicker } from '../../components/icon-picker'; import { IconPicker } from '../../components/icon-picker';
import { import { createSchema, removeSchema, updateSchema, useDefaultAction } from '..';
createSchema,
removeSchema,
updateSchema,
useDefaultAction,
useVisible,
VisibleContext,
} from '..';
import { useMount } from 'ahooks'; import { useMount } from 'ahooks';
import './style.less'; import './style.less';
import { Link } from 'react-router-dom';
import { findPropertyByPath, getSchemaPath, useSchemaPath } from '../../'; import { findPropertyByPath, getSchemaPath, useSchemaPath } from '../../';
import { request } from '../';
import { generateDefaultSchema } from './defaultSchemas'; import { generateDefaultSchema } from './defaultSchemas';
import _, { cloneDeep, get, isNull } from 'lodash'; import _, { cloneDeep, get, isNull } from 'lodash';
import { FormDialog, FormItem, FormLayout, Input } from '@formily/antd'; import { FormDialog, FormItem, FormLayout, Input } from '@formily/antd';
import deepmerge from 'deepmerge'; import deepmerge from 'deepmerge';
import { onFieldChange } from '@formily/core'; import { onFieldChange } from '@formily/core';
import { VisibleContext } from '../../context';
export const MenuModeContext = createContext(null); export const MenuModeContext = createContext(null);
@ -101,11 +93,19 @@ const SideMenu = (props: any) => {
}; };
export const Menu: any = observer((props: any) => { export const Menu: any = observer((props: any) => {
const { mode, onSelect, sideMenuRef, defaultSelectedKeys = [], ...others } = props; const {
mode,
onSelect,
sideMenuRef,
defaultSelectedKeys = [],
...others
} = props;
const { designable, schema } = useDesignable(); const { designable, schema } = useDesignable();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
console.log('Menu.schema', schema, fieldSchema); console.log('Menu.schema', schema, fieldSchema);
const [selectedKey, setSelectedKey] = useState(defaultSelectedKeys[0]||null); const [selectedKey, setSelectedKey] = useState(
defaultSelectedKeys[0] || null,
);
const ref = useRef(); const ref = useRef();
const path = useSchemaPath(); const path = useSchemaPath();
const child = schema.properties && schema.properties[selectedKey]; const child = schema.properties && schema.properties[selectedKey];
@ -237,7 +237,7 @@ Menu.Action = observer((props: any) => {
const { icon, useAction = useDefaultAction, ...others } = props; const { icon, useAction = useDefaultAction, ...others } = props;
const { schema, DesignableBar } = useDesignable(); const { schema, DesignableBar } = useDesignable();
const field = useField(); const field = useField();
const { visible, setVisible } = useVisible(field.address.entire); const [visible, setVisible] = useState(false);
const { run } = useAction(); const { run } = useAction();
return ( return (
<VisibleContext.Provider value={[visible, setVisible]}> <VisibleContext.Provider value={[visible, setVisible]}>

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { PageHeader as AntdPageHeader } from 'antd'; import { PageHeader as AntdPageHeader } from 'antd';
import { observer } from '@formily/react'; import { observer } from '@formily/react';
import { usePageTitleContext } from '..'; import { usePageTitleContext } from '../../constate/PageTitle';
export const Page = observer((props) => { export const Page = observer((props) => {
const { children, ...others } = props; const { children, ...others } = props;

View File

@ -1,43 +0,0 @@
import React, { useState } from 'react';
import classNames from 'classnames';
import { useField, observer, RecursionField, Schema } from '@formily/react';
import { Dropdown, Menu } from 'antd';
import {
MenuOutlined,
ArrowUpOutlined,
ArrowDownOutlined,
DeleteOutlined,
} from '@ant-design/icons';
export const ColumnDesignableBar = (props) => {
const [active, setActive] = useState(false);
return (
<>
<Menu.Item
onClick={() => {
setActive(false);
}}
icon={<ArrowUpOutlined />}
>
</Menu.Item>
<Menu.Item
onClick={() => {
setActive(false);
}}
icon={<ArrowDownOutlined />}
>
</Menu.Item>
<Menu.Divider />
<Menu.Item
onClick={() => {
setActive(false);
}}
icon={<DeleteOutlined />}
>
</Menu.Item>
</>
);
};

View File

@ -1,43 +0,0 @@
import React, { useState } from 'react';
import classNames from 'classnames';
import { useField, observer, RecursionField, Schema } from '@formily/react';
import { Dropdown, Menu } from 'antd';
import {
MenuOutlined,
ArrowUpOutlined,
ArrowDownOutlined,
DeleteOutlined,
} from '@ant-design/icons';
export const DesignableBar = (props) => {
const [active, setActive] = useState(false);
return (
<>
<Menu.Item
onClick={() => {
setActive(false);
}}
icon={<ArrowUpOutlined />}
>
</Menu.Item>
<Menu.Item
onClick={() => {
setActive(false);
}}
icon={<ArrowDownOutlined />}
>
</Menu.Item>
<Menu.Divider />
<Menu.Item
onClick={() => {
setActive(false);
}}
icon={<DeleteOutlined />}
>
</Menu.Item>
</>
);
};

View File

@ -1,386 +0,0 @@
import React from 'react';
import { SchemaRenderer } from '../..';
import { Table, useTableCreateAction } from '../';
import { uid } from '@formily/shared';
import { createForm } from '@formily/core';
import { ISchema, observer } from '@formily/react';
function useAction() {
return {
async run() {
alert('自定义');
},
};
}
const schema: ISchema = {
name: `table_${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',
dragSort: 'sort',
showIndex: true,
defaultFilter: {},
defaultSort: [],
pagination: {
defaultPageSize: 50,
},
},
properties: {
[`action_bar_${uid()}`]: {
type: 'void',
'x-component': 'Table.ActionBar',
'x-designable-bar': 'Table.ActionBar.DesignableBar',
properties: {
action1: {
type: 'void',
title: '筛选',
'x-component': 'Action',
'x-designable-bar': 'Table.Filter.DesignableBar',
properties: {
popover1: {
type: 'void',
title: '弹窗标题',
'x-decorator': 'Form',
'x-component': 'Action.Popover',
'x-component-props': {},
properties: {
filter: {
name: 'filter',
type: 'object',
'x-component': 'Filter',
properties: {
column1: {
type: 'void',
title: '字段1',
'x-component': 'Filter.Column',
'x-component-props': {
operations: [
{ label: '等于', value: 'eq' },
{ label: '不等于', value: 'ne' },
],
},
properties: {
field1: {
type: 'string',
'x-component': 'Input',
},
},
},
column2: {
type: 'void',
title: '字段2',
'x-component': 'Filter.Column',
'x-component-props': {
operations: [
{ label: '大于', value: 'gt' },
{ label: '小于', value: 'lt' },
{ label: '非空', value: 'notNull', noValue: true },
],
},
properties: {
field1: {
type: 'number',
'x-component': 'InputNumber',
},
},
},
},
},
},
},
},
},
action2: {
type: 'void',
name: 'action1',
title: '新增',
'x-component': 'Action',
'x-designable-bar': 'Table.Action.DesignableBar',
properties: {
drawer1: {
type: 'void',
title: '抽屉标题',
'x-decorator': 'Form',
'x-component': 'Action.Modal',
'x-component-props': {
useOkAction: '{{ Table.useTableCreateAction }}',
},
properties: {
field1: {
type: 'string',
title: '字段1',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
field2: {
type: 'string',
title: '字段2',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
},
action3: {
type: 'void',
name: 'action1',
title: '删除',
'x-designable-bar': 'Table.Action.SimpleDesignableBar',
'x-component': 'Action',
'x-component-props': {
useAction: '{{ Table.useTableDestroyAction }}',
},
},
},
},
[`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': {},
},
},
},
// [`column_${uid()}`]: {
// type: 'void',
// title: '排序',
// 'x-component': 'Table.Column',
// properties: {
// sort: {
// type: 'void',
// 'x-component': 'Table.SortHandle',
// },
// },
// },
// [`column_${uid()}`]: {
// type: 'void',
// title: '序号',
// 'x-component': 'Table.Column',
// properties: {
// index: {
// type: 'void',
// 'x-component': 'Table.Index',
// },
// },
// },
[`column_${uid()}`]: {
type: 'void',
'x-component': 'Table.Column',
'x-component-props': {
className: 'nb-table-operation',
},
properties: {
[uid()]: {
type: 'void',
'x-component': 'Action.Dropdown',
'x-component-props': {
buttonProps: {
icon: 'EllipsisOutlined',
},
},
properties: {
[uid()]: {
type: 'void',
title: '操作 1',
'x-component': 'Menu.Action',
'x-component-props': {
useAction,
style: {
minWidth: 150,
},
disabled: true,
},
},
[uid()]: {
type: 'void',
title: '操作 2',
'x-component': 'Menu.Action',
'x-default-action': true,
'x-component-props': {
useAction,
},
},
[uid()]: {
type: 'void',
name: 'action1',
title: '查看',
'x-component': 'Menu.Action',
'x-designable-bar': 'Table.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',
},
},
},
},
},
},
},
},
},
[uid()]: {
type: 'void',
name: 'action1',
title: '修改',
'x-component': 'Menu.Action',
'x-designable-bar': 'Table.Action.DesignableBar',
properties: {
drawer1: {
type: 'void',
title: '编辑表单',
'x-decorator': 'Form',
'x-decorator-props': {
useValues: '{{ Table.useTableRow }}',
},
'x-component': 'Action.Modal',
'x-component-props': {
useOkAction: '{{ Table.useTableUpdateAction }}',
},
properties: {
field1: {
type: 'string',
title: '字段1',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
field2: {
type: 'string',
title: '字段2',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
},
[uid()]: {
type: 'void',
title: '删除',
'x-component': 'Menu.Action',
'x-component-props': {
useAction: '{{ Table.useTableDestroyAction }}',
},
},
},
},
},
},
[`column_${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',
},
},
},
[`column_${uid()}`]: {
type: 'void',
title: '字段2',
'x-component': 'Table.Column',
'x-designable-bar': 'Table.Column.DesignableBar',
properties: {
field2: {
type: 'string',
// title: '字段2',
required: true,
'x-read-pretty': true,
'x-decorator-props': {
feedbackLayout: 'popover',
},
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
};
const form = createForm({
// designable: true,
});
export default observer(() => {
return (
<div>
<SchemaRenderer
scope={{ Table, useTableCreateAction }}
form={form}
schema={schema}
/>
</div>
);
});

View File

@ -0,0 +1,261 @@
import React from 'react';
import { range } from 'lodash';
import { ISchema } from '@formily/react';
import { SchemaRenderer } from '../..';
import { uid } from '@formily/shared';
const loadDataSource = (params?: any): Promise<any> => {
const { page, pageSize = 50 } = params || {};
console.log({ pageSize });
return new Promise((resolve) => {
setTimeout(() => {
resolve({
list: range(50 || pageSize).map(() => {
return { id: uid(), title: uid() };
}),
total: 50,
});
}, 1000);
});
};
const schema: ISchema = {
type: 'array',
name: 'arr',
'x-component': 'Table',
'x-component-props': {
dragSort: true,
showIndex: true,
refreshRequestOnChange: false,
clientSidePagination: true,
dataRequest: '{{ loadDataSource }}',
pagination: {
pageSize: 2,
},
},
// default: range(50).map(() => {
// return { id: uid(), title: uid() };
// }),
properties: {
[uid()]: {
type: 'void',
'x-component': 'Table.ActionBar',
'x-designable-bar': 'Table.ActionBar.DesignableBar',
properties: {
[uid()]: {
type: 'void',
'x-component': 'Table.Filter',
'x-designable-bar': 'Table.Filter.DesignableBar',
'x-component-props': {
fieldNames: [],
},
},
[uid()]: {
type: 'void',
name: 'action1',
title: '新增',
'x-component': 'Action',
'x-designable-bar': 'Table.Action.DesignableBar',
properties: {
modal: {
type: 'void',
title: '新增数据',
'x-decorator': 'Form',
'x-component': 'Action.Modal',
'x-component-props': {
useOkAction: '{{ Table.useTableCreateAction }}',
},
properties: {
[uid()]: {
type: 'void',
'x-component': 'Grid',
'x-component-props': {
addNewComponent: 'AddNew.FormItem',
},
},
},
},
},
},
[uid()]: {
type: 'void',
name: 'action1',
title: '删除',
'x-component': 'Action',
'x-designable-bar': 'Table.Action.DesignableBar',
'x-component-props': {
useAction: '{{ Table.useTableDestroyAction }}',
},
},
},
},
column2: {
type: 'void',
title: '操作',
'x-component': 'Table.Column',
'x-component-props': {
className: 'nb-table-operation',
},
properties: {
action1: {
type: 'void',
name: 'dropdown1',
'x-component': 'Action.Dropdown',
'x-designable-bar': 'Action.DesignableBar',
'x-component-props': {
buttonProps: {
icon: 'EllipsisOutlined',
},
},
properties: {
[uid()]: {
type: 'void',
title: '操作 1',
'x-component': 'Menu.Action',
'x-component-props': {
style: {
minWidth: 150,
},
disabled: true,
},
},
[uid()]: {
type: 'void',
name: 'action1',
title: '查看',
'x-component': 'Menu.Action',
'x-designable-bar': 'Table.Action.DesignableBar',
properties: {
[uid()]: {
type: 'void',
title: '查看',
'x-component': 'Action.Modal',
'x-component-props': {
bodyStyle: {
background: '#f0f2f5',
paddingTop: 0,
},
},
properties: {
[uid()]: {
type: 'void',
'x-component': 'Tabs',
properties: {
[uid()]: {
type: 'void',
'x-component': 'Tabs.TabPane',
'x-component-props': {
tab: 'Tab1',
},
properties: {
[uid()]: {
type: 'void',
'x-component': 'Grid',
'x-component-props': {
addNewComponent: 'AddNew.PaneItem',
},
},
},
},
},
},
},
},
},
},
[uid()]: {
type: 'void',
title: '编辑',
'x-component': 'Menu.Action',
'x-designable-bar': 'Table.Action.DesignableBar',
properties: {
[uid()]: {
type: 'void',
title: '编辑数据',
'x-decorator': 'Form',
'x-decorator-props': {
useValues: '{{ Table.useTableRowRecord }}',
},
'x-component': 'Action.Modal',
'x-component-props': {
useOkAction: '{{ Table.useTableUpdateAction }}',
},
properties: {
[uid()]: {
type: 'void',
'x-component': 'Grid',
'x-component-props': {
addNewComponent: 'AddNew.FormItem',
},
properties: {
[uid()]: {
type: 'void',
'x-component': 'Grid.Row',
properties: {
[uid()]: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
title: {
title: '标题',
type: 'string',
'x-component': 'Input',
'x-decorator': 'FormItem',
},
},
},
},
},
},
},
},
},
},
},
[uid()]: {
type: 'void',
title: '删除',
'x-component': 'Menu.Action',
'x-component-props': {
useAction: '{{ Table.useTableDestroyAction }}',
},
},
},
},
},
},
column0: {
type: 'void',
title: 'ID',
'x-component': 'Table.Column',
'x-designable-bar': 'Table.Column.DesignableBar',
properties: {
id: {
type: 'string',
'x-component': 'Input',
'x-read-pretty': true,
},
},
},
column1: {
type: 'void',
title: 'Title',
'x-component': 'Table.Column',
properties: {
title: {
type: 'string',
'x-component': 'Input',
'x-read-pretty': true,
},
},
},
},
};
export default () => {
return (
<div>
<SchemaRenderer scope={{ loadDataSource }} schema={schema} />
</div>
);
};

View File

@ -60,4 +60,15 @@ group:
## Examples ## Examples
<code src="./demos/demo1.tsx"/> <code src="./demos/demo3.tsx"/>
## API
表格的几个重要概念
- collection 当前表格的数据表配置信息(主要是字段)
- resource 当前资源,用于处理 create、update、list、destroy
- service 当前表格的 resource.list 实例
- field 当前表格的 ArrayField 对象
- schema 当前表格的 json schema
- props 当前表格的参数

File diff suppressed because it is too large Load Diff

View File

@ -71,8 +71,8 @@
th.nb-table-operation, th.nb-table-operation,
td.nb-table-operation { td.nb-table-operation {
width: 24px; width: 50px;
text-align: center; text-align: center !important;
padding: 0 !important; padding: 0 !important;
&::before { &::before {
display: none; display: none;

View File

@ -1755,6 +1755,37 @@
resolved "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz#c3c5ae543c897caa9c2a68630bed355be5f9990f" resolved "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz#c3c5ae543c897caa9c2a68630bed355be5f9990f"
integrity sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ== integrity sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ==
"@dnd-kit/accessibility@^3.0.0":
version "3.0.0"
resolved "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.0.0.tgz#b56e3750414fd907b7d6972b3116aa8f96d07fde"
integrity sha512-QwaQ1IJHQHMMuAGOOYHQSx7h7vMZPfO97aDts8t5N/MY7n2QTDSnW+kF7uRQ1tVBkr6vJ+BqHWG5dlgGvwVjow==
dependencies:
tslib "^2.0.0"
"@dnd-kit/core@^3.1.1":
version "3.1.1"
resolved "https://registry.npmjs.org/@dnd-kit/core/-/core-3.1.1.tgz#c5ad6665931f5a51e74226220e58ac7514f3faf0"
integrity sha512-18YY5+1lTqJbGSg6JBSa/fjAOTUYAysFrQ5Ti8oppEPHFacQbC+owM51y2z2KN0LkDHBfGZKw2sFT7++ttwfpA==
dependencies:
"@dnd-kit/accessibility" "^3.0.0"
"@dnd-kit/utilities" "^2.0.0"
tslib "^2.0.0"
"@dnd-kit/sortable@^4.0.0":
version "4.0.0"
resolved "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-4.0.0.tgz#81dd2b014a16527cf89602dc40060d9ee4dad352"
integrity sha512-teYVFy6mQG/u6F6CaGxAkzPfiNJvguFzWfJ/zonYQRxfANHX6QJ6GziMG9KON/Ae9Q2ODJP8vib+guWJrDXeGg==
dependencies:
"@dnd-kit/utilities" "^2.0.0"
tslib "^2.0.0"
"@dnd-kit/utilities@^2.0.0":
version "2.0.0"
resolved "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-2.0.0.tgz#a8462dff65c6f606ecbe95273c7e263b14a1ab97"
integrity sha512-bjs49yMNzMM+BYRsBUhTqhTk6HEvhuY3leFt6Em6NaYGgygaMbtGbbXof/UXBv7rqyyi0OkmBBnrCCcxqS2t/g==
dependencies:
tslib "^2.0.0"
"@eslint/eslintrc@^0.4.2": "@eslint/eslintrc@^0.4.2":
version "0.4.2" version "0.4.2"
resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz#f63d0ef06f5c0c57d76c4ab5f63d3835c51b0179" resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz#f63d0ef06f5c0c57d76c4ab5f63d3835c51b0179"