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",
"dependencies": {
"@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/core": "^2.0.0-beta.72",
"@formily/react": "^2.0.0-beta.72",

View File

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

View File

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

View File

@ -52,7 +52,7 @@ import { DragAndDrop } from '../../schemas/drag-and-drop';
import { TreeSelect } from '../../schemas/tree-select';
import { Page } from '../../schemas/page';
import { Chart } from '../../schemas/chart';
import { useCollectionContext, useSwithDesignableContext } from '../../schemas';
import { useDesignableSwitchContext } from '../../constate/DesignableSwitch';
export const BlockContext = createContext({ dragRef: null });
@ -193,7 +193,7 @@ export function useSchemaComponent(component: string) {
}
export function useDesignable(path?: any) {
const { designable } = useSwithDesignableContext();
let { designable = true } = useDesignableSwitchContext();
const { schema = new Schema({}), refresh } = useContext(DesignableContext);
const schemaPath = path || useSchemaPath();
const fieldSchema = useFieldSchema();
@ -453,7 +453,7 @@ export interface SchemaRendererProps {
}
export const SchemaRenderer = (props: SchemaRendererProps) => {
const { designable } = useSwithDesignableContext();
const { designable = true } = useDesignableSwitchContext();
const [, refresh] = useState(uid());
const form = useMemo(() => props.form || createForm(), []);
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 {
protected options: ResourceOptions;
public options: ResourceOptions;
constructor(options: string | ResourceOptions) {
if (typeof options === 'string') {
@ -45,6 +45,15 @@ export class Resource {
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 = {}) {
const resourceKey = options.resourceKey || this.options.resourceKey;
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') {
return new Resource({ resourceName: options });
}
@ -76,6 +85,7 @@ export class Resource {
if (typeof options === 'object' && options.resourceName) {
return new Resource(options);
}
throw 'resource 参数错误';
console.warn('resource 初始化参数错误');
return null;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,15 +12,7 @@ import {
useForm,
RecursionField,
} from '@formily/react';
import {
useSchemaPath,
SchemaField,
useDesignable,
removeSchema,
useCollectionContext,
useResourceContext,
ResourceContextProvider,
} from '../';
import { useSchemaPath, SchemaField, useDesignable, removeSchema } from '../';
import get from 'lodash/get';
import { Button, Dropdown, Menu, message, Space } from 'antd';
import { MenuOutlined, DragOutlined } from '@ant-design/icons';
@ -39,48 +31,30 @@ import { FieldDesignableBar } from './Field.DesignableBar';
import { createContext } from 'react';
import { BlockItem } from '../block-item';
import { Resource } from '../../resource';
import { useMap } from 'ahooks';
const [DisplayFieldsContextProvider, useDisplayFieldsContext] = constate(() => {
const [displayFields, { set, remove, get }] = useMap([]);
return {
displayFields,
hasDisplayField(fieldName) {
return !!get(fieldName);
},
addDisplayField(fieldName, schema) {
set(fieldName, schema);
},
getDisplayField(fieldName) {
return get(fieldName);
},
removeDisplayField(fieldName) {
remove(fieldName);
},
};
});
export { DisplayFieldsContextProvider, useDisplayFieldsContext };
import {
CollectionProvider,
useCollectionContext,
DisplayedMapProvider,
useDisplayedMapContext,
} from '../../constate';
export const Form: any = observer((props: any) => {
const {
useValues = () => ({}),
showDefaultButtons = false,
resourceName,
...others
} = props;
const { schema } = useDesignable();
const initialValues = useValues();
const form = useMemo(() => {
console.log('Form.useMemo', initialValues);
return createForm({
initialValues,
return createForm({
initialValues,
readPretty: schema['x-read-pretty'],
});
}, []);
const path = useSchemaPath();
const scope = useContext(SchemaExpressionScopeContext);
const { collection } = useCollectionContext();
const content = (
<FormProvider form={form}>
{schema['x-decorator'] === 'Form' ? (
@ -115,7 +89,7 @@ export const Form: any = observer((props: any) => {
onClick={async () => {
const values = await form.submit();
console.log({ values });
await Resource.make(resourceName).save(values);
await Resource.make(props.resource).save(values);
message.success('保存成功');
await form.reset();
}}
@ -134,49 +108,47 @@ export const Form: any = observer((props: any) => {
)}
</FormProvider>
);
return resourceName ? (
// @ts-ignore
<ResourceContextProvider resourceName={resourceName}>
<DisplayFieldsContextProvider>{content}</DisplayFieldsContextProvider>
</ResourceContextProvider>
) : (
<DisplayFieldsContextProvider>{content}</DisplayFieldsContextProvider>
return (
<CollectionProvider
collectionName={props.collectionName || collection?.name}
>
<DisplayedMapProvider>{content}</DisplayedMapProvider>
</CollectionProvider>
);
});
export const FormFieldUIDContext = createContext(null);
export const RandomNameContext = createContext(null);
Form.Field = observer((props: any) => {
const { fieldName } = props;
const { schema } = useDesignable();
const path = getSchemaPath(schema);
console.log('getSchemaPath', path);
const { addDisplayField } = useDisplayFieldsContext();
const { resource = {} } = useResourceContext();
const { getField } = useCollectionContext();
const displayed = useDisplayedMapContext();
useEffect(() => {
if (fieldName) {
addDisplayField && addDisplayField(fieldName, schema);
displayed.set(fieldName, schema);
}
}, [fieldName, schema]);
if (!fieldName) {
return null;
}
const field = resource?.fields?.find((item) => item.name === fieldName);
if (!field) {
const collectionField = getField(fieldName);
if (!collectionField) {
return null;
}
const name = useContext(FormFieldUIDContext);
const title = schema['title'] || field.uiSchema['title'];
const randomName = useContext(RandomNameContext);
const title = schema['title'] || collectionField?.uiSchema?.title;
return (
<RecursionField
name={name}
name={randomName}
schema={
new Schema({
'x-path': path,
type: 'void',
properties: {
[field.name]: {
...field.uiSchema,
[collectionField.name]: {
...collectionField.uiSchema,
title,
'x-decorator': 'FormilyFormItem',
},
@ -188,11 +160,10 @@ Form.Field = observer((props: any) => {
});
Form.Field.Item = observer((props) => {
const name = uid();
return (
<FormFieldUIDContext.Provider value={name}>
<RandomNameContext.Provider value={uid()}>
<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 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() {
return {
async run() {},
@ -93,6 +48,9 @@ export async function deleteCollection(name) {
}
export async function createSchema(schema: ISchema) {
if (!schema) {
return;
}
if (!schema['key']) {
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) => {
const field = useField();
const fieldSchema = useFieldSchema();
const { root, currentSchema, schema, refresh, deepRemove } = useDesignable();
const { root, currentSchema, schema, refresh, deepRemove, remove } = useDesignable();
const [visible, setVisible] = useState(false);
const { dragRef } = useContext(DraggableBlockContext);
return (
@ -71,6 +71,10 @@ Input.DesignableBar = observer((props) => {
<Menu.Item
key={'update'}
onClick={() => {
const title = uid();
schema['title'] = title;
field.title = title;
refresh();
console.log({ root, currentSchema, schema, fieldSchema }, getSchemaPath(fieldSchema));
}}
>
@ -80,12 +84,13 @@ Input.DesignableBar = observer((props) => {
<Menu.Item
key={'delete'}
onClick={async () => {
const removed = deepRemove();
// console.log({ removed })
const last = removed.pop();
if (isGridRowOrCol(last)) {
await removeSchema(last);
}
remove();
// const removed = deepRemove();
// // console.log({ removed })
// const last = removed.pop();
// if (isGridRowOrCol(last)) {
// await removeSchema(last);
// }
}}
>

View File

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

View File

@ -1,7 +1,7 @@
import React from 'react';
import { PageHeader as AntdPageHeader } from 'antd';
import { observer } from '@formily/react';
import { usePageTitleContext } from '..';
import { usePageTitleContext } from '../../constate/PageTitle';
export const Page = observer((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
<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,
td.nb-table-operation {
width: 24px;
text-align: center;
width: 50px;
text-align: center !important;
padding: 0 !important;
&::before {
display: none;

View File

@ -1755,6 +1755,37 @@
resolved "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz#c3c5ae543c897caa9c2a68630bed355be5f9990f"
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":
version "0.4.2"
resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz#f63d0ef06f5c0c57d76c4ab5f63d3835c51b0179"