mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 06:55:50 +00:00
fix: table benchmark
This commit is contained in:
parent
17549f9f42
commit
4628355194
@ -49,6 +49,10 @@ export default defineConfig({
|
|||||||
title: 'Application',
|
title: 'Application',
|
||||||
type: 'group',
|
type: 'group',
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
title: 'tmp',
|
||||||
|
link: '/core/data-block/table-performance'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Application',
|
title: 'Application',
|
||||||
link: '/core/application/application',
|
link: '/core/application/application',
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
# Table 性能
|
||||||
|
|
||||||
|
## Antd Table
|
||||||
|
|
||||||
|
### Antd Table + String
|
||||||
|
|
||||||
|
<code src="./table-performance/antd-and-string.tsx"></code>
|
||||||
|
|
||||||
|
### Antd Table + Component
|
||||||
|
|
||||||
|
<code src="./table-performance/antd-and-component.tsx"></code>
|
||||||
|
|
||||||
|
### Antd Table + Component + Association
|
||||||
|
|
||||||
|
## Antd Table + formily
|
||||||
|
|
||||||
|
### Antd Table + formily + String
|
||||||
|
|
||||||
|
<code src="./table-performance/formily-and-string.tsx"></code>
|
||||||
|
|
||||||
|
### Antd Table + formily + Component
|
||||||
|
|
||||||
|
<code src="./table-performance/formily-and-component.tsx"></code>
|
||||||
|
|
||||||
|
### Antd Table + formily + Component + Association
|
||||||
|
|
||||||
|
## New NocoBase Table
|
||||||
|
|
||||||
|
### Full Functional Table
|
||||||
|
|
||||||
|
<code src="./table-performance/nocobase-table-and-record-and-collection-field-and-component.tsx"></code>
|
@ -0,0 +1,75 @@
|
|||||||
|
import { Button, Card, Table } from "antd";
|
||||||
|
import React, { FC, useContext } from "react";
|
||||||
|
|
||||||
|
import tableCollection from './tableCollection.json';
|
||||||
|
import tableData from './tableData.json';
|
||||||
|
import { AntdSchemaComponentPlugin, Application, Plugin, useCompile } from "@nocobase/client";
|
||||||
|
import { SchemaComponentsContext } from "@formily/react";
|
||||||
|
import { get } from 'lodash';
|
||||||
|
import MapPlugin from "@nocobase/plugin-map/client";
|
||||||
|
import FormulaFieldPlugin from "@nocobase/plugin-formula-field/client";
|
||||||
|
import ChinaRegionPlugin from "@nocobase/plugin-china-region/client";
|
||||||
|
|
||||||
|
const AssociationField: FC<{ value: any }> = ({ value }) => {
|
||||||
|
if (value === undefined || value === null) return null;
|
||||||
|
|
||||||
|
const text = JSON.stringify(value)
|
||||||
|
return <span>{text.slice(0, 20)}{text.length > 20 && '...'}</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
const Demo = () => {
|
||||||
|
const [show, setShow] = React.useState<boolean>(false);
|
||||||
|
const [dataSource, setDataSource] = React.useState<any>([]);
|
||||||
|
const [columns, setColumns] = React.useState<any>([]);
|
||||||
|
const components = useContext(SchemaComponentsContext);
|
||||||
|
const compile = useCompile();
|
||||||
|
return <Card>
|
||||||
|
{show && <Table scroll={{ x: 'max-content' }} rowKey={'id'} dataSource={dataSource} columns={columns} pagination={false} />}
|
||||||
|
|
||||||
|
<Button block onClick={() => {
|
||||||
|
setShow(true);
|
||||||
|
setDataSource(tableData.data);
|
||||||
|
const columns = tableCollection.fields.map((field: any) => {
|
||||||
|
const Component: any = get(components, field.uiSchema?.['x-component'] || 'Input');
|
||||||
|
const componentProps = compile(field.uiSchema?.['x-component-props']);
|
||||||
|
const ReadPretty = Component.ReadPretty ?? Component;
|
||||||
|
|
||||||
|
return ({
|
||||||
|
title: field.name,
|
||||||
|
dataIndex: field.name,
|
||||||
|
key: field.key,
|
||||||
|
render(v) {
|
||||||
|
return <ReadPretty {...componentProps} value={v} />;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const indexColumn = { title: 'index', key: 'index', render(_1, _2, index) { return index + 1 } }
|
||||||
|
setColumns([indexColumn, ...columns]);
|
||||||
|
}}>渲染 Table</Button>
|
||||||
|
</Card>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.addComponents({
|
||||||
|
AssociationField,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
initialEntries: ['/'],
|
||||||
|
},
|
||||||
|
plugins: [AntdSchemaComponentPlugin, MapPlugin, FormulaFieldPlugin, ChinaRegionPlugin, MyPlugin],
|
||||||
|
})
|
||||||
|
|
||||||
|
app.router.add('home', {
|
||||||
|
path: '/',
|
||||||
|
Component: Demo,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,36 @@
|
|||||||
|
import { Button, Card, Table } from "antd";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import tableCollection from './tableCollection.json';
|
||||||
|
import tableData from './tableData.json';
|
||||||
|
|
||||||
|
const Demo = () => {
|
||||||
|
const [show, setShow] = React.useState<boolean>(false);
|
||||||
|
const [dataSource, setDataSource] = React.useState<any>([]);
|
||||||
|
const [columns, setColumns] = React.useState<any>([]);
|
||||||
|
|
||||||
|
return <Card>
|
||||||
|
{show && <Table scroll={{ x: 'max-content' }} rowKey={'id'} dataSource={dataSource} columns={columns} pagination={false} />}
|
||||||
|
|
||||||
|
<Button block onClick={() => {
|
||||||
|
setShow(true);
|
||||||
|
setDataSource(tableData.data);
|
||||||
|
const columns = tableCollection.fields.map((field: any) => ({
|
||||||
|
title: field.name,
|
||||||
|
dataIndex: field.name,
|
||||||
|
key: field.key,
|
||||||
|
render(v) {
|
||||||
|
if (v && typeof v === 'object') {
|
||||||
|
return JSON.stringify(v);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const indexColumn = { title: 'index', key: 'index', render(_1, _2, index) { return index + 1 } }
|
||||||
|
setColumns([indexColumn, ...columns]);
|
||||||
|
}}>渲染 Table</Button>
|
||||||
|
</Card>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Demo;
|
@ -0,0 +1,117 @@
|
|||||||
|
import React, { FC, useContext } from 'react'
|
||||||
|
import {
|
||||||
|
FormItem,
|
||||||
|
ArrayTable,
|
||||||
|
} from '@formily/antd-v5'
|
||||||
|
import { Field, SchemaComponentsContext, useFieldSchema } from '@formily/react'
|
||||||
|
import { Button, Card } from 'antd'
|
||||||
|
|
||||||
|
import tableCollection from './tableCollection.json';
|
||||||
|
import tableData from './tableData.json';
|
||||||
|
import { get, merge } from 'lodash'
|
||||||
|
import { AntdSchemaComponentPlugin, Application, Plugin, SchemaComponent, useCompile } from '@nocobase/client'
|
||||||
|
import MapPlugin from '@nocobase/plugin-map/client'
|
||||||
|
import FormulaFieldPlugin from '@nocobase/plugin-formula-field/client'
|
||||||
|
import ChinaRegionPlugin from '@nocobase/plugin-china-region/client'
|
||||||
|
|
||||||
|
const AssociationField: FC<{ value: any }> = ({ value }) => {
|
||||||
|
if (value === undefined || value === null) return null;
|
||||||
|
const text = JSON.stringify(value)
|
||||||
|
return <span>{text.slice(0, 20)}{text.length > 20 && '...'}</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldsMap = tableCollection.fields.reduce((acc, field) => {
|
||||||
|
acc[field.name] = field;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const CollectionField: FC<{ value: any }> = ({ value }) => {
|
||||||
|
const compile = useCompile();
|
||||||
|
const components = useContext(SchemaComponentsContext);
|
||||||
|
const fieldSchema: any = useFieldSchema();
|
||||||
|
const collectionField = get(fieldsMap, fieldSchema.name);
|
||||||
|
const Component: any = get(components, collectionField?.uiSchema?.['x-component'] || 'Input');
|
||||||
|
const componentProps = compile(merge(fieldSchema['x-component-props'], collectionField.uiSchema?.['x-component-props']));
|
||||||
|
return <Component {...componentProps} value={value} />
|
||||||
|
}
|
||||||
|
|
||||||
|
const Demo = () => {
|
||||||
|
const [show, setShow] = React.useState<boolean>(false);
|
||||||
|
const [schema, setSchema] = React.useState<any>(null);
|
||||||
|
|
||||||
|
return <Card>
|
||||||
|
{show && <SchemaComponent schema={schema} />}
|
||||||
|
|
||||||
|
<Button block onClick={() => {
|
||||||
|
setShow(true);
|
||||||
|
const columns = tableCollection.fields.reduce((acc, field: any) => {
|
||||||
|
acc[field.name] = {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayTable.Column',
|
||||||
|
'x-component-props': { width: 200, title: field.name },
|
||||||
|
properties: {
|
||||||
|
[field.name]: {
|
||||||
|
type: 'string',
|
||||||
|
// pattern: 'readPretty',
|
||||||
|
'x-pattern': 'readPretty',
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
setSchema({
|
||||||
|
type: 'object',
|
||||||
|
name: 'table',
|
||||||
|
default: {
|
||||||
|
array: tableData.data
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
array: {
|
||||||
|
type: 'array',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'ArrayTable',
|
||||||
|
'x-component-props': {
|
||||||
|
rowKey: 'id',
|
||||||
|
pagination: {
|
||||||
|
pageSize: 100,
|
||||||
|
},
|
||||||
|
scroll: { x: 'max-content' },
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: columns,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}}>渲染 Table</Button>
|
||||||
|
</Card>
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.addComponents({
|
||||||
|
AssociationField,
|
||||||
|
ArrayTable,
|
||||||
|
FormItem,
|
||||||
|
CollectionField,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
initialEntries: ['/'],
|
||||||
|
},
|
||||||
|
plugins: [AntdSchemaComponentPlugin, MapPlugin, ChinaRegionPlugin, FormulaFieldPlugin, MyPlugin],
|
||||||
|
})
|
||||||
|
|
||||||
|
app.router.add('home', {
|
||||||
|
path: '/',
|
||||||
|
Component: Demo,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
@ -0,0 +1,90 @@
|
|||||||
|
import React, { FC, useMemo } from 'react'
|
||||||
|
import {
|
||||||
|
FormItem,
|
||||||
|
ArrayTable,
|
||||||
|
} from '@formily/antd-v5'
|
||||||
|
import { createForm } from '@formily/core'
|
||||||
|
import { FormProvider, createSchemaField } from '@formily/react'
|
||||||
|
import { Button, Card } from 'antd'
|
||||||
|
|
||||||
|
import tableCollection from './tableCollection.json';
|
||||||
|
import tableData from './tableData.json';
|
||||||
|
|
||||||
|
const CollectionField = ({ value }) => {
|
||||||
|
return <span>{value && typeof value === 'object' ? JSON.stringify(value) : value}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SchemaField = createSchemaField({
|
||||||
|
components: {
|
||||||
|
ArrayTable,
|
||||||
|
FormItem,
|
||||||
|
CollectionField,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const Demo = () => {
|
||||||
|
const [show, setShow] = React.useState<boolean>(false);
|
||||||
|
const [schema, setSchema] = React.useState<any>(null);
|
||||||
|
const form = useMemo(() => createForm(), []);
|
||||||
|
|
||||||
|
return <Card>
|
||||||
|
{show && <FormProvider form={form}>
|
||||||
|
<SchemaField schema={schema} />
|
||||||
|
</FormProvider>}
|
||||||
|
|
||||||
|
<Button block onClick={() => {
|
||||||
|
setShow(true);
|
||||||
|
form.setInitialValues({ array: tableData.data });
|
||||||
|
const indexColumn = {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayTable.Column',
|
||||||
|
'x-component-props': { width: 50, title: 'Index', align: 'center' },
|
||||||
|
properties: {
|
||||||
|
index: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'span',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const columns = tableCollection.fields.reduce((acc, field: any) => {
|
||||||
|
acc[field.name] = {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayTable.Column',
|
||||||
|
'x-component-props': { width: 200, title: field.name },
|
||||||
|
properties: {
|
||||||
|
[field.name]: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, { index: indexColumn });
|
||||||
|
|
||||||
|
setSchema({
|
||||||
|
type: 'object',
|
||||||
|
name: 'table',
|
||||||
|
properties: {
|
||||||
|
array: {
|
||||||
|
type: 'array',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'ArrayTable',
|
||||||
|
'x-component-props': {
|
||||||
|
rowKey: 'id',
|
||||||
|
pagination: {
|
||||||
|
pageSize: 100,
|
||||||
|
},
|
||||||
|
scroll: { x: 'max-content' },
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: columns,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}}>渲染 Table</Button>
|
||||||
|
</Card>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Demo;
|
@ -0,0 +1,392 @@
|
|||||||
|
import { Button, Drawer, Space, Table, TableProps } from 'antd';
|
||||||
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
|
import {
|
||||||
|
AntdSchemaComponentPlugin,
|
||||||
|
CardItem,
|
||||||
|
CollectionField,
|
||||||
|
ColorPicker,
|
||||||
|
DataBlockProvider,
|
||||||
|
FormItem,
|
||||||
|
Input,
|
||||||
|
InputNumber,
|
||||||
|
Plugin,
|
||||||
|
SchemaComponent,
|
||||||
|
SchemaInitializer,
|
||||||
|
SchemaInitializerItem,
|
||||||
|
SchemaSettings,
|
||||||
|
SchemaToolbar,
|
||||||
|
allBuiltInFieldInterfaces,
|
||||||
|
useCollection,
|
||||||
|
useCompile,
|
||||||
|
useDataBlock,
|
||||||
|
useDataBlockProps,
|
||||||
|
useDataBlockRequest,
|
||||||
|
useDataSourceManager,
|
||||||
|
useSchemaInitializer,
|
||||||
|
useSchemaInitializerRender,
|
||||||
|
withDynamicSchemaProps,
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import { Application } from '@nocobase/client';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
import { ISchema, RecursionField, observer, useField, useFieldSchema } from '@formily/react';
|
||||||
|
|
||||||
|
import tableCollection from './tableCollection.json';
|
||||||
|
import tableData from './tableData.json';
|
||||||
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
|
import MapPlugin from '@nocobase/plugin-map/client';
|
||||||
|
import FormulaFieldPlugin from '@nocobase/plugin-formula-field/client';
|
||||||
|
|
||||||
|
const MyTable = withDynamicSchemaProps(Table, { displayName: 'MyTable' });
|
||||||
|
|
||||||
|
const TableColumn = observer(() => {
|
||||||
|
const field = useField<any>();
|
||||||
|
return <div>{field.title}</div>;
|
||||||
|
});
|
||||||
|
|
||||||
|
function useTableProps(): TableProps<any> {
|
||||||
|
const { tableProps } = useDataBlockProps();
|
||||||
|
const { data, loading } = useDataBlockRequest<any[]>();
|
||||||
|
const dataSource = useMemo(() => data?.data || [], [data]);
|
||||||
|
const collection = useCollection();
|
||||||
|
const field = useField<any>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
field.value = dataSource;
|
||||||
|
}, [dataSource]);
|
||||||
|
|
||||||
|
const columns = useMemo(() => {
|
||||||
|
return collection?.getFields().map((collectionField) => {
|
||||||
|
const tableFieldSchema = {
|
||||||
|
name: collectionField.name,
|
||||||
|
type: 'void',
|
||||||
|
title: collectionField.uiSchema?.title || collectionField.name,
|
||||||
|
'x-component': 'TableColumn',
|
||||||
|
properties: {
|
||||||
|
[collectionField.name]: {
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-decorator-props': {
|
||||||
|
labelStyle: {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: <RecursionField name={collectionField.name} schema={tableFieldSchema} onlyRenderSelf />,
|
||||||
|
dataIndex: collectionField.name,
|
||||||
|
render(value, record, index) {
|
||||||
|
return (
|
||||||
|
<RecursionField basePath={field.address.concat(index)} onlyRenderProperties schema={tableFieldSchema} />
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, [collection]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...tableProps,
|
||||||
|
pagination: false,
|
||||||
|
loading,
|
||||||
|
dataSource,
|
||||||
|
columns,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const myTableSettings = new SchemaSettings({
|
||||||
|
name: 'myTableSettings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'bordered',
|
||||||
|
type: 'switch',
|
||||||
|
useComponentProps() {
|
||||||
|
const { props: blockSettingsProps, dn } = useDataBlock();
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: 'Bordered',
|
||||||
|
checked: !!blockSettingsProps.tableProps?.bordered,
|
||||||
|
onChange: (checked) => {
|
||||||
|
// 修改 schema
|
||||||
|
dn.deepMerge({ 'x-decorator-props': { tableProps: { bordered: checked } } });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'remove',
|
||||||
|
name: 'remove',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const AddBlockButton = observer(
|
||||||
|
() => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const { exists, render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
||||||
|
if (!exists) {
|
||||||
|
console.log('exists', exists);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return render();
|
||||||
|
},
|
||||||
|
{ displayName: 'AddBlockButton' },
|
||||||
|
);
|
||||||
|
|
||||||
|
const Page = observer(
|
||||||
|
(props) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{props.children}
|
||||||
|
<AddBlockButton />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Page' },
|
||||||
|
);
|
||||||
|
|
||||||
|
function useCollectionMenuItems() {
|
||||||
|
const dataSourceManager = useDataSourceManager();
|
||||||
|
const allCollections = dataSourceManager.getAllCollections();
|
||||||
|
const menus = useMemo(
|
||||||
|
() =>
|
||||||
|
allCollections.map((item) => {
|
||||||
|
const { key, displayName, collections } = item;
|
||||||
|
return {
|
||||||
|
name: key,
|
||||||
|
label: displayName,
|
||||||
|
type: 'subMenu',
|
||||||
|
children: collections.map((collection) => {
|
||||||
|
return {
|
||||||
|
name: collection.name,
|
||||||
|
label: collection.title,
|
||||||
|
collection: collection.name,
|
||||||
|
dataSource: key,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
[allCollections],
|
||||||
|
);
|
||||||
|
|
||||||
|
return menus;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CreateAction = () => {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const showDrawer = () => {
|
||||||
|
setOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
setOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const compile = useCompile();
|
||||||
|
const collection = useCollection();
|
||||||
|
const title = compile(collection.title);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button type="primary" onClick={showDrawer}>
|
||||||
|
Add New
|
||||||
|
</Button>
|
||||||
|
<Drawer title={`${title} | Add New`} onClose={onClose} open={open}>
|
||||||
|
<p>Some contents...</p>
|
||||||
|
</Drawer>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const RefreshAction = () => {
|
||||||
|
const { refresh } = useDataBlockRequest();
|
||||||
|
return <Button onClick={refresh}>Refresh</Button>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ActionBar = ({ children }) => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: 'var(--nb-spacing)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Space>
|
||||||
|
{children}
|
||||||
|
{render()}
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CreateActionInitializer = () => {
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
const handleClick = () => {
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'CreateAction',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return <SchemaInitializerItem title={'Add New'} onClick={handleClick}></SchemaInitializerItem>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const RefreshActionInitializer = () => {
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
const handleClick = () => {
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'RefreshAction',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return <SchemaInitializerItem title={'Refresh'} onClick={handleClick}></SchemaInitializerItem>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const tableActionInitializers = new SchemaInitializer({
|
||||||
|
name: 'tableActionInitializers',
|
||||||
|
title: 'Configure actions',
|
||||||
|
icon: 'SettingOutlined',
|
||||||
|
style: {
|
||||||
|
marginLeft: 8,
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
type: 'item',
|
||||||
|
name: 'addNew',
|
||||||
|
Component: CreateActionInitializer,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'item',
|
||||||
|
name: 'refresh',
|
||||||
|
Component: RefreshActionInitializer,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const TableDataBlockInitializer = () => {
|
||||||
|
const { insert, setVisible } = useSchemaInitializer();
|
||||||
|
|
||||||
|
const handleClick = ({ item }) => {
|
||||||
|
const tableSchema = {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
'x-settings': 'myTableSettings',
|
||||||
|
'x-decorator': 'DataBlockProvider',
|
||||||
|
'x-toolbar': 'MyToolbar',
|
||||||
|
'x-decorator-props': {
|
||||||
|
collection: item.collection,
|
||||||
|
dataSource: item.dataSource,
|
||||||
|
action: 'list',
|
||||||
|
tableProps: {
|
||||||
|
pagination: false,
|
||||||
|
scroll: { x: 'max-content' },
|
||||||
|
rowKey: 'id'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-initializer': 'tableActionInitializers',
|
||||||
|
},
|
||||||
|
[uid()]: {
|
||||||
|
type: 'array',
|
||||||
|
'x-component': 'MyTable',
|
||||||
|
'x-use-component-props': 'useTableProps',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
insert(tableSchema);
|
||||||
|
setVisible(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const menuItems = useCollectionMenuItems();
|
||||||
|
|
||||||
|
return <SchemaInitializerItem title={'Table'} items={menuItems} onClick={handleClick} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const MyToolbar = (props) => {
|
||||||
|
const collection = useCollection();
|
||||||
|
const compile = useCompile();
|
||||||
|
return <SchemaToolbar title={`${compile(collection.title)}`} {...props} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const myInitializer = new SchemaInitializer({
|
||||||
|
name: 'myInitializer',
|
||||||
|
title: 'Add Block',
|
||||||
|
insertPosition: 'beforeEnd',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'table',
|
||||||
|
Component: TableDataBlockInitializer,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const rootSchema: ISchema = {
|
||||||
|
type: 'void',
|
||||||
|
name: 'root',
|
||||||
|
'x-component': 'Page',
|
||||||
|
'x-initializer': 'myInitializer',
|
||||||
|
};
|
||||||
|
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.addComponents({ MyTable, TableColumn, MyToolbar, ActionBar, CreateAction, RefreshAction });
|
||||||
|
this.app.addComponents({
|
||||||
|
Page,
|
||||||
|
AddBlockButton,
|
||||||
|
CardItem,
|
||||||
|
DataBlockProvider,
|
||||||
|
InputNumber,
|
||||||
|
Input,
|
||||||
|
CollectionField,
|
||||||
|
ColorPicker,
|
||||||
|
FormItem,
|
||||||
|
})
|
||||||
|
this.app.schemaInitializerManager.add(myInitializer);
|
||||||
|
this.app.schemaSettingsManager.add(myTableSettings);
|
||||||
|
this.app.addScopes({ useTableProps });
|
||||||
|
this.app.schemaInitializerManager.add(tableActionInitializers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Root = () => {
|
||||||
|
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
plugins: [AntdSchemaComponentPlugin, MapPlugin, FormulaFieldPlugin, MyPlugin],
|
||||||
|
components: {
|
||||||
|
|
||||||
|
},
|
||||||
|
router: {
|
||||||
|
type: 'memory',
|
||||||
|
initialEntries: ['/'],
|
||||||
|
},
|
||||||
|
designable: true,
|
||||||
|
dataSourceManager: {
|
||||||
|
collections: [tableCollection as any],
|
||||||
|
fieldInterfaces: allBuiltInFieldInterfaces,
|
||||||
|
},
|
||||||
|
apiClient: {
|
||||||
|
baseURL: 'http://localhost:8000',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
app.router.add('home', {
|
||||||
|
path: '/',
|
||||||
|
Component: Root,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mock = new MockAdapter(app.apiClient.axios);
|
||||||
|
mock.onGet('tt_pmt_staff:list').reply(200, tableData);
|
||||||
|
|
||||||
|
export default app.getRootComponent();
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -51,6 +51,47 @@ import {
|
|||||||
import { DEFAULT_DATA_SOURCE_KEY, DEFAULT_DATA_SOURCE_TITLE } from '../data-source/data-source/DataSourceManager';
|
import { DEFAULT_DATA_SOURCE_KEY, DEFAULT_DATA_SOURCE_TITLE } from '../data-source/data-source/DataSourceManager';
|
||||||
import { DataSource } from '../data-source/data-source/DataSource';
|
import { DataSource } from '../data-source/data-source/DataSource';
|
||||||
|
|
||||||
|
export const allBuiltInFieldInterfaces = [
|
||||||
|
CheckboxFieldInterface,
|
||||||
|
CheckboxGroupFieldInterface,
|
||||||
|
ChinaRegionFieldInterface,
|
||||||
|
CollectionSelectFieldInterface,
|
||||||
|
ColorFieldInterface,
|
||||||
|
CreatedAtFieldInterface,
|
||||||
|
CreatedByFieldInterface,
|
||||||
|
DatetimeFieldInterface,
|
||||||
|
EmailFieldInterface,
|
||||||
|
IconFieldInterface,
|
||||||
|
IdFieldInterface,
|
||||||
|
InputFieldInterface,
|
||||||
|
IntegerFieldInterface,
|
||||||
|
JsonFieldInterface,
|
||||||
|
LinkToFieldInterface,
|
||||||
|
M2MFieldInterface,
|
||||||
|
M2OFieldInterface,
|
||||||
|
MarkdownFieldInterface,
|
||||||
|
MultipleSelectFieldInterface,
|
||||||
|
NumberFieldInterface,
|
||||||
|
O2MFieldInterface,
|
||||||
|
O2OFieldInterface,
|
||||||
|
OHOFieldInterface,
|
||||||
|
OBOFieldInterface,
|
||||||
|
PasswordFieldInterface,
|
||||||
|
PercentFieldInterface,
|
||||||
|
PhoneFieldInterface,
|
||||||
|
RadioGroupFieldInterface,
|
||||||
|
RichTextFieldInterface,
|
||||||
|
SelectFieldInterface,
|
||||||
|
SubTableFieldInterface,
|
||||||
|
TableoidFieldInterface,
|
||||||
|
TextareaFieldInterface,
|
||||||
|
TimeFieldInterface,
|
||||||
|
UpdatedAtFieldInterface,
|
||||||
|
UpdatedByFieldInterface,
|
||||||
|
UrlFieldInterface,
|
||||||
|
SortFieldInterface,
|
||||||
|
]
|
||||||
|
|
||||||
class MainDataSource extends DataSource {
|
class MainDataSource extends DataSource {
|
||||||
async getDataSource() {
|
async getDataSource() {
|
||||||
const service = await this.app.apiClient.request({
|
const service = await this.app.apiClient.request({
|
||||||
@ -75,9 +116,8 @@ class MainDataSource extends DataSource {
|
|||||||
export class CollectionPlugin extends Plugin {
|
export class CollectionPlugin extends Plugin {
|
||||||
async load() {
|
async load() {
|
||||||
this.dataSourceManager.addCollectionMixins([InheritanceCollectionMixin]);
|
this.dataSourceManager.addCollectionMixins([InheritanceCollectionMixin]);
|
||||||
this.addFieldInterfaces();
|
|
||||||
this.addCollectionTemplates();
|
this.addCollectionTemplates();
|
||||||
this.addFieldInterfaces();
|
this.dataSourceManager.addFieldInterfaces(allBuiltInFieldInterfaces);
|
||||||
this.addFieldInterfaceGroups();
|
this.addFieldInterfaceGroups();
|
||||||
|
|
||||||
this.dataSourceManager.addDataSource(MainDataSource, {
|
this.dataSourceManager.addDataSource(MainDataSource, {
|
||||||
@ -115,49 +155,6 @@ export class CollectionPlugin extends Plugin {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addFieldInterfaces() {
|
|
||||||
this.dataSourceManager.addFieldInterfaces([
|
|
||||||
CheckboxFieldInterface,
|
|
||||||
CheckboxGroupFieldInterface,
|
|
||||||
ChinaRegionFieldInterface,
|
|
||||||
CollectionSelectFieldInterface,
|
|
||||||
ColorFieldInterface,
|
|
||||||
CreatedAtFieldInterface,
|
|
||||||
CreatedByFieldInterface,
|
|
||||||
DatetimeFieldInterface,
|
|
||||||
EmailFieldInterface,
|
|
||||||
IconFieldInterface,
|
|
||||||
IdFieldInterface,
|
|
||||||
InputFieldInterface,
|
|
||||||
IntegerFieldInterface,
|
|
||||||
JsonFieldInterface,
|
|
||||||
LinkToFieldInterface,
|
|
||||||
M2MFieldInterface,
|
|
||||||
M2OFieldInterface,
|
|
||||||
MarkdownFieldInterface,
|
|
||||||
MultipleSelectFieldInterface,
|
|
||||||
NumberFieldInterface,
|
|
||||||
O2MFieldInterface,
|
|
||||||
O2OFieldInterface,
|
|
||||||
OHOFieldInterface,
|
|
||||||
OBOFieldInterface,
|
|
||||||
PasswordFieldInterface,
|
|
||||||
PercentFieldInterface,
|
|
||||||
PhoneFieldInterface,
|
|
||||||
RadioGroupFieldInterface,
|
|
||||||
RichTextFieldInterface,
|
|
||||||
SelectFieldInterface,
|
|
||||||
SubTableFieldInterface,
|
|
||||||
TableoidFieldInterface,
|
|
||||||
TextareaFieldInterface,
|
|
||||||
TimeFieldInterface,
|
|
||||||
UpdatedAtFieldInterface,
|
|
||||||
UpdatedByFieldInterface,
|
|
||||||
UrlFieldInterface,
|
|
||||||
SortFieldInterface,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
addCollectionTemplates() {
|
addCollectionTemplates() {
|
||||||
this.dataSourceManager.addCollectionTemplates([
|
this.dataSourceManager.addCollectionTemplates([
|
||||||
GeneralCollectionTemplate,
|
GeneralCollectionTemplate,
|
||||||
|
@ -11,7 +11,7 @@ import { defaultFieldNames } from './defaultFieldNames';
|
|||||||
|
|
||||||
const useDefDataSource = (options) => {
|
const useDefDataSource = (options) => {
|
||||||
const field = useField<ArrayField>();
|
const field = useField<ArrayField>();
|
||||||
return useRequest(() => Promise.resolve({ data: field.dataSource || [] }), options);
|
return useRequest(() => Promise.resolve({ data: field?.dataSource || [] }), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
const useDefLoadData = (props: any) => {
|
const useDefLoadData = (props: any) => {
|
||||||
@ -37,7 +37,9 @@ export const Cascader = connect(
|
|||||||
const loadData = useLoadData(props);
|
const loadData = useLoadData(props);
|
||||||
const { loading, run } = useDataSource({
|
const { loading, run } = useDataSource({
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
field.dataSource = data?.data || [];
|
if (field) {
|
||||||
|
field.dataSource = data?.data || [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// 兼容值为 object[] 的情况
|
// 兼容值为 object[] 的情况
|
||||||
@ -74,7 +76,7 @@ export const Cascader = connect(
|
|||||||
<AntdCascader
|
<AntdCascader
|
||||||
loading={loading}
|
loading={loading}
|
||||||
{...others}
|
{...others}
|
||||||
options={field.dataSource}
|
options={field?.dataSource}
|
||||||
loadData={loadData}
|
loadData={loadData}
|
||||||
changeOnSelect={isBoolean(changeOnSelectLast) ? !changeOnSelectLast : changeOnSelect}
|
changeOnSelect={isBoolean(changeOnSelectLast) ? !changeOnSelectLast : changeOnSelect}
|
||||||
value={toValue()}
|
value={toValue()}
|
||||||
|
@ -46,4 +46,8 @@ export const Input: ComposedInput = Object.assign(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
(Input as any).TextArea.ReadPretty = ReadPretty.TextArea;
|
||||||
|
(Input as any).URL.ReadPretty = ReadPretty.URL;
|
||||||
|
(Input as any).JSON.ReadPretty = ReadPretty.JSON;
|
||||||
|
|
||||||
export default Input;
|
export default Input;
|
||||||
|
@ -19,8 +19,8 @@ export const ReadPretty = observer(
|
|||||||
return <div />;
|
return <div />;
|
||||||
}
|
}
|
||||||
const collectionField = useCollectionField();
|
const collectionField = useCollectionField();
|
||||||
const dataSource = field.dataSource || props.options || collectionField?.uiSchema.enum || [];
|
const dataSource = field?.dataSource || props.options || collectionField?.uiSchema.enum || [];
|
||||||
const currentOptions = getCurrentOptions(field.value, dataSource, fieldNames);
|
const currentOptions = getCurrentOptions(field?.value, dataSource, fieldNames);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -137,7 +137,7 @@ export const useValidator = (validator: (value: any) => string) => {
|
|||||||
const field = useField<Field>();
|
const field = useField<Field>();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const dispose = reaction(
|
const dispose = reaction(
|
||||||
() => field.value,
|
() => field?.value,
|
||||||
(value) => {
|
(value) => {
|
||||||
const message = validator(value);
|
const message = validator(value);
|
||||||
field.setFeedback({
|
field.setFeedback({
|
||||||
|
@ -4,7 +4,7 @@ import { useAPIClient, useRequest } from '@nocobase/client';
|
|||||||
|
|
||||||
export const useChinaRegionDataSource = (options) => {
|
export const useChinaRegionDataSource = (options) => {
|
||||||
const field = useField<ArrayField>();
|
const field = useField<ArrayField>();
|
||||||
const maxLevel = field.componentProps.maxLevel;
|
const maxLevel = field?.componentProps.maxLevel;
|
||||||
return useRequest(
|
return useRequest(
|
||||||
{
|
{
|
||||||
resource: 'chinaRegions',
|
resource: 'chinaRegions',
|
||||||
@ -38,7 +38,7 @@ export const useChinaRegionDataSource = (options) => {
|
|||||||
export const useChinaRegionLoadData = () => {
|
export const useChinaRegionLoadData = () => {
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const field = useField<ArrayField>();
|
const field = useField<ArrayField>();
|
||||||
const maxLevel = field.componentProps.maxLevel;
|
const maxLevel = field?.componentProps?.maxLevel;
|
||||||
return (selectedOptions) => {
|
return (selectedOptions) => {
|
||||||
const targetOption = selectedOptions[selectedOptions.length - 1];
|
const targetOption = selectedOptions[selectedOptions.length - 1];
|
||||||
if (targetOption?.children?.length > 0) {
|
if (targetOption?.children?.length > 0) {
|
||||||
|
@ -31,6 +31,7 @@ function useTargetCollectionField() {
|
|||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const providedCollection = useCollection_deprecated();
|
const providedCollection = useCollection_deprecated();
|
||||||
const { getCollection, getCollectionField } = useCollectionManager_deprecated();
|
const { getCollection, getCollectionField } = useCollectionManager_deprecated();
|
||||||
|
if (!fieldSchema) return;
|
||||||
const paths = (fieldSchema.name as string).split('.');
|
const paths = (fieldSchema.name as string).split('.');
|
||||||
let collection: any = providedCollection;
|
let collection: any = providedCollection;
|
||||||
for (let i = 0; i < paths.length - 1; i++) {
|
for (let i = 0; i < paths.length - 1; i++) {
|
||||||
@ -59,10 +60,10 @@ export function Result(props) {
|
|||||||
const { evaluate } = (evaluators as Registry<Evaluator>).get(engine);
|
const { evaluate } = (evaluators as Registry<Evaluator>).get(engine);
|
||||||
const formBlockContext = useFormBlockContext();
|
const formBlockContext = useFormBlockContext();
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const path: any = field.path.entire;
|
const path: any = field?.path?.entire;
|
||||||
const fieldPath = path?.replace(`.${fieldSchema.name}`, '');
|
const fieldPath = path?.replace(`.${fieldSchema.name}`, '');
|
||||||
const fieldName = fieldPath.split('.')[0];
|
const fieldName = fieldPath?.split('.')[0];
|
||||||
const index = parseInt(fieldPath.split('.')?.[1]);
|
const index = parseInt(fieldPath?.split('.')?.[1]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setEditingValue(value);
|
setEditingValue(value);
|
||||||
}, [value]);
|
}, [value]);
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { Plugin } from '@nocobase/client';
|
import { Plugin } from '@nocobase/client';
|
||||||
import { FormulaFieldProvider } from './FormulaFieldProvider';
|
import { FormulaFieldProvider } from './FormulaFieldProvider';
|
||||||
import { FormulaFieldInterface } from './interfaces/formula';
|
import { FormulaFieldInterface } from './interfaces/formula';
|
||||||
|
import { Formula } from './components';
|
||||||
|
|
||||||
export class FormulaFieldPlugin extends Plugin {
|
export class FormulaFieldPlugin extends Plugin {
|
||||||
async load() {
|
async load() {
|
||||||
this.app.use(FormulaFieldProvider);
|
this.app.use(FormulaFieldProvider);
|
||||||
|
this.app.addComponents({ Formula })
|
||||||
this.app.dataSourceManager.addFieldInterfaces([FormulaFieldInterface]);
|
this.app.dataSourceManager.addFieldInterfaces([FormulaFieldInterface]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ const InternalMap = connect((props: MapProps) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}, mapReadPretty(ReadPretty));
|
}, mapReadPretty(ReadPretty));
|
||||||
|
InternalMap.displayName = 'Map';
|
||||||
|
|
||||||
const Map = InternalMap as typeof InternalMap & {
|
const Map = InternalMap as typeof InternalMap & {
|
||||||
Designer: typeof Designer;
|
Designer: typeof Designer;
|
||||||
|
@ -21,6 +21,7 @@ MapProvider.displayName = 'MapProvider';
|
|||||||
export class MapPlugin extends Plugin {
|
export class MapPlugin extends Plugin {
|
||||||
async load() {
|
async load() {
|
||||||
this.app.use(MapProvider);
|
this.app.use(MapProvider);
|
||||||
|
this.app.addComponents({ Map: Map as any });
|
||||||
|
|
||||||
this.app.dataSourceManager.addFieldInterfaces(fields);
|
this.app.dataSourceManager.addFieldInterfaces(fields);
|
||||||
this.app.dataSourceManager.addFieldInterfaceGroups({
|
this.app.dataSourceManager.addFieldInterfaceGroups({
|
||||||
|
Loading…
Reference in New Issue
Block a user