fix: remove temp doc (#3603)
* fix: remove temp doc * refactor: moment => dayjs --------- Co-authored-by: katherinehhh <katherine_15995@163.com>
This commit is contained in:
parent
b545c6ccc3
commit
b1a49f0d06
@ -179,12 +179,8 @@ export default defineConfig({
|
|||||||
title: 'DataBlock',
|
title: 'DataBlock',
|
||||||
type: 'group',
|
type: 'group',
|
||||||
children: [
|
children: [
|
||||||
// {
|
|
||||||
// title: '临时',
|
|
||||||
// link: '/core/data-block/tmp',
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
title: 'Record',
|
title: 'CollectionRecord',
|
||||||
link: '/core/data-block/collection-record',
|
link: '/core/data-block/collection-record',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,128 +0,0 @@
|
|||||||
export const mainCollections = [
|
|
||||||
{
|
|
||||||
name: 'users',
|
|
||||||
title: 'Users',
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
key: 'id',
|
|
||||||
name: 'id',
|
|
||||||
interface: 'id',
|
|
||||||
primaryKey: true,
|
|
||||||
uiSchema: {
|
|
||||||
type: 'number',
|
|
||||||
title: 'ID',
|
|
||||||
'x-component': 'InputNumber',
|
|
||||||
'x-read-pretty': true,
|
|
||||||
rawTitle: 'ID',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
uiSchema: {
|
|
||||||
type: 'string',
|
|
||||||
title: 'Nickname',
|
|
||||||
'x-component': 'Input',
|
|
||||||
},
|
|
||||||
name: 'nickname',
|
|
||||||
type: 'string',
|
|
||||||
interface: 'input',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
uiSchema: {
|
|
||||||
type: 'string',
|
|
||||||
title: 'Email',
|
|
||||||
'x-component': 'Input',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
key: 'rrskwjl5wt1',
|
|
||||||
name: 'email',
|
|
||||||
type: 'string',
|
|
||||||
interface: 'email',
|
|
||||||
unique: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'favoriteColor',
|
|
||||||
type: 'string',
|
|
||||||
interface: 'color',
|
|
||||||
uiSchema: {
|
|
||||||
type: 'string',
|
|
||||||
'x-component': 'ColorPicker',
|
|
||||||
default: '#1677FF',
|
|
||||||
title: 'Favorite Color',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'roles',
|
|
||||||
title: 'Roles',
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
uiSchema: {
|
|
||||||
type: 'string',
|
|
||||||
title: 'Role UID',
|
|
||||||
'x-component': 'Input',
|
|
||||||
},
|
|
||||||
name: 'name',
|
|
||||||
type: 'uid',
|
|
||||||
interface: 'input',
|
|
||||||
primaryKey: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
uiSchema: {
|
|
||||||
type: 'string',
|
|
||||||
title: 'Role name',
|
|
||||||
'x-component': 'Input',
|
|
||||||
},
|
|
||||||
name: 'title',
|
|
||||||
type: 'string',
|
|
||||||
interface: 'input',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'description',
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const TestDBCollections = [
|
|
||||||
{
|
|
||||||
name: 'test1',
|
|
||||||
title: 'Test1',
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: 'id',
|
|
||||||
interface: 'id',
|
|
||||||
primaryKey: true,
|
|
||||||
uiSchema: {
|
|
||||||
type: 'number',
|
|
||||||
title: 'ID',
|
|
||||||
'x-component': 'InputNumber',
|
|
||||||
'x-read-pretty': true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'field1',
|
|
||||||
interface: 'input',
|
|
||||||
uiSchema: {
|
|
||||||
type: 'string',
|
|
||||||
'x-component': 'Input',
|
|
||||||
title: 'Field1',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'field2',
|
|
||||||
interface: 'number',
|
|
||||||
uiSchema: {
|
|
||||||
'x-component-props': {
|
|
||||||
step: '1',
|
|
||||||
stringMode: true,
|
|
||||||
},
|
|
||||||
'x-component': 'InputNumber',
|
|
||||||
title: 'Field2',
|
|
||||||
},
|
|
||||||
defaultValue: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
@ -1 +0,0 @@
|
|||||||
import { Application } from '@nocobase/client';
|
|
@ -1,41 +0,0 @@
|
|||||||
import { Table } from 'antd';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
const MyTable = () => {
|
|
||||||
const dataSource = [
|
|
||||||
{
|
|
||||||
key: '1',
|
|
||||||
name: 'Mike',
|
|
||||||
age: 32,
|
|
||||||
address: '10 Downing Street',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '2',
|
|
||||||
name: 'John',
|
|
||||||
age: 42,
|
|
||||||
address: '10 Downing Street',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: 'Name',
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Age',
|
|
||||||
dataIndex: 'age',
|
|
||||||
key: 'age',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Address',
|
|
||||||
dataIndex: 'address',
|
|
||||||
key: 'address',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return <Table dataSource={dataSource} columns={columns} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MyTable;
|
|
@ -1,412 +0,0 @@
|
|||||||
import { Button, Drawer, Space, Table, TableProps } from 'antd';
|
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
CollectionField,
|
|
||||||
ColorFieldInterface,
|
|
||||||
ColorPicker,
|
|
||||||
DataBlockProvider,
|
|
||||||
EmailFieldInterface,
|
|
||||||
FormItem,
|
|
||||||
IdFieldInterface,
|
|
||||||
Input,
|
|
||||||
InputFieldInterface,
|
|
||||||
InputNumber,
|
|
||||||
NumberFieldInterface,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
SchemaSettings,
|
|
||||||
SchemaToolbar,
|
|
||||||
useCollection,
|
|
||||||
useCompile,
|
|
||||||
useDataBlock,
|
|
||||||
useDataBlockProps,
|
|
||||||
useDataBlockRequest,
|
|
||||||
useDataSourceManager,
|
|
||||||
useSchemaInitializer,
|
|
||||||
useSchemaInitializerRender,
|
|
||||||
useSchemaToolbar,
|
|
||||||
useSchemaToolbarRender,
|
|
||||||
withDynamicSchemaProps,
|
|
||||||
} from '@nocobase/client';
|
|
||||||
import { Application } from '@nocobase/client';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { ISchema, RecursionField, Schema, observer, useField, useFieldSchema } from '@formily/react';
|
|
||||||
import { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
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,
|
|
||||||
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 { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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);
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
|
|
||||||
const { render } = useSchemaToolbarRender(fieldSchema);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
{render()}
|
|
||||||
<Button type="primary" onClick={showDrawer}>
|
|
||||||
Add New
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<Drawer title={`${title} | Add New`} onClose={onClose} open={open}>
|
|
||||||
<p>Some contents...</p>
|
|
||||||
</Drawer>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const createActionSettings = new SchemaSettings({
|
|
||||||
name: 'createActionSettings',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
type: 'remove',
|
|
||||||
name: 'remove',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
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',
|
|
||||||
'x-settings': 'createActionSettings',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
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: {
|
|
||||||
bordered: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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.schemaInitializerManager.add(myInitializer);
|
|
||||||
this.app.schemaSettingsManager.add(myTableSettings);
|
|
||||||
this.app.schemaSettingsManager.add(createActionSettings);
|
|
||||||
this.app.addScopes({ useTableProps });
|
|
||||||
this.app.schemaInitializerManager.add(tableActionInitializers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
InputNumber,
|
|
||||||
Input,
|
|
||||||
CollectionField,
|
|
||||||
ColorPicker,
|
|
||||||
FormItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fieldInterfaces: [
|
|
||||||
IdFieldInterface,
|
|
||||||
InputFieldInterface,
|
|
||||||
EmailFieldInterface,
|
|
||||||
ColorFieldInterface,
|
|
||||||
NumberFieldInterface,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,492 +0,0 @@
|
|||||||
import { Button, Drawer, Space, Table, TableProps } from 'antd';
|
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
CollectionField,
|
|
||||||
ColorFieldInterface,
|
|
||||||
ColorPicker,
|
|
||||||
DataBlockProvider,
|
|
||||||
EmailFieldInterface,
|
|
||||||
FormItem,
|
|
||||||
IdFieldInterface,
|
|
||||||
Input,
|
|
||||||
InputFieldInterface,
|
|
||||||
InputNumber,
|
|
||||||
NumberFieldInterface,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
SchemaInitializerSwitch,
|
|
||||||
SchemaSettings,
|
|
||||||
SchemaToolbar,
|
|
||||||
useCollection,
|
|
||||||
useCompile,
|
|
||||||
useCurrentSchema,
|
|
||||||
useDataBlock,
|
|
||||||
useDataBlockProps,
|
|
||||||
useDataBlockRequest,
|
|
||||||
useDataSourceManager,
|
|
||||||
useDesignable,
|
|
||||||
useSchemaInitializer,
|
|
||||||
useSchemaInitializerItem,
|
|
||||||
useSchemaInitializerRender,
|
|
||||||
useSchemaToolbarRender,
|
|
||||||
withDynamicSchemaProps,
|
|
||||||
} from '@nocobase/client';
|
|
||||||
import { Application } from '@nocobase/client';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { ISchema, RecursionField, observer, useField, useFieldSchema } from '@formily/react';
|
|
||||||
import { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
const MyTable = withDynamicSchemaProps(Table, { displayName: 'MyTable' });
|
|
||||||
|
|
||||||
const TableColumn = observer(() => {
|
|
||||||
const field = useField<any>();
|
|
||||||
return <div>{field.title}</div>;
|
|
||||||
});
|
|
||||||
|
|
||||||
function useTableColumns() {
|
|
||||||
const schema = useFieldSchema();
|
|
||||||
const filed = useField();
|
|
||||||
const { designable } = useDesignable();
|
|
||||||
const { render } = useSchemaInitializerRender(schema['x-initializer'], schema['x-initializer-props']);
|
|
||||||
const columns = schema.mapProperties((tableField: any, name) => {
|
|
||||||
return {
|
|
||||||
title: <RecursionField name={tableField.name} schema={tableField} onlyRenderSelf />,
|
|
||||||
dataIndex: name,
|
|
||||||
key: name,
|
|
||||||
width: 200,
|
|
||||||
render(value, record, index) {
|
|
||||||
return <RecursionField basePath={filed.address.concat(index)} onlyRenderProperties schema={tableField} />;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const tableColumns = useMemo(() => {
|
|
||||||
return [
|
|
||||||
...columns,
|
|
||||||
{
|
|
||||||
title: render(),
|
|
||||||
dataIndex: 'TABLE_COLUMN_INITIALIZER',
|
|
||||||
key: 'TABLE_COLUMN_INITIALIZER',
|
|
||||||
width: 200,
|
|
||||||
render: designable ? () => <div style={{ minWidth: 300 }} /> : null,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}, [columns, render, designable]);
|
|
||||||
|
|
||||||
return tableColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
function useTableProps(): TableProps<any> {
|
|
||||||
const { tableProps } = useDataBlockProps();
|
|
||||||
const { data, loading } = useDataBlockRequest<any[]>();
|
|
||||||
const dataSource = useMemo(() => data?.data || [], [data]);
|
|
||||||
const field = useField<any>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
field.value = dataSource;
|
|
||||||
}, [dataSource]);
|
|
||||||
|
|
||||||
const columns = useTableColumns();
|
|
||||||
|
|
||||||
return {
|
|
||||||
...tableProps,
|
|
||||||
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 { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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);
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
|
|
||||||
const { render } = useSchemaToolbarRender(fieldSchema);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
{render()}
|
|
||||||
<Button type="primary" onClick={showDrawer}>
|
|
||||||
Add New
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<Drawer title={`${title} | Add New`} onClose={onClose} open={open}>
|
|
||||||
<p>Some contents...</p>
|
|
||||||
</Drawer>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const createActionSettings = new SchemaSettings({
|
|
||||||
name: 'createActionSettings',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
type: 'remove',
|
|
||||||
name: 'remove',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
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',
|
|
||||||
'x-settings': 'createActionSettings',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
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: {
|
|
||||||
bordered: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
actions: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'ActionBar',
|
|
||||||
'x-initializer': 'tableActionInitializers',
|
|
||||||
},
|
|
||||||
[uid()]: {
|
|
||||||
type: 'array',
|
|
||||||
'x-component': 'MyTable',
|
|
||||||
'x-use-component-props': 'useTableProps',
|
|
||||||
'x-initializer': 'tableColumnInitializers',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
insert(tableSchema);
|
|
||||||
setVisible(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const menuItems = useCollectionMenuItems();
|
|
||||||
|
|
||||||
return <SchemaInitializerItem title={'Table'} items={menuItems} onClick={handleClick} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const useTableColumnInitializerFields = () => {
|
|
||||||
const collection = useCollection();
|
|
||||||
return collection.fields.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 {
|
|
||||||
type: 'item',
|
|
||||||
name: collectionField.name,
|
|
||||||
title: collectionField?.uiSchema?.title || collectionField.name,
|
|
||||||
Component: 'TableCollectionFieldInitializer',
|
|
||||||
schema: tableFieldSchema,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TableCollectionFieldInitializer = () => {
|
|
||||||
const itemConfig = useSchemaInitializerItem();
|
|
||||||
const { insert } = useSchemaInitializer();
|
|
||||||
const { remove } = useDesignable();
|
|
||||||
const schema = useFieldSchema();
|
|
||||||
const exists = !!schema.properties?.[itemConfig.name];
|
|
||||||
return (
|
|
||||||
<SchemaInitializerSwitch
|
|
||||||
checked={exists}
|
|
||||||
title={itemConfig.title}
|
|
||||||
onClick={() => {
|
|
||||||
if (exists) {
|
|
||||||
return remove(schema.properties?.[itemConfig.name]);
|
|
||||||
}
|
|
||||||
insert(itemConfig.schema);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const tableColumnInitializers = new SchemaInitializer({
|
|
||||||
name: 'tableColumnInitializers',
|
|
||||||
insertPosition: 'beforeEnd',
|
|
||||||
icon: 'SettingOutlined',
|
|
||||||
title: 'Configure columns',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: 'displayFields',
|
|
||||||
type: 'itemGroup',
|
|
||||||
title: 'Display fields',
|
|
||||||
useChildren: useTableColumnInitializerFields,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
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,
|
|
||||||
TableCollectionFieldInitializer,
|
|
||||||
});
|
|
||||||
this.app.schemaInitializerManager.add(myInitializer);
|
|
||||||
this.app.schemaSettingsManager.add(myTableSettings);
|
|
||||||
this.app.addScopes({ useTableProps });
|
|
||||||
this.app.schemaInitializerManager.add(tableActionInitializers);
|
|
||||||
this.app.schemaInitializerManager.add(tableColumnInitializers);
|
|
||||||
this.app.schemaSettingsManager.add(createActionSettings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
InputNumber,
|
|
||||||
Input,
|
|
||||||
CollectionField,
|
|
||||||
ColorPicker,
|
|
||||||
FormItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fieldInterfaces: [
|
|
||||||
IdFieldInterface,
|
|
||||||
InputFieldInterface,
|
|
||||||
EmailFieldInterface,
|
|
||||||
ColorFieldInterface,
|
|
||||||
NumberFieldInterface,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,532 +0,0 @@
|
|||||||
import { Button, Drawer, Space, Table, TableProps } from 'antd';
|
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
CollectionField,
|
|
||||||
ColorFieldInterface,
|
|
||||||
ColorPicker,
|
|
||||||
DataBlockProvider,
|
|
||||||
EmailFieldInterface,
|
|
||||||
FormItem,
|
|
||||||
IdFieldInterface,
|
|
||||||
Input,
|
|
||||||
InputFieldInterface,
|
|
||||||
InputNumber,
|
|
||||||
NumberFieldInterface,
|
|
||||||
Plugin,
|
|
||||||
CollectionRecordProvider,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
SchemaInitializerSwitch,
|
|
||||||
SchemaSettings,
|
|
||||||
SchemaToolbar,
|
|
||||||
useCollection,
|
|
||||||
useCompile,
|
|
||||||
useCurrentSchema,
|
|
||||||
useDataBlock,
|
|
||||||
useDataBlockProps,
|
|
||||||
useDataBlockRequest,
|
|
||||||
useDataSource,
|
|
||||||
useDataSourceManager,
|
|
||||||
useDesignable,
|
|
||||||
useCollectionRecord,
|
|
||||||
useSchemaInitializer,
|
|
||||||
useSchemaInitializerItem,
|
|
||||||
useSchemaInitializerRender,
|
|
||||||
useSchemaToolbarRender,
|
|
||||||
withDynamicSchemaProps,
|
|
||||||
} from '@nocobase/client';
|
|
||||||
import { Application } from '@nocobase/client';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { ISchema, RecursionField, observer, useField, useFieldSchema } from '@formily/react';
|
|
||||||
import { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
const MyTable = withDynamicSchemaProps(Table, { displayName: 'MyTable' });
|
|
||||||
|
|
||||||
const TableColumn = observer(() => {
|
|
||||||
const field = useField<any>();
|
|
||||||
return <div>{field.title}</div>;
|
|
||||||
});
|
|
||||||
|
|
||||||
const ColumnView = observer(
|
|
||||||
() => {
|
|
||||||
const record = useCollectionRecord();
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
|
|
||||||
const showDrawer = () => {
|
|
||||||
setOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onClose = () => {
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Button type="link" onClick={showDrawer}>
|
|
||||||
view
|
|
||||||
</Button>
|
|
||||||
<Drawer onClose={onClose} open={open}>
|
|
||||||
<pre>{JSON.stringify(record, null, 2)}</pre>
|
|
||||||
</Drawer>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
{ displayName: 'ColumnView' },
|
|
||||||
);
|
|
||||||
|
|
||||||
function useTableColumns() {
|
|
||||||
const schema = useFieldSchema();
|
|
||||||
const filed = useField();
|
|
||||||
const { designable } = useDesignable();
|
|
||||||
const { render } = useSchemaInitializerRender(schema['x-initializer'], schema['x-initializer-props']);
|
|
||||||
const columns = schema.mapProperties((tableField: any, name) => {
|
|
||||||
return {
|
|
||||||
title: <RecursionField name={tableField.name} schema={tableField} onlyRenderSelf />,
|
|
||||||
dataIndex: name,
|
|
||||||
key: name,
|
|
||||||
render(value, record, index) {
|
|
||||||
return <RecursionField basePath={filed.address.concat(index)} onlyRenderProperties schema={tableField} />;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
const tableColumns = useMemo(() => {
|
|
||||||
return [
|
|
||||||
...columns,
|
|
||||||
{
|
|
||||||
title: 'Actions',
|
|
||||||
dataIndex: 'actions',
|
|
||||||
key: 'actions',
|
|
||||||
render: (value, record) => {
|
|
||||||
return (
|
|
||||||
<CollectionRecordProvider record={record}>
|
|
||||||
<Space>
|
|
||||||
<ColumnView />
|
|
||||||
</Space>
|
|
||||||
</CollectionRecordProvider>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: render(),
|
|
||||||
dataIndex: 'TABLE_COLUMN_INITIALIZER',
|
|
||||||
key: 'TABLE_COLUMN_INITIALIZER',
|
|
||||||
render: designable ? () => <div style={{ minWidth: 300 }} /> : null,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}, [columns, render, designable]);
|
|
||||||
|
|
||||||
return tableColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
function useTableProps(): TableProps<any> {
|
|
||||||
const { tableProps } = useDataBlockProps();
|
|
||||||
const { data, loading } = useDataBlockRequest<any[]>();
|
|
||||||
const dataSource = useMemo(() => data?.data || [], [data]);
|
|
||||||
const field = useField<any>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
field.value = dataSource;
|
|
||||||
}, [dataSource]);
|
|
||||||
|
|
||||||
const columns = useTableColumns();
|
|
||||||
|
|
||||||
return {
|
|
||||||
...tableProps,
|
|
||||||
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 { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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);
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
|
|
||||||
const { render } = useSchemaToolbarRender(fieldSchema);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
{render()}
|
|
||||||
<Button type="primary" onClick={showDrawer}>
|
|
||||||
Add New
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<Drawer title={`${title} | Add New`} onClose={onClose} open={open}>
|
|
||||||
<p>Some contents...</p>
|
|
||||||
</Drawer>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const createActionSettings = new SchemaSettings({
|
|
||||||
name: 'createActionSettings',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
type: 'remove',
|
|
||||||
name: 'remove',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
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',
|
|
||||||
'x-settings': 'createActionSettings',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
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: {
|
|
||||||
bordered: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
actions: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'ActionBar',
|
|
||||||
'x-initializer': 'tableActionInitializers',
|
|
||||||
},
|
|
||||||
[uid()]: {
|
|
||||||
type: 'array',
|
|
||||||
'x-component': 'MyTable',
|
|
||||||
'x-use-component-props': 'useTableProps',
|
|
||||||
'x-initializer': 'tableColumnInitializers',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
insert(tableSchema);
|
|
||||||
setVisible(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const menuItems = useCollectionMenuItems();
|
|
||||||
|
|
||||||
return <SchemaInitializerItem title={'Table'} items={menuItems} onClick={handleClick} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const useTableColumnInitializerFields = () => {
|
|
||||||
const collection = useCollection();
|
|
||||||
return collection.fields.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 {
|
|
||||||
type: 'item',
|
|
||||||
name: collectionField.name,
|
|
||||||
title: collectionField?.uiSchema?.title || collectionField.name,
|
|
||||||
Component: 'TableCollectionFieldInitializer',
|
|
||||||
schema: tableFieldSchema,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TableCollectionFieldInitializer = () => {
|
|
||||||
const itemConfig = useSchemaInitializerItem();
|
|
||||||
const { insert } = useSchemaInitializer();
|
|
||||||
const { remove } = useDesignable();
|
|
||||||
const schema = useFieldSchema();
|
|
||||||
const exists = !!schema.properties?.[itemConfig.name];
|
|
||||||
return (
|
|
||||||
<SchemaInitializerSwitch
|
|
||||||
checked={exists}
|
|
||||||
title={itemConfig.title}
|
|
||||||
onClick={() => {
|
|
||||||
if (exists) {
|
|
||||||
return remove(schema.properties?.[itemConfig.name]);
|
|
||||||
}
|
|
||||||
insert(itemConfig.schema);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const tableColumnInitializers = new SchemaInitializer({
|
|
||||||
name: 'tableColumnInitializers',
|
|
||||||
insertPosition: 'beforeEnd',
|
|
||||||
icon: 'SettingOutlined',
|
|
||||||
title: 'Configure columns',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: 'displayFields',
|
|
||||||
type: 'itemGroup',
|
|
||||||
title: 'Display fields',
|
|
||||||
useChildren: useTableColumnInitializerFields,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
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,
|
|
||||||
TableCollectionFieldInitializer,
|
|
||||||
});
|
|
||||||
this.app.schemaInitializerManager.add(myInitializer);
|
|
||||||
this.app.schemaSettingsManager.add(myTableSettings);
|
|
||||||
this.app.addScopes({ useTableProps });
|
|
||||||
this.app.schemaInitializerManager.add(tableActionInitializers);
|
|
||||||
this.app.schemaInitializerManager.add(tableColumnInitializers);
|
|
||||||
this.app.schemaSettingsManager.add(createActionSettings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
InputNumber,
|
|
||||||
Input,
|
|
||||||
CollectionField,
|
|
||||||
ColorPicker,
|
|
||||||
FormItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fieldInterfaces: [
|
|
||||||
IdFieldInterface,
|
|
||||||
InputFieldInterface,
|
|
||||||
EmailFieldInterface,
|
|
||||||
ColorFieldInterface,
|
|
||||||
NumberFieldInterface,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,82 +0,0 @@
|
|||||||
import { Table } from 'antd';
|
|
||||||
import React from 'react';
|
|
||||||
import { CardItem, Plugin, SchemaComponent } from '@nocobase/client';
|
|
||||||
import { Application } from '@nocobase/client';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
|
|
||||||
const MyTable = () => {
|
|
||||||
const dataSource = [
|
|
||||||
{
|
|
||||||
key: '1',
|
|
||||||
name: 'Mike',
|
|
||||||
age: 32,
|
|
||||||
address: '10 Downing Street',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '2',
|
|
||||||
name: 'John',
|
|
||||||
age: 42,
|
|
||||||
address: '10 Downing Street',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: 'Name',
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Age',
|
|
||||||
dataIndex: 'age',
|
|
||||||
key: 'age',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Address',
|
|
||||||
dataIndex: 'address',
|
|
||||||
key: 'address',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return <Table dataSource={dataSource} columns={columns} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MyPlugin extends Plugin {
|
|
||||||
async load() {
|
|
||||||
this.app.addComponents({ MyTable });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const tableSchema = {
|
|
||||||
name: 'demo',
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'CardItem',
|
|
||||||
properties: {
|
|
||||||
[uid()]: {
|
|
||||||
type: 'array',
|
|
||||||
'x-component': 'MyTable',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={tableSchema} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
CardItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('root', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,147 +0,0 @@
|
|||||||
import { Table } from 'antd';
|
|
||||||
import React, { useCallback } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
useSchemaInitializer,
|
|
||||||
useSchemaInitializerRender,
|
|
||||||
} from '@nocobase/client';
|
|
||||||
import { Application } from '@nocobase/client';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { ISchema, observer, useFieldSchema } from '@formily/react';
|
|
||||||
|
|
||||||
const MyTable = () => {
|
|
||||||
const dataSource = [
|
|
||||||
{
|
|
||||||
key: '1',
|
|
||||||
name: 'Mike',
|
|
||||||
age: 32,
|
|
||||||
address: '10 Downing Street',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '2',
|
|
||||||
name: 'John',
|
|
||||||
age: 42,
|
|
||||||
address: '10 Downing Street',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: 'Name',
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Age',
|
|
||||||
dataIndex: 'age',
|
|
||||||
key: 'age',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Address',
|
|
||||||
dataIndex: 'address',
|
|
||||||
key: 'address',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return <Table dataSource={dataSource} columns={columns} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const AddBlockButton = observer(
|
|
||||||
() => {
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
return render();
|
|
||||||
},
|
|
||||||
{ displayName: 'AddBlockButton' },
|
|
||||||
);
|
|
||||||
|
|
||||||
const Page = observer(
|
|
||||||
(props) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{props.children}
|
|
||||||
<AddBlockButton />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
{ displayName: 'Page' },
|
|
||||||
);
|
|
||||||
|
|
||||||
const TableDataBlockInitializer = () => {
|
|
||||||
const { insert, setVisible } = useSchemaInitializer();
|
|
||||||
|
|
||||||
const handleClick = useCallback(
|
|
||||||
({ item }) => {
|
|
||||||
const tableSchema = {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'CardItem',
|
|
||||||
properties: {
|
|
||||||
[uid()]: {
|
|
||||||
type: 'array',
|
|
||||||
'x-component': 'MyTable',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
insert(tableSchema);
|
|
||||||
setVisible(false);
|
|
||||||
},
|
|
||||||
[insert, setVisible],
|
|
||||||
);
|
|
||||||
|
|
||||||
return <SchemaInitializerItem title={'Table'} onClick={handleClick} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
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 });
|
|
||||||
this.app.schemaInitializerManager.add(myInitializer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,180 +0,0 @@
|
|||||||
import { Table } from 'antd';
|
|
||||||
import React, { useMemo } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
useCollection,
|
|
||||||
useDataBlockRequest,
|
|
||||||
useDataSourceManager,
|
|
||||||
useSchemaInitializer,
|
|
||||||
useSchemaInitializerRender,
|
|
||||||
} from '@nocobase/client';
|
|
||||||
import { Application } from '@nocobase/client';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { ISchema, observer, useFieldSchema } from '@formily/react';
|
|
||||||
import { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
const MyTable = () => {
|
|
||||||
const { data, loading } = useDataBlockRequest<any[]>();
|
|
||||||
const dataSource = useMemo(() => data?.data || [], [data]);
|
|
||||||
const collection = useCollection();
|
|
||||||
const columns = useMemo(() => {
|
|
||||||
return collection.getFields().map((collectionField) => {
|
|
||||||
return {
|
|
||||||
title: collectionField.uiSchema?.title || collectionField.name,
|
|
||||||
dataIndex: collectionField.name,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, [collection]);
|
|
||||||
|
|
||||||
return <Table loading={loading} rowKey="id" dataSource={dataSource} columns={columns} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const AddBlockButton = observer(
|
|
||||||
() => {
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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 TableDataBlockInitializer = () => {
|
|
||||||
const { insert, setVisible } = useSchemaInitializer();
|
|
||||||
|
|
||||||
const handleClick = ({ item }) => {
|
|
||||||
const tableSchema = {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'CardItem',
|
|
||||||
'x-decorator': 'DataBlockProvider',
|
|
||||||
'x-decorator-props': {
|
|
||||||
collection: item.collection,
|
|
||||||
dataSource: item.dataSource,
|
|
||||||
action: 'list',
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
[uid()]: {
|
|
||||||
type: 'array',
|
|
||||||
'x-component': 'MyTable',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
insert(tableSchema);
|
|
||||||
setVisible(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const menuItems = useCollectionMenuItems();
|
|
||||||
|
|
||||||
return <SchemaInitializerItem title={'Table'} items={menuItems} onClick={handleClick} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
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 });
|
|
||||||
this.app.schemaInitializerManager.add(myInitializer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,236 +0,0 @@
|
|||||||
import { Table } from 'antd';
|
|
||||||
import React, { useEffect, useMemo } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
CollectionField,
|
|
||||||
ColorFieldInterface,
|
|
||||||
ColorPicker,
|
|
||||||
DataBlockProvider,
|
|
||||||
EmailFieldInterface,
|
|
||||||
FormItem,
|
|
||||||
IdFieldInterface,
|
|
||||||
Input,
|
|
||||||
InputFieldInterface,
|
|
||||||
InputNumber,
|
|
||||||
NumberFieldInterface,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
useCollection,
|
|
||||||
useDataBlockRequest,
|
|
||||||
useDataSourceManager,
|
|
||||||
useSchemaInitializer,
|
|
||||||
useSchemaInitializerRender,
|
|
||||||
} from '@nocobase/client';
|
|
||||||
import { Application } from '@nocobase/client';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { ISchema, RecursionField, observer, useField, useFieldSchema } from '@formily/react';
|
|
||||||
import { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
const TableColumn = observer(() => {
|
|
||||||
const field = useField<any>();
|
|
||||||
return <div>{field.title}</div>;
|
|
||||||
});
|
|
||||||
|
|
||||||
const MyTable = () => {
|
|
||||||
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 <Table loading={loading} rowKey={'id'} dataSource={dataSource} columns={columns} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const AddBlockButton = observer(
|
|
||||||
() => {
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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 TableDataBlockInitializer = () => {
|
|
||||||
const { insert, setVisible } = useSchemaInitializer();
|
|
||||||
|
|
||||||
const handleClick = ({ item }) => {
|
|
||||||
const tableSchema = {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'CardItem',
|
|
||||||
'x-decorator': 'DataBlockProvider',
|
|
||||||
'x-decorator-props': {
|
|
||||||
collection: item.collection,
|
|
||||||
dataSource: item.dataSource,
|
|
||||||
action: 'list',
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
[uid()]: {
|
|
||||||
type: 'array',
|
|
||||||
'x-component': 'MyTable',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
insert(tableSchema);
|
|
||||||
setVisible(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const menuItems = useCollectionMenuItems();
|
|
||||||
|
|
||||||
return <SchemaInitializerItem title={'Table'} items={menuItems} onClick={handleClick} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
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 });
|
|
||||||
this.app.schemaInitializerManager.add(myInitializer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
InputNumber,
|
|
||||||
Input,
|
|
||||||
CollectionField,
|
|
||||||
ColorPicker,
|
|
||||||
FormItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fieldInterfaces: [
|
|
||||||
IdFieldInterface,
|
|
||||||
InputFieldInterface,
|
|
||||||
EmailFieldInterface,
|
|
||||||
ColorFieldInterface,
|
|
||||||
NumberFieldInterface,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,280 +0,0 @@
|
|||||||
import { Table, TableProps } from 'antd';
|
|
||||||
import React, { useEffect, useMemo } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
CollectionField,
|
|
||||||
ColorFieldInterface,
|
|
||||||
ColorPicker,
|
|
||||||
DataBlockProvider,
|
|
||||||
EmailFieldInterface,
|
|
||||||
FormItem,
|
|
||||||
IdFieldInterface,
|
|
||||||
Input,
|
|
||||||
InputFieldInterface,
|
|
||||||
InputNumber,
|
|
||||||
NumberFieldInterface,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
SchemaSettings,
|
|
||||||
useCollection,
|
|
||||||
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 { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
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,
|
|
||||||
loading,
|
|
||||||
dataSource,
|
|
||||||
columns,
|
|
||||||
rowKey: 'id',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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 { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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 TableDataBlockInitializer = () => {
|
|
||||||
const { insert, setVisible } = useSchemaInitializer();
|
|
||||||
|
|
||||||
const handleClick = ({ item }) => {
|
|
||||||
const tableSchema = {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'CardItem',
|
|
||||||
'x-settings': 'myTableSettings',
|
|
||||||
'x-decorator': 'DataBlockProvider',
|
|
||||||
'x-decorator-props': {
|
|
||||||
collection: item.collection,
|
|
||||||
dataSource: item.dataSource,
|
|
||||||
action: 'list',
|
|
||||||
tableProps: {
|
|
||||||
bordered: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
[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 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 });
|
|
||||||
this.app.schemaInitializerManager.add(myInitializer);
|
|
||||||
this.app.schemaSettingsManager.add(myTableSettings);
|
|
||||||
this.app.addScopes({ useTableProps });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
InputNumber,
|
|
||||||
Input,
|
|
||||||
CollectionField,
|
|
||||||
ColorPicker,
|
|
||||||
FormItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fieldInterfaces: [
|
|
||||||
IdFieldInterface,
|
|
||||||
InputFieldInterface,
|
|
||||||
EmailFieldInterface,
|
|
||||||
ColorFieldInterface,
|
|
||||||
NumberFieldInterface,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,291 +0,0 @@
|
|||||||
import { Table, TableProps } from 'antd';
|
|
||||||
import React, { useEffect, useMemo } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
CollectionField,
|
|
||||||
ColorFieldInterface,
|
|
||||||
ColorPicker,
|
|
||||||
DataBlockProvider,
|
|
||||||
EmailFieldInterface,
|
|
||||||
FormItem,
|
|
||||||
IdFieldInterface,
|
|
||||||
Input,
|
|
||||||
InputFieldInterface,
|
|
||||||
InputNumber,
|
|
||||||
NumberFieldInterface,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
SchemaSettings,
|
|
||||||
SchemaToolbar,
|
|
||||||
useCollection,
|
|
||||||
useCompile,
|
|
||||||
useDataBlock,
|
|
||||||
useDataBlockProps,
|
|
||||||
useDataBlockRequest,
|
|
||||||
useDataSource,
|
|
||||||
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 { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
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,
|
|
||||||
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 { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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 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: {
|
|
||||||
bordered: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
[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 });
|
|
||||||
this.app.schemaInitializerManager.add(myInitializer);
|
|
||||||
this.app.schemaSettingsManager.add(myTableSettings);
|
|
||||||
this.app.addScopes({ useTableProps });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
InputNumber,
|
|
||||||
Input,
|
|
||||||
CollectionField,
|
|
||||||
ColorPicker,
|
|
||||||
FormItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fieldInterfaces: [
|
|
||||||
IdFieldInterface,
|
|
||||||
InputFieldInterface,
|
|
||||||
EmailFieldInterface,
|
|
||||||
ColorFieldInterface,
|
|
||||||
NumberFieldInterface,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,351 +0,0 @@
|
|||||||
import { Button, Drawer, Space, Table, TableProps } from 'antd';
|
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
CollectionField,
|
|
||||||
ColorFieldInterface,
|
|
||||||
ColorPicker,
|
|
||||||
DataBlockProvider,
|
|
||||||
EmailFieldInterface,
|
|
||||||
FormItem,
|
|
||||||
IdFieldInterface,
|
|
||||||
Input,
|
|
||||||
InputFieldInterface,
|
|
||||||
InputNumber,
|
|
||||||
NumberFieldInterface,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
SchemaSettings,
|
|
||||||
SchemaToolbar,
|
|
||||||
useCollection,
|
|
||||||
useCompile,
|
|
||||||
useDataBlock,
|
|
||||||
useDataBlockProps,
|
|
||||||
useDataBlockRequest,
|
|
||||||
useDataSource,
|
|
||||||
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 { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
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,
|
|
||||||
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 { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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 = () => {
|
|
||||||
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>
|
|
||||||
<CreateAction></CreateAction>
|
|
||||||
<RefreshAction></RefreshAction>
|
|
||||||
{render()}
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
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: {
|
|
||||||
bordered: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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 });
|
|
||||||
this.app.schemaInitializerManager.add(myInitializer);
|
|
||||||
this.app.schemaSettingsManager.add(myTableSettings);
|
|
||||||
this.app.addScopes({ useTableProps });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
InputNumber,
|
|
||||||
Input,
|
|
||||||
CollectionField,
|
|
||||||
ColorPicker,
|
|
||||||
FormItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fieldInterfaces: [
|
|
||||||
IdFieldInterface,
|
|
||||||
InputFieldInterface,
|
|
||||||
EmailFieldInterface,
|
|
||||||
ColorFieldInterface,
|
|
||||||
NumberFieldInterface,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,394 +0,0 @@
|
|||||||
import { Button, Drawer, Space, Table, TableProps } from 'antd';
|
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
CollectionField,
|
|
||||||
ColorFieldInterface,
|
|
||||||
ColorPicker,
|
|
||||||
DataBlockProvider,
|
|
||||||
EmailFieldInterface,
|
|
||||||
FormItem,
|
|
||||||
IdFieldInterface,
|
|
||||||
Input,
|
|
||||||
InputFieldInterface,
|
|
||||||
InputNumber,
|
|
||||||
NumberFieldInterface,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
SchemaSettings,
|
|
||||||
SchemaToolbar,
|
|
||||||
useCollection,
|
|
||||||
useCompile,
|
|
||||||
useDataBlock,
|
|
||||||
useDataBlockProps,
|
|
||||||
useDataBlockRequest,
|
|
||||||
useDataSource,
|
|
||||||
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 { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
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,
|
|
||||||
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 { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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: {
|
|
||||||
bordered: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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.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: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
InputNumber,
|
|
||||||
Input,
|
|
||||||
CollectionField,
|
|
||||||
ColorPicker,
|
|
||||||
FormItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fieldInterfaces: [
|
|
||||||
IdFieldInterface,
|
|
||||||
InputFieldInterface,
|
|
||||||
EmailFieldInterface,
|
|
||||||
ColorFieldInterface,
|
|
||||||
NumberFieldInterface,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,79 +0,0 @@
|
|||||||
import type { Application } from '@nocobase/client';
|
|
||||||
import MockAdapter from 'axios-mock-adapter';
|
|
||||||
|
|
||||||
const mockData = {
|
|
||||||
'users:list': {
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
nickname: 'Jack Ma',
|
|
||||||
email: 'test@gmail.com',
|
|
||||||
favoriteColor: '#1677FF',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
email: 'jim@gmail.com',
|
|
||||||
nickname: 'Jim Green',
|
|
||||||
favoriteColor: '#36C',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
nickname: 'Tom Cat',
|
|
||||||
email: 'tom@gmail.com',
|
|
||||||
favoriteColor: '#F0F',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
'users:destroy': {
|
|
||||||
result: 'ok',
|
|
||||||
},
|
|
||||||
'roles:list': {
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
name: 'root',
|
|
||||||
title: 'Root',
|
|
||||||
description: 'Root',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'admin',
|
|
||||||
title: 'Admin',
|
|
||||||
description: 'Admin description',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
'test1:list': {
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
field1: 'aaa',
|
|
||||||
field2: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
field1: 'bbb',
|
|
||||||
field2: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
field1: 'ccc',
|
|
||||||
field2: 3,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export function mock(app: Application) {
|
|
||||||
const mock = new MockAdapter(app.apiClient.axios);
|
|
||||||
|
|
||||||
Object.entries(mockData).forEach(([url, data]) => {
|
|
||||||
mock.onGet(url).reply(async (config) => {
|
|
||||||
return [200, data];
|
|
||||||
});
|
|
||||||
mock.onPost(url).reply(async (config) => {
|
|
||||||
return [200, data];
|
|
||||||
});
|
|
||||||
mock.onDelete(url).reply(async (config) => {
|
|
||||||
return [200, data];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,128 +0,0 @@
|
|||||||
export const mainCollections = [
|
|
||||||
{
|
|
||||||
name: 'users',
|
|
||||||
title: 'Users',
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
key: 'id',
|
|
||||||
name: 'id',
|
|
||||||
interface: 'id',
|
|
||||||
primaryKey: true,
|
|
||||||
uiSchema: {
|
|
||||||
type: 'number',
|
|
||||||
title: 'ID',
|
|
||||||
'x-component': 'InputNumber',
|
|
||||||
'x-read-pretty': true,
|
|
||||||
rawTitle: 'ID',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
uiSchema: {
|
|
||||||
type: 'string',
|
|
||||||
title: 'Nickname',
|
|
||||||
'x-component': 'Input',
|
|
||||||
},
|
|
||||||
name: 'nickname',
|
|
||||||
type: 'string',
|
|
||||||
interface: 'input',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
uiSchema: {
|
|
||||||
type: 'string',
|
|
||||||
title: 'Email',
|
|
||||||
'x-component': 'Input',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
key: 'rrskwjl5wt1',
|
|
||||||
name: 'email',
|
|
||||||
type: 'string',
|
|
||||||
interface: 'email',
|
|
||||||
unique: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'favoriteColor',
|
|
||||||
type: 'string',
|
|
||||||
interface: 'color',
|
|
||||||
uiSchema: {
|
|
||||||
type: 'string',
|
|
||||||
'x-component': 'ColorPicker',
|
|
||||||
default: '#1677FF',
|
|
||||||
title: 'Favorite Color',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'roles',
|
|
||||||
title: 'Roles',
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
uiSchema: {
|
|
||||||
type: 'string',
|
|
||||||
title: 'Role UID',
|
|
||||||
'x-component': 'Input',
|
|
||||||
},
|
|
||||||
name: 'name',
|
|
||||||
type: 'uid',
|
|
||||||
interface: 'input',
|
|
||||||
primaryKey: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
uiSchema: {
|
|
||||||
type: 'string',
|
|
||||||
title: 'Role name',
|
|
||||||
'x-component': 'Input',
|
|
||||||
},
|
|
||||||
name: 'title',
|
|
||||||
type: 'string',
|
|
||||||
interface: 'input',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'description',
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const TestDBCollections = [
|
|
||||||
{
|
|
||||||
name: 'test1',
|
|
||||||
title: 'Test1',
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: 'id',
|
|
||||||
interface: 'id',
|
|
||||||
primaryKey: true,
|
|
||||||
uiSchema: {
|
|
||||||
type: 'number',
|
|
||||||
title: 'ID',
|
|
||||||
'x-component': 'InputNumber',
|
|
||||||
'x-read-pretty': true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'field1',
|
|
||||||
interface: 'input',
|
|
||||||
uiSchema: {
|
|
||||||
type: 'string',
|
|
||||||
'x-component': 'Input',
|
|
||||||
title: 'Field1',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'field2',
|
|
||||||
interface: 'number',
|
|
||||||
uiSchema: {
|
|
||||||
'x-component-props': {
|
|
||||||
step: '1',
|
|
||||||
stringMode: true,
|
|
||||||
},
|
|
||||||
'x-component': 'InputNumber',
|
|
||||||
title: 'Field2',
|
|
||||||
},
|
|
||||||
defaultValue: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
@ -1 +0,0 @@
|
|||||||
import { Application } from '@nocobase/client';
|
|
@ -1,41 +0,0 @@
|
|||||||
import { Table } from 'antd';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
const MyTable = () => {
|
|
||||||
const dataSource = [
|
|
||||||
{
|
|
||||||
key: '1',
|
|
||||||
name: 'Mike',
|
|
||||||
age: 32,
|
|
||||||
address: '10 Downing Street',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '2',
|
|
||||||
name: 'John',
|
|
||||||
age: 42,
|
|
||||||
address: '10 Downing Street',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: 'Name',
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Age',
|
|
||||||
dataIndex: 'age',
|
|
||||||
key: 'age',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Address',
|
|
||||||
dataIndex: 'address',
|
|
||||||
key: 'address',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return <Table dataSource={dataSource} columns={columns} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MyTable;
|
|
@ -1,412 +0,0 @@
|
|||||||
import { Button, Drawer, Space, Table, TableProps } from 'antd';
|
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
CollectionField,
|
|
||||||
ColorFieldInterface,
|
|
||||||
ColorPicker,
|
|
||||||
DataBlockProvider,
|
|
||||||
EmailFieldInterface,
|
|
||||||
FormItem,
|
|
||||||
IdFieldInterface,
|
|
||||||
Input,
|
|
||||||
InputFieldInterface,
|
|
||||||
InputNumber,
|
|
||||||
NumberFieldInterface,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
SchemaSettings,
|
|
||||||
SchemaToolbar,
|
|
||||||
useCollection,
|
|
||||||
useCompile,
|
|
||||||
useDataBlock,
|
|
||||||
useDataBlockProps,
|
|
||||||
useDataBlockRequest,
|
|
||||||
useDataSourceManager,
|
|
||||||
useSchemaInitializer,
|
|
||||||
useSchemaInitializerRender,
|
|
||||||
useSchemaToolbar,
|
|
||||||
useSchemaToolbarRender,
|
|
||||||
withDynamicSchemaProps,
|
|
||||||
} from '@nocobase/client';
|
|
||||||
import { Application } from '@nocobase/client';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { ISchema, RecursionField, Schema, observer, useField, useFieldSchema } from '@formily/react';
|
|
||||||
import { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
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,
|
|
||||||
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 { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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);
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
|
|
||||||
const { render } = useSchemaToolbarRender(fieldSchema);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
{render()}
|
|
||||||
<Button type="primary" onClick={showDrawer}>
|
|
||||||
Add New
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<Drawer title={`${title} | Add New`} onClose={onClose} open={open}>
|
|
||||||
<p>Some contents...</p>
|
|
||||||
</Drawer>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const createActionSettings = new SchemaSettings({
|
|
||||||
name: 'createActionSettings',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
type: 'remove',
|
|
||||||
name: 'remove',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
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',
|
|
||||||
'x-settings': 'createActionSettings',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
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: {
|
|
||||||
bordered: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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.schemaInitializerManager.add(myInitializer);
|
|
||||||
this.app.schemaSettingsManager.add(myTableSettings);
|
|
||||||
this.app.schemaSettingsManager.add(createActionSettings);
|
|
||||||
this.app.addScopes({ useTableProps });
|
|
||||||
this.app.schemaInitializerManager.add(tableActionInitializers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
InputNumber,
|
|
||||||
Input,
|
|
||||||
CollectionField,
|
|
||||||
ColorPicker,
|
|
||||||
FormItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fieldInterfaces: [
|
|
||||||
IdFieldInterface,
|
|
||||||
InputFieldInterface,
|
|
||||||
EmailFieldInterface,
|
|
||||||
ColorFieldInterface,
|
|
||||||
NumberFieldInterface,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,492 +0,0 @@
|
|||||||
import { Button, Drawer, Space, Table, TableProps } from 'antd';
|
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
CollectionField,
|
|
||||||
ColorFieldInterface,
|
|
||||||
ColorPicker,
|
|
||||||
DataBlockProvider,
|
|
||||||
EmailFieldInterface,
|
|
||||||
FormItem,
|
|
||||||
IdFieldInterface,
|
|
||||||
Input,
|
|
||||||
InputFieldInterface,
|
|
||||||
InputNumber,
|
|
||||||
NumberFieldInterface,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
SchemaInitializerSwitch,
|
|
||||||
SchemaSettings,
|
|
||||||
SchemaToolbar,
|
|
||||||
useCollection,
|
|
||||||
useCompile,
|
|
||||||
useCurrentSchema,
|
|
||||||
useDataBlock,
|
|
||||||
useDataBlockProps,
|
|
||||||
useDataBlockRequest,
|
|
||||||
useDataSourceManager,
|
|
||||||
useDesignable,
|
|
||||||
useSchemaInitializer,
|
|
||||||
useSchemaInitializerItem,
|
|
||||||
useSchemaInitializerRender,
|
|
||||||
useSchemaToolbarRender,
|
|
||||||
withDynamicSchemaProps,
|
|
||||||
} from '@nocobase/client';
|
|
||||||
import { Application } from '@nocobase/client';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { ISchema, RecursionField, observer, useField, useFieldSchema } from '@formily/react';
|
|
||||||
import { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
const MyTable = withDynamicSchemaProps(Table, { displayName: 'MyTable' });
|
|
||||||
|
|
||||||
const TableColumn = observer(() => {
|
|
||||||
const field = useField<any>();
|
|
||||||
return <div>{field.title}</div>;
|
|
||||||
});
|
|
||||||
|
|
||||||
function useTableColumns() {
|
|
||||||
const schema = useFieldSchema();
|
|
||||||
const filed = useField();
|
|
||||||
const { designable } = useDesignable();
|
|
||||||
const { render } = useSchemaInitializerRender(schema['x-initializer'], schema['x-initializer-props']);
|
|
||||||
const columns = schema.mapProperties((tableField: any, name) => {
|
|
||||||
return {
|
|
||||||
title: <RecursionField name={tableField.name} schema={tableField} onlyRenderSelf />,
|
|
||||||
dataIndex: name,
|
|
||||||
key: name,
|
|
||||||
width: 200,
|
|
||||||
render(value, record, index) {
|
|
||||||
return <RecursionField basePath={filed.address.concat(index)} onlyRenderProperties schema={tableField} />;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const tableColumns = useMemo(() => {
|
|
||||||
return [
|
|
||||||
...columns,
|
|
||||||
{
|
|
||||||
title: render(),
|
|
||||||
dataIndex: 'TABLE_COLUMN_INITIALIZER',
|
|
||||||
key: 'TABLE_COLUMN_INITIALIZER',
|
|
||||||
width: 200,
|
|
||||||
render: designable ? () => <div style={{ minWidth: 300 }} /> : null,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}, [columns, render, designable]);
|
|
||||||
|
|
||||||
return tableColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
function useTableProps(): TableProps<any> {
|
|
||||||
const { tableProps } = useDataBlockProps();
|
|
||||||
const { data, loading } = useDataBlockRequest<any[]>();
|
|
||||||
const dataSource = useMemo(() => data?.data || [], [data]);
|
|
||||||
const field = useField<any>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
field.value = dataSource;
|
|
||||||
}, [dataSource]);
|
|
||||||
|
|
||||||
const columns = useTableColumns();
|
|
||||||
|
|
||||||
return {
|
|
||||||
...tableProps,
|
|
||||||
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 { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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);
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
|
|
||||||
const { render } = useSchemaToolbarRender(fieldSchema);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
{render()}
|
|
||||||
<Button type="primary" onClick={showDrawer}>
|
|
||||||
Add New
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<Drawer title={`${title} | Add New`} onClose={onClose} open={open}>
|
|
||||||
<p>Some contents...</p>
|
|
||||||
</Drawer>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const createActionSettings = new SchemaSettings({
|
|
||||||
name: 'createActionSettings',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
type: 'remove',
|
|
||||||
name: 'remove',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
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',
|
|
||||||
'x-settings': 'createActionSettings',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
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: {
|
|
||||||
bordered: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
actions: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'ActionBar',
|
|
||||||
'x-initializer': 'tableActionInitializers',
|
|
||||||
},
|
|
||||||
[uid()]: {
|
|
||||||
type: 'array',
|
|
||||||
'x-component': 'MyTable',
|
|
||||||
'x-use-component-props': 'useTableProps',
|
|
||||||
'x-initializer': 'tableColumnInitializers',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
insert(tableSchema);
|
|
||||||
setVisible(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const menuItems = useCollectionMenuItems();
|
|
||||||
|
|
||||||
return <SchemaInitializerItem title={'Table'} items={menuItems} onClick={handleClick} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const useTableColumnInitializerFields = () => {
|
|
||||||
const collection = useCollection();
|
|
||||||
return collection.fields.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 {
|
|
||||||
type: 'item',
|
|
||||||
name: collectionField.name,
|
|
||||||
title: collectionField?.uiSchema?.title || collectionField.name,
|
|
||||||
Component: 'TableCollectionFieldInitializer',
|
|
||||||
schema: tableFieldSchema,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TableCollectionFieldInitializer = () => {
|
|
||||||
const itemConfig = useSchemaInitializerItem();
|
|
||||||
const { insert } = useSchemaInitializer();
|
|
||||||
const { remove } = useDesignable();
|
|
||||||
const schema = useFieldSchema();
|
|
||||||
const exists = !!schema.properties?.[itemConfig.name];
|
|
||||||
return (
|
|
||||||
<SchemaInitializerSwitch
|
|
||||||
checked={exists}
|
|
||||||
title={itemConfig.title}
|
|
||||||
onClick={() => {
|
|
||||||
if (exists) {
|
|
||||||
return remove(schema.properties?.[itemConfig.name]);
|
|
||||||
}
|
|
||||||
insert(itemConfig.schema);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const tableColumnInitializers = new SchemaInitializer({
|
|
||||||
name: 'tableColumnInitializers',
|
|
||||||
insertPosition: 'beforeEnd',
|
|
||||||
icon: 'SettingOutlined',
|
|
||||||
title: 'Configure columns',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: 'displayFields',
|
|
||||||
type: 'itemGroup',
|
|
||||||
title: 'Display fields',
|
|
||||||
useChildren: useTableColumnInitializerFields,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
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,
|
|
||||||
TableCollectionFieldInitializer,
|
|
||||||
});
|
|
||||||
this.app.schemaInitializerManager.add(myInitializer);
|
|
||||||
this.app.schemaSettingsManager.add(myTableSettings);
|
|
||||||
this.app.addScopes({ useTableProps });
|
|
||||||
this.app.schemaInitializerManager.add(tableActionInitializers);
|
|
||||||
this.app.schemaInitializerManager.add(tableColumnInitializers);
|
|
||||||
this.app.schemaSettingsManager.add(createActionSettings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
InputNumber,
|
|
||||||
Input,
|
|
||||||
CollectionField,
|
|
||||||
ColorPicker,
|
|
||||||
FormItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fieldInterfaces: [
|
|
||||||
IdFieldInterface,
|
|
||||||
InputFieldInterface,
|
|
||||||
EmailFieldInterface,
|
|
||||||
ColorFieldInterface,
|
|
||||||
NumberFieldInterface,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,532 +0,0 @@
|
|||||||
import { Button, Drawer, Space, Table, TableProps } from 'antd';
|
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
CollectionField,
|
|
||||||
ColorFieldInterface,
|
|
||||||
ColorPicker,
|
|
||||||
DataBlockProvider,
|
|
||||||
EmailFieldInterface,
|
|
||||||
FormItem,
|
|
||||||
IdFieldInterface,
|
|
||||||
Input,
|
|
||||||
InputFieldInterface,
|
|
||||||
InputNumber,
|
|
||||||
NumberFieldInterface,
|
|
||||||
Plugin,
|
|
||||||
CollectionRecordProvider,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
SchemaInitializerSwitch,
|
|
||||||
SchemaSettings,
|
|
||||||
SchemaToolbar,
|
|
||||||
useCollection,
|
|
||||||
useCompile,
|
|
||||||
useCurrentSchema,
|
|
||||||
useDataBlock,
|
|
||||||
useDataBlockProps,
|
|
||||||
useDataBlockRequest,
|
|
||||||
useDataSource,
|
|
||||||
useDataSourceManager,
|
|
||||||
useDesignable,
|
|
||||||
useCollectionRecord,
|
|
||||||
useSchemaInitializer,
|
|
||||||
useSchemaInitializerItem,
|
|
||||||
useSchemaInitializerRender,
|
|
||||||
useSchemaToolbarRender,
|
|
||||||
withDynamicSchemaProps,
|
|
||||||
} from '@nocobase/client';
|
|
||||||
import { Application } from '@nocobase/client';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { ISchema, RecursionField, observer, useField, useFieldSchema } from '@formily/react';
|
|
||||||
import { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
const MyTable = withDynamicSchemaProps(Table, { displayName: 'MyTable' });
|
|
||||||
|
|
||||||
const TableColumn = observer(() => {
|
|
||||||
const field = useField<any>();
|
|
||||||
return <div>{field.title}</div>;
|
|
||||||
});
|
|
||||||
|
|
||||||
const ColumnView = observer(
|
|
||||||
() => {
|
|
||||||
const record = useCollectionRecord();
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
|
|
||||||
const showDrawer = () => {
|
|
||||||
setOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onClose = () => {
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Button type="link" onClick={showDrawer}>
|
|
||||||
view
|
|
||||||
</Button>
|
|
||||||
<Drawer onClose={onClose} open={open}>
|
|
||||||
<pre>{JSON.stringify(record, null, 2)}</pre>
|
|
||||||
</Drawer>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
{ displayName: 'ColumnView' },
|
|
||||||
);
|
|
||||||
|
|
||||||
function useTableColumns() {
|
|
||||||
const schema = useFieldSchema();
|
|
||||||
const filed = useField();
|
|
||||||
const { designable } = useDesignable();
|
|
||||||
const { render } = useSchemaInitializerRender(schema['x-initializer'], schema['x-initializer-props']);
|
|
||||||
const columns = schema.mapProperties((tableField: any, name) => {
|
|
||||||
return {
|
|
||||||
title: <RecursionField name={tableField.name} schema={tableField} onlyRenderSelf />,
|
|
||||||
dataIndex: name,
|
|
||||||
key: name,
|
|
||||||
render(value, record, index) {
|
|
||||||
return <RecursionField basePath={filed.address.concat(index)} onlyRenderProperties schema={tableField} />;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
const tableColumns = useMemo(() => {
|
|
||||||
return [
|
|
||||||
...columns,
|
|
||||||
{
|
|
||||||
title: 'Actions',
|
|
||||||
dataIndex: 'actions',
|
|
||||||
key: 'actions',
|
|
||||||
render: (value, record) => {
|
|
||||||
return (
|
|
||||||
<CollectionRecordProvider record={record}>
|
|
||||||
<Space>
|
|
||||||
<ColumnView />
|
|
||||||
</Space>
|
|
||||||
</CollectionRecordProvider>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: render(),
|
|
||||||
dataIndex: 'TABLE_COLUMN_INITIALIZER',
|
|
||||||
key: 'TABLE_COLUMN_INITIALIZER',
|
|
||||||
render: designable ? () => <div style={{ minWidth: 300 }} /> : null,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}, [columns, render, designable]);
|
|
||||||
|
|
||||||
return tableColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
function useTableProps(): TableProps<any> {
|
|
||||||
const { tableProps } = useDataBlockProps();
|
|
||||||
const { data, loading } = useDataBlockRequest<any[]>();
|
|
||||||
const dataSource = useMemo(() => data?.data || [], [data]);
|
|
||||||
const field = useField<any>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
field.value = dataSource;
|
|
||||||
}, [dataSource]);
|
|
||||||
|
|
||||||
const columns = useTableColumns();
|
|
||||||
|
|
||||||
return {
|
|
||||||
...tableProps,
|
|
||||||
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 { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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);
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
|
|
||||||
const { render } = useSchemaToolbarRender(fieldSchema);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
{render()}
|
|
||||||
<Button type="primary" onClick={showDrawer}>
|
|
||||||
Add New
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<Drawer title={`${title} | Add New`} onClose={onClose} open={open}>
|
|
||||||
<p>Some contents...</p>
|
|
||||||
</Drawer>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const createActionSettings = new SchemaSettings({
|
|
||||||
name: 'createActionSettings',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
type: 'remove',
|
|
||||||
name: 'remove',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
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',
|
|
||||||
'x-settings': 'createActionSettings',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
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: {
|
|
||||||
bordered: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
actions: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'ActionBar',
|
|
||||||
'x-initializer': 'tableActionInitializers',
|
|
||||||
},
|
|
||||||
[uid()]: {
|
|
||||||
type: 'array',
|
|
||||||
'x-component': 'MyTable',
|
|
||||||
'x-use-component-props': 'useTableProps',
|
|
||||||
'x-initializer': 'tableColumnInitializers',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
insert(tableSchema);
|
|
||||||
setVisible(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const menuItems = useCollectionMenuItems();
|
|
||||||
|
|
||||||
return <SchemaInitializerItem title={'Table'} items={menuItems} onClick={handleClick} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const useTableColumnInitializerFields = () => {
|
|
||||||
const collection = useCollection();
|
|
||||||
return collection.fields.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 {
|
|
||||||
type: 'item',
|
|
||||||
name: collectionField.name,
|
|
||||||
title: collectionField?.uiSchema?.title || collectionField.name,
|
|
||||||
Component: 'TableCollectionFieldInitializer',
|
|
||||||
schema: tableFieldSchema,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TableCollectionFieldInitializer = () => {
|
|
||||||
const itemConfig = useSchemaInitializerItem();
|
|
||||||
const { insert } = useSchemaInitializer();
|
|
||||||
const { remove } = useDesignable();
|
|
||||||
const schema = useFieldSchema();
|
|
||||||
const exists = !!schema.properties?.[itemConfig.name];
|
|
||||||
return (
|
|
||||||
<SchemaInitializerSwitch
|
|
||||||
checked={exists}
|
|
||||||
title={itemConfig.title}
|
|
||||||
onClick={() => {
|
|
||||||
if (exists) {
|
|
||||||
return remove(schema.properties?.[itemConfig.name]);
|
|
||||||
}
|
|
||||||
insert(itemConfig.schema);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const tableColumnInitializers = new SchemaInitializer({
|
|
||||||
name: 'tableColumnInitializers',
|
|
||||||
insertPosition: 'beforeEnd',
|
|
||||||
icon: 'SettingOutlined',
|
|
||||||
title: 'Configure columns',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: 'displayFields',
|
|
||||||
type: 'itemGroup',
|
|
||||||
title: 'Display fields',
|
|
||||||
useChildren: useTableColumnInitializerFields,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
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,
|
|
||||||
TableCollectionFieldInitializer,
|
|
||||||
});
|
|
||||||
this.app.schemaInitializerManager.add(myInitializer);
|
|
||||||
this.app.schemaSettingsManager.add(myTableSettings);
|
|
||||||
this.app.addScopes({ useTableProps });
|
|
||||||
this.app.schemaInitializerManager.add(tableActionInitializers);
|
|
||||||
this.app.schemaInitializerManager.add(tableColumnInitializers);
|
|
||||||
this.app.schemaSettingsManager.add(createActionSettings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
InputNumber,
|
|
||||||
Input,
|
|
||||||
CollectionField,
|
|
||||||
ColorPicker,
|
|
||||||
FormItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fieldInterfaces: [
|
|
||||||
IdFieldInterface,
|
|
||||||
InputFieldInterface,
|
|
||||||
EmailFieldInterface,
|
|
||||||
ColorFieldInterface,
|
|
||||||
NumberFieldInterface,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,82 +0,0 @@
|
|||||||
import { Table } from 'antd';
|
|
||||||
import React from 'react';
|
|
||||||
import { CardItem, Plugin, SchemaComponent } from '@nocobase/client';
|
|
||||||
import { Application } from '@nocobase/client';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
|
|
||||||
const MyTable = () => {
|
|
||||||
const dataSource = [
|
|
||||||
{
|
|
||||||
key: '1',
|
|
||||||
name: 'Mike',
|
|
||||||
age: 32,
|
|
||||||
address: '10 Downing Street',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '2',
|
|
||||||
name: 'John',
|
|
||||||
age: 42,
|
|
||||||
address: '10 Downing Street',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: 'Name',
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Age',
|
|
||||||
dataIndex: 'age',
|
|
||||||
key: 'age',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Address',
|
|
||||||
dataIndex: 'address',
|
|
||||||
key: 'address',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return <Table dataSource={dataSource} columns={columns} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MyPlugin extends Plugin {
|
|
||||||
async load() {
|
|
||||||
this.app.addComponents({ MyTable });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const tableSchema = {
|
|
||||||
name: 'demo',
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'CardItem',
|
|
||||||
properties: {
|
|
||||||
[uid()]: {
|
|
||||||
type: 'array',
|
|
||||||
'x-component': 'MyTable',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={tableSchema} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
CardItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('root', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,147 +0,0 @@
|
|||||||
import { Table } from 'antd';
|
|
||||||
import React, { useCallback } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
useSchemaInitializer,
|
|
||||||
useSchemaInitializerRender,
|
|
||||||
} from '@nocobase/client';
|
|
||||||
import { Application } from '@nocobase/client';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { ISchema, observer, useFieldSchema } from '@formily/react';
|
|
||||||
|
|
||||||
const MyTable = () => {
|
|
||||||
const dataSource = [
|
|
||||||
{
|
|
||||||
key: '1',
|
|
||||||
name: 'Mike',
|
|
||||||
age: 32,
|
|
||||||
address: '10 Downing Street',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '2',
|
|
||||||
name: 'John',
|
|
||||||
age: 42,
|
|
||||||
address: '10 Downing Street',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: 'Name',
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Age',
|
|
||||||
dataIndex: 'age',
|
|
||||||
key: 'age',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Address',
|
|
||||||
dataIndex: 'address',
|
|
||||||
key: 'address',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return <Table dataSource={dataSource} columns={columns} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const AddBlockButton = observer(
|
|
||||||
() => {
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
return render();
|
|
||||||
},
|
|
||||||
{ displayName: 'AddBlockButton' },
|
|
||||||
);
|
|
||||||
|
|
||||||
const Page = observer(
|
|
||||||
(props) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{props.children}
|
|
||||||
<AddBlockButton />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
{ displayName: 'Page' },
|
|
||||||
);
|
|
||||||
|
|
||||||
const TableDataBlockInitializer = () => {
|
|
||||||
const { insert, setVisible } = useSchemaInitializer();
|
|
||||||
|
|
||||||
const handleClick = useCallback(
|
|
||||||
({ item }) => {
|
|
||||||
const tableSchema = {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'CardItem',
|
|
||||||
properties: {
|
|
||||||
[uid()]: {
|
|
||||||
type: 'array',
|
|
||||||
'x-component': 'MyTable',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
insert(tableSchema);
|
|
||||||
setVisible(false);
|
|
||||||
},
|
|
||||||
[insert, setVisible],
|
|
||||||
);
|
|
||||||
|
|
||||||
return <SchemaInitializerItem title={'Table'} onClick={handleClick} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
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 });
|
|
||||||
this.app.schemaInitializerManager.add(myInitializer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,180 +0,0 @@
|
|||||||
import { Table } from 'antd';
|
|
||||||
import React, { useMemo } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
useCollection,
|
|
||||||
useDataBlockRequest,
|
|
||||||
useDataSourceManager,
|
|
||||||
useSchemaInitializer,
|
|
||||||
useSchemaInitializerRender,
|
|
||||||
} from '@nocobase/client';
|
|
||||||
import { Application } from '@nocobase/client';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { ISchema, observer, useFieldSchema } from '@formily/react';
|
|
||||||
import { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
const MyTable = () => {
|
|
||||||
const { data, loading } = useDataBlockRequest<any[]>();
|
|
||||||
const dataSource = useMemo(() => data?.data || [], [data]);
|
|
||||||
const collection = useCollection();
|
|
||||||
const columns = useMemo(() => {
|
|
||||||
return collection.getFields().map((collectionField) => {
|
|
||||||
return {
|
|
||||||
title: collectionField.uiSchema?.title || collectionField.name,
|
|
||||||
dataIndex: collectionField.name,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, [collection]);
|
|
||||||
|
|
||||||
return <Table loading={loading} rowKey="id" dataSource={dataSource} columns={columns} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const AddBlockButton = observer(
|
|
||||||
() => {
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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 TableDataBlockInitializer = () => {
|
|
||||||
const { insert, setVisible } = useSchemaInitializer();
|
|
||||||
|
|
||||||
const handleClick = ({ item }) => {
|
|
||||||
const tableSchema = {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'CardItem',
|
|
||||||
'x-decorator': 'DataBlockProvider',
|
|
||||||
'x-decorator-props': {
|
|
||||||
collection: item.collection,
|
|
||||||
dataSource: item.dataSource,
|
|
||||||
action: 'list',
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
[uid()]: {
|
|
||||||
type: 'array',
|
|
||||||
'x-component': 'MyTable',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
insert(tableSchema);
|
|
||||||
setVisible(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const menuItems = useCollectionMenuItems();
|
|
||||||
|
|
||||||
return <SchemaInitializerItem title={'Table'} items={menuItems} onClick={handleClick} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
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 });
|
|
||||||
this.app.schemaInitializerManager.add(myInitializer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,236 +0,0 @@
|
|||||||
import { Table } from 'antd';
|
|
||||||
import React, { useEffect, useMemo } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
CollectionField,
|
|
||||||
ColorFieldInterface,
|
|
||||||
ColorPicker,
|
|
||||||
DataBlockProvider,
|
|
||||||
EmailFieldInterface,
|
|
||||||
FormItem,
|
|
||||||
IdFieldInterface,
|
|
||||||
Input,
|
|
||||||
InputFieldInterface,
|
|
||||||
InputNumber,
|
|
||||||
NumberFieldInterface,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
useCollection,
|
|
||||||
useDataBlockRequest,
|
|
||||||
useDataSourceManager,
|
|
||||||
useSchemaInitializer,
|
|
||||||
useSchemaInitializerRender,
|
|
||||||
} from '@nocobase/client';
|
|
||||||
import { Application } from '@nocobase/client';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { ISchema, RecursionField, observer, useField, useFieldSchema } from '@formily/react';
|
|
||||||
import { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
const TableColumn = observer(() => {
|
|
||||||
const field = useField<any>();
|
|
||||||
return <div>{field.title}</div>;
|
|
||||||
});
|
|
||||||
|
|
||||||
const MyTable = () => {
|
|
||||||
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 <Table loading={loading} rowKey={'id'} dataSource={dataSource} columns={columns} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const AddBlockButton = observer(
|
|
||||||
() => {
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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 TableDataBlockInitializer = () => {
|
|
||||||
const { insert, setVisible } = useSchemaInitializer();
|
|
||||||
|
|
||||||
const handleClick = ({ item }) => {
|
|
||||||
const tableSchema = {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'CardItem',
|
|
||||||
'x-decorator': 'DataBlockProvider',
|
|
||||||
'x-decorator-props': {
|
|
||||||
collection: item.collection,
|
|
||||||
dataSource: item.dataSource,
|
|
||||||
action: 'list',
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
[uid()]: {
|
|
||||||
type: 'array',
|
|
||||||
'x-component': 'MyTable',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
insert(tableSchema);
|
|
||||||
setVisible(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const menuItems = useCollectionMenuItems();
|
|
||||||
|
|
||||||
return <SchemaInitializerItem title={'Table'} items={menuItems} onClick={handleClick} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
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 });
|
|
||||||
this.app.schemaInitializerManager.add(myInitializer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
InputNumber,
|
|
||||||
Input,
|
|
||||||
CollectionField,
|
|
||||||
ColorPicker,
|
|
||||||
FormItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fieldInterfaces: [
|
|
||||||
IdFieldInterface,
|
|
||||||
InputFieldInterface,
|
|
||||||
EmailFieldInterface,
|
|
||||||
ColorFieldInterface,
|
|
||||||
NumberFieldInterface,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,280 +0,0 @@
|
|||||||
import { Table, TableProps } from 'antd';
|
|
||||||
import React, { useEffect, useMemo } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
CollectionField,
|
|
||||||
ColorFieldInterface,
|
|
||||||
ColorPicker,
|
|
||||||
DataBlockProvider,
|
|
||||||
EmailFieldInterface,
|
|
||||||
FormItem,
|
|
||||||
IdFieldInterface,
|
|
||||||
Input,
|
|
||||||
InputFieldInterface,
|
|
||||||
InputNumber,
|
|
||||||
NumberFieldInterface,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
SchemaSettings,
|
|
||||||
useCollection,
|
|
||||||
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 { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
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,
|
|
||||||
loading,
|
|
||||||
dataSource,
|
|
||||||
columns,
|
|
||||||
rowKey: 'id',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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 { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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 TableDataBlockInitializer = () => {
|
|
||||||
const { insert, setVisible } = useSchemaInitializer();
|
|
||||||
|
|
||||||
const handleClick = ({ item }) => {
|
|
||||||
const tableSchema = {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'CardItem',
|
|
||||||
'x-settings': 'myTableSettings',
|
|
||||||
'x-decorator': 'DataBlockProvider',
|
|
||||||
'x-decorator-props': {
|
|
||||||
collection: item.collection,
|
|
||||||
dataSource: item.dataSource,
|
|
||||||
action: 'list',
|
|
||||||
tableProps: {
|
|
||||||
bordered: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
[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 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 });
|
|
||||||
this.app.schemaInitializerManager.add(myInitializer);
|
|
||||||
this.app.schemaSettingsManager.add(myTableSettings);
|
|
||||||
this.app.addScopes({ useTableProps });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
InputNumber,
|
|
||||||
Input,
|
|
||||||
CollectionField,
|
|
||||||
ColorPicker,
|
|
||||||
FormItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fieldInterfaces: [
|
|
||||||
IdFieldInterface,
|
|
||||||
InputFieldInterface,
|
|
||||||
EmailFieldInterface,
|
|
||||||
ColorFieldInterface,
|
|
||||||
NumberFieldInterface,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,291 +0,0 @@
|
|||||||
import { Table, TableProps } from 'antd';
|
|
||||||
import React, { useEffect, useMemo } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
CollectionField,
|
|
||||||
ColorFieldInterface,
|
|
||||||
ColorPicker,
|
|
||||||
DataBlockProvider,
|
|
||||||
EmailFieldInterface,
|
|
||||||
FormItem,
|
|
||||||
IdFieldInterface,
|
|
||||||
Input,
|
|
||||||
InputFieldInterface,
|
|
||||||
InputNumber,
|
|
||||||
NumberFieldInterface,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
SchemaSettings,
|
|
||||||
SchemaToolbar,
|
|
||||||
useCollection,
|
|
||||||
useCompile,
|
|
||||||
useDataBlock,
|
|
||||||
useDataBlockProps,
|
|
||||||
useDataBlockRequest,
|
|
||||||
useDataSource,
|
|
||||||
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 { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
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,
|
|
||||||
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 { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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 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: {
|
|
||||||
bordered: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
[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 });
|
|
||||||
this.app.schemaInitializerManager.add(myInitializer);
|
|
||||||
this.app.schemaSettingsManager.add(myTableSettings);
|
|
||||||
this.app.addScopes({ useTableProps });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
InputNumber,
|
|
||||||
Input,
|
|
||||||
CollectionField,
|
|
||||||
ColorPicker,
|
|
||||||
FormItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fieldInterfaces: [
|
|
||||||
IdFieldInterface,
|
|
||||||
InputFieldInterface,
|
|
||||||
EmailFieldInterface,
|
|
||||||
ColorFieldInterface,
|
|
||||||
NumberFieldInterface,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,351 +0,0 @@
|
|||||||
import { Button, Drawer, Space, Table, TableProps } from 'antd';
|
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
CollectionField,
|
|
||||||
ColorFieldInterface,
|
|
||||||
ColorPicker,
|
|
||||||
DataBlockProvider,
|
|
||||||
EmailFieldInterface,
|
|
||||||
FormItem,
|
|
||||||
IdFieldInterface,
|
|
||||||
Input,
|
|
||||||
InputFieldInterface,
|
|
||||||
InputNumber,
|
|
||||||
NumberFieldInterface,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
SchemaSettings,
|
|
||||||
SchemaToolbar,
|
|
||||||
useCollection,
|
|
||||||
useCompile,
|
|
||||||
useDataBlock,
|
|
||||||
useDataBlockProps,
|
|
||||||
useDataBlockRequest,
|
|
||||||
useDataSource,
|
|
||||||
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 { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
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,
|
|
||||||
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 { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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 = () => {
|
|
||||||
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>
|
|
||||||
<CreateAction></CreateAction>
|
|
||||||
<RefreshAction></RefreshAction>
|
|
||||||
{render()}
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
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: {
|
|
||||||
bordered: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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 });
|
|
||||||
this.app.schemaInitializerManager.add(myInitializer);
|
|
||||||
this.app.schemaSettingsManager.add(myTableSettings);
|
|
||||||
this.app.addScopes({ useTableProps });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = () => {
|
|
||||||
return <SchemaComponent schema={rootSchema}></SchemaComponent>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = new Application({
|
|
||||||
plugins: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
InputNumber,
|
|
||||||
Input,
|
|
||||||
CollectionField,
|
|
||||||
ColorPicker,
|
|
||||||
FormItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fieldInterfaces: [
|
|
||||||
IdFieldInterface,
|
|
||||||
InputFieldInterface,
|
|
||||||
EmailFieldInterface,
|
|
||||||
ColorFieldInterface,
|
|
||||||
NumberFieldInterface,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,394 +0,0 @@
|
|||||||
import { Button, Drawer, Space, Table, TableProps } from 'antd';
|
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
import {
|
|
||||||
CardItem,
|
|
||||||
CollectionField,
|
|
||||||
ColorFieldInterface,
|
|
||||||
ColorPicker,
|
|
||||||
DataBlockProvider,
|
|
||||||
EmailFieldInterface,
|
|
||||||
FormItem,
|
|
||||||
IdFieldInterface,
|
|
||||||
Input,
|
|
||||||
InputFieldInterface,
|
|
||||||
InputNumber,
|
|
||||||
NumberFieldInterface,
|
|
||||||
Plugin,
|
|
||||||
SchemaComponent,
|
|
||||||
SchemaInitializer,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
SchemaSettings,
|
|
||||||
SchemaToolbar,
|
|
||||||
useCollection,
|
|
||||||
useCompile,
|
|
||||||
useDataBlock,
|
|
||||||
useDataBlockProps,
|
|
||||||
useDataBlockRequest,
|
|
||||||
useDataSource,
|
|
||||||
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 { mainCollections, TestDBCollections } from './collections';
|
|
||||||
import { mock } from './mockData';
|
|
||||||
|
|
||||||
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,
|
|
||||||
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 { render } = useSchemaInitializerRender(fieldSchema['x-initializer']);
|
|
||||||
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: {
|
|
||||||
bordered: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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.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: [MyPlugin],
|
|
||||||
components: {
|
|
||||||
Page,
|
|
||||||
AddBlockButton,
|
|
||||||
CardItem,
|
|
||||||
DataBlockProvider,
|
|
||||||
InputNumber,
|
|
||||||
Input,
|
|
||||||
CollectionField,
|
|
||||||
ColorPicker,
|
|
||||||
FormItem,
|
|
||||||
},
|
|
||||||
router: {
|
|
||||||
type: 'memory',
|
|
||||||
initialEntries: ['/'],
|
|
||||||
},
|
|
||||||
designable: true,
|
|
||||||
dataSourceManager: {
|
|
||||||
collections: mainCollections,
|
|
||||||
dataSources: [
|
|
||||||
{
|
|
||||||
key: 'test-db',
|
|
||||||
displayName: 'TestDB',
|
|
||||||
collections: TestDBCollections,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fieldInterfaces: [
|
|
||||||
IdFieldInterface,
|
|
||||||
InputFieldInterface,
|
|
||||||
EmailFieldInterface,
|
|
||||||
ColorFieldInterface,
|
|
||||||
NumberFieldInterface,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
apiClient: {
|
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.router.add('home', {
|
|
||||||
path: '/',
|
|
||||||
Component: Root,
|
|
||||||
});
|
|
||||||
|
|
||||||
mock(app);
|
|
||||||
|
|
||||||
export default app.getRootComponent();
|
|
@ -1,79 +0,0 @@
|
|||||||
import type { Application } from '@nocobase/client';
|
|
||||||
import MockAdapter from 'axios-mock-adapter';
|
|
||||||
|
|
||||||
const mockData = {
|
|
||||||
'users:list': {
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
nickname: 'Jack Ma',
|
|
||||||
email: 'test@gmail.com',
|
|
||||||
favoriteColor: '#1677FF',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
email: 'jim@gmail.com',
|
|
||||||
nickname: 'Jim Green',
|
|
||||||
favoriteColor: '#36C',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
nickname: 'Tom Cat',
|
|
||||||
email: 'tom@gmail.com',
|
|
||||||
favoriteColor: '#F0F',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
'users:destroy': {
|
|
||||||
result: 'ok',
|
|
||||||
},
|
|
||||||
'roles:list': {
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
name: 'root',
|
|
||||||
title: 'Root',
|
|
||||||
description: 'Root',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'admin',
|
|
||||||
title: 'Admin',
|
|
||||||
description: 'Admin description',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
'test1:list': {
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
field1: 'aaa',
|
|
||||||
field2: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
field1: 'bbb',
|
|
||||||
field2: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
field1: 'ccc',
|
|
||||||
field2: 3,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export function mock(app: Application) {
|
|
||||||
const mock = new MockAdapter(app.apiClient.axios);
|
|
||||||
|
|
||||||
Object.entries(mockData).forEach(([url, data]) => {
|
|
||||||
mock.onGet(url).reply(async (config) => {
|
|
||||||
return [200, data];
|
|
||||||
});
|
|
||||||
mock.onPost(url).reply(async (config) => {
|
|
||||||
return [200, data];
|
|
||||||
});
|
|
||||||
mock.onDelete(url).reply(async (config) => {
|
|
||||||
return [200, data];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import moment from 'moment';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
import { connect, mapProps } from '@formily/react';
|
import { connect, mapProps } from '@formily/react';
|
||||||
import { useBoolean } from 'ahooks';
|
import { useBoolean } from 'ahooks';
|
||||||
@ -7,7 +7,7 @@ import { Input, Radio, Space } from 'antd';
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useToken } from '../../';
|
import { useToken } from '../../';
|
||||||
|
|
||||||
const date = moment();
|
const date = dayjs();
|
||||||
|
|
||||||
const spaceCSS = css`
|
const spaceCSS = css`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -16,7 +16,7 @@ const spaceCSS = css`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const DateFormatCom = (props?) => {
|
export const DateFormatCom = (props?) => {
|
||||||
const date = moment();
|
const date = dayjs();
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'inline-flex' }}>
|
<div style={{ display: 'inline-flex' }}>
|
||||||
<span>{props.format}</span>
|
<span>{props.format}</span>
|
||||||
@ -57,7 +57,6 @@ const InternalExpiresRadio = (props) => {
|
|||||||
onChange(v.target.value);
|
onChange(v.target.value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space className={spaceCSS}>
|
<Space className={spaceCSS}>
|
||||||
<Radio.Group value={isCustom ? 'custom' : props.value} onChange={onSelectChange}>
|
<Radio.Group value={isCustom ? 'custom' : props.value} onChange={onSelectChange}>
|
||||||
|
Loading…
Reference in New Issue
Block a user