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',
|
||||
type: 'group',
|
||||
children: [
|
||||
// {
|
||||
// title: '临时',
|
||||
// link: '/core/data-block/tmp',
|
||||
// },
|
||||
{
|
||||
title: 'Record',
|
||||
title: 'CollectionRecord',
|
||||
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 moment from 'moment';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { connect, mapProps } from '@formily/react';
|
||||
import { useBoolean } from 'ahooks';
|
||||
@ -7,7 +7,7 @@ import { Input, Radio, Space } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { useToken } from '../../';
|
||||
|
||||
const date = moment();
|
||||
const date = dayjs();
|
||||
|
||||
const spaceCSS = css`
|
||||
width: 100%;
|
||||
@ -16,7 +16,7 @@ const spaceCSS = css`
|
||||
}
|
||||
`;
|
||||
export const DateFormatCom = (props?) => {
|
||||
const date = moment();
|
||||
const date = dayjs();
|
||||
return (
|
||||
<div style={{ display: 'inline-flex' }}>
|
||||
<span>{props.format}</span>
|
||||
@ -57,7 +57,6 @@ const InternalExpiresRadio = (props) => {
|
||||
onChange(v.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Space className={spaceCSS}>
|
||||
<Radio.Group value={isCustom ? 'custom' : props.value} onChange={onSelectChange}>
|
||||
|
Loading…
Reference in New Issue
Block a user