mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 23:46:02 +00:00
fix: data-block docs and bug
This commit is contained in:
parent
591b9d6dc5
commit
44ad995d81
@ -1,486 +0,0 @@
|
||||
[
|
||||
{
|
||||
"key": "h7b9i8khc3q",
|
||||
"name": "users",
|
||||
"inherit": false,
|
||||
"hidden": false,
|
||||
"description": null,
|
||||
"category": [],
|
||||
"namespace": "users.users",
|
||||
"duplicator": {
|
||||
"dumpable": "optional",
|
||||
"with": "rolesUsers"
|
||||
},
|
||||
"sortable": "sort",
|
||||
"model": "UserModel",
|
||||
"createdBy": true,
|
||||
"updatedBy": true,
|
||||
"logging": true,
|
||||
"from": "db2cm",
|
||||
"title": "{{t(\"Users\")}}",
|
||||
"rawTitle": "{{t(\"Users\")}}",
|
||||
"fields": [
|
||||
{
|
||||
"uiSchema": {
|
||||
"type": "number",
|
||||
"title": "{{t(\"ID\")}}",
|
||||
"x-component": "InputNumber",
|
||||
"x-read-pretty": true,
|
||||
"rawTitle": "{{t(\"ID\")}}"
|
||||
},
|
||||
"key": "ffp1f2sula0",
|
||||
"name": "id",
|
||||
"type": "bigInt",
|
||||
"interface": "id",
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"autoIncrement": true,
|
||||
"primaryKey": true,
|
||||
"allowNull": false
|
||||
},
|
||||
{
|
||||
"uiSchema": {
|
||||
"type": "string",
|
||||
"title": "{{t(\"Nickname\")}}",
|
||||
"x-component": "Input",
|
||||
"rawTitle": "{{t(\"Nickname\")}}"
|
||||
},
|
||||
"key": "vrv7yjue90g",
|
||||
"name": "nickname",
|
||||
"type": "string",
|
||||
"interface": "input",
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null
|
||||
},
|
||||
{
|
||||
"uiSchema": {
|
||||
"type": "string",
|
||||
"title": "{{t(\"Username\")}}",
|
||||
"x-component": "Input",
|
||||
"x-validator": {
|
||||
"username": true
|
||||
},
|
||||
"required": true,
|
||||
"rawTitle": "{{t(\"Username\")}}"
|
||||
},
|
||||
"key": "2ccs6evyrub",
|
||||
"name": "username",
|
||||
"type": "string",
|
||||
"interface": "input",
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"unique": true
|
||||
},
|
||||
{
|
||||
"uiSchema": {
|
||||
"type": "string",
|
||||
"title": "{{t(\"Email\")}}",
|
||||
"x-component": "Input",
|
||||
"x-validator": "email",
|
||||
"required": true,
|
||||
"rawTitle": "{{t(\"Email\")}}"
|
||||
},
|
||||
"key": "rrskwjl5wt1",
|
||||
"name": "email",
|
||||
"type": "string",
|
||||
"interface": "email",
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"unique": true
|
||||
},
|
||||
{
|
||||
"uiSchema": {
|
||||
"type": "string",
|
||||
"title": "{{t(\"Phone\")}}",
|
||||
"x-component": "Input",
|
||||
"x-validator": "phone",
|
||||
"required": true,
|
||||
"rawTitle": "{{t(\"Phone\")}}"
|
||||
},
|
||||
"key": "yzv4yjeg0pn",
|
||||
"name": "phone",
|
||||
"type": "string",
|
||||
"interface": "phone",
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"unique": true
|
||||
},
|
||||
{
|
||||
"uiSchema": {
|
||||
"type": "string",
|
||||
"title": "{{t(\"Password\")}}",
|
||||
"x-component": "Password",
|
||||
"rawTitle": "{{t(\"Password\")}}"
|
||||
},
|
||||
"key": "3pzwulm5o6b",
|
||||
"name": "password",
|
||||
"type": "password",
|
||||
"interface": "password",
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"key": "q3is17d1abv",
|
||||
"name": "appLang",
|
||||
"type": "string",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null
|
||||
},
|
||||
{
|
||||
"key": "lhysy09i27u",
|
||||
"name": "resetToken",
|
||||
"type": "string",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"unique": true,
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"key": "xlbup0n8dr5",
|
||||
"name": "systemSettings",
|
||||
"type": "json",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"defaultValue": {}
|
||||
},
|
||||
{
|
||||
"key": "w1vjr3cu2dk",
|
||||
"name": "sort",
|
||||
"type": "sort",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"key": "p3tw5siagb4",
|
||||
"name": "createdById",
|
||||
"type": "context",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"dataType": "bigInt",
|
||||
"dataIndex": "state.currentUser.id",
|
||||
"createOnly": true,
|
||||
"visible": true,
|
||||
"index": true
|
||||
},
|
||||
{
|
||||
"key": "1j6mx2762or",
|
||||
"name": "createdBy",
|
||||
"type": "belongsTo",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"target": "users",
|
||||
"foreignKey": "createdById",
|
||||
"targetKey": "id"
|
||||
},
|
||||
{
|
||||
"key": "k91xpvbvwle",
|
||||
"name": "updatedById",
|
||||
"type": "context",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"dataType": "bigInt",
|
||||
"dataIndex": "state.currentUser.id",
|
||||
"visible": true,
|
||||
"index": true
|
||||
},
|
||||
{
|
||||
"key": "xfog81yb136",
|
||||
"name": "updatedBy",
|
||||
"type": "belongsTo",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"target": "users",
|
||||
"foreignKey": "updatedById",
|
||||
"targetKey": "id"
|
||||
},
|
||||
{
|
||||
"uiSchema": {
|
||||
"type": "array",
|
||||
"title": "{{t(\"Roles\")}}",
|
||||
"x-component": "AssociationField",
|
||||
"x-component-props": {
|
||||
"multiple": true,
|
||||
"fieldNames": {
|
||||
"label": "title",
|
||||
"value": "name"
|
||||
}
|
||||
},
|
||||
"rawTitle": "{{t(\"Roles\")}}"
|
||||
},
|
||||
"key": "b7s9ywt6n06",
|
||||
"name": "roles",
|
||||
"type": "belongsToMany",
|
||||
"interface": "m2m",
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"target": "roles",
|
||||
"foreignKey": "userId",
|
||||
"otherKey": "roleName",
|
||||
"onDelete": "CASCADE",
|
||||
"sourceKey": "id",
|
||||
"targetKey": "name",
|
||||
"through": "rolesUsers"
|
||||
},
|
||||
{
|
||||
"key": "0j83jlhhi9r",
|
||||
"name": "jobs",
|
||||
"type": "belongsToMany",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"through": "users_jobs",
|
||||
"onDelete": "CASCADE",
|
||||
"foreignKey": "userId",
|
||||
"sourceKey": "id",
|
||||
"otherKey": "jobId",
|
||||
"targetKey": "id"
|
||||
},
|
||||
{
|
||||
"key": "h7s78jvbo89",
|
||||
"name": "usersJobs",
|
||||
"type": "hasMany",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"target": "users_jobs",
|
||||
"foreignKey": "userId",
|
||||
"sourceKey": "id",
|
||||
"targetKey": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "pqnenvqrzxr",
|
||||
"name": "roles",
|
||||
"inherit": false,
|
||||
"hidden": false,
|
||||
"description": null,
|
||||
"category": [],
|
||||
"namespace": "acl.acl",
|
||||
"duplicator": {
|
||||
"dumpable": "required",
|
||||
"with": "uiSchemas"
|
||||
},
|
||||
"autoGenId": false,
|
||||
"model": "RoleModel",
|
||||
"filterTargetKey": "name",
|
||||
"sortable": true,
|
||||
"from": "db2cm",
|
||||
"title": "{{t(\"Roles\")}}",
|
||||
"rawTitle": "{{t(\"Roles\")}}",
|
||||
"fields": [
|
||||
{
|
||||
"uiSchema": {
|
||||
"type": "string",
|
||||
"title": "{{t(\"Role UID\")}}",
|
||||
"x-component": "Input",
|
||||
"rawTitle": "{{t(\"Role UID\")}}"
|
||||
},
|
||||
"key": "jbz9m80bxmp",
|
||||
"name": "name",
|
||||
"type": "uid",
|
||||
"interface": "input",
|
||||
"description": null,
|
||||
"collectionName": "roles",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"prefix": "r_",
|
||||
"primaryKey": true
|
||||
},
|
||||
{
|
||||
"uiSchema": {
|
||||
"type": "string",
|
||||
"title": "{{t(\"Role name\")}}",
|
||||
"x-component": "Input",
|
||||
"rawTitle": "{{t(\"Role name\")}}"
|
||||
},
|
||||
"key": "faywtz4sf3u",
|
||||
"name": "title",
|
||||
"type": "string",
|
||||
"interface": "input",
|
||||
"description": null,
|
||||
"collectionName": "roles",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"unique": true,
|
||||
"translation": true
|
||||
},
|
||||
{
|
||||
"key": "1enkovm9sye",
|
||||
"name": "description",
|
||||
"type": "string",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "roles",
|
||||
"parentKey": null,
|
||||
"reverseKey": null
|
||||
},
|
||||
{
|
||||
"key": "wwoozt6911x",
|
||||
"name": "strategy",
|
||||
"type": "json",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "roles",
|
||||
"parentKey": null,
|
||||
"reverseKey": null
|
||||
},
|
||||
{
|
||||
"key": "wupe6r46azg",
|
||||
"name": "default",
|
||||
"type": "boolean",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "roles",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"key": "1wxu26hqcey",
|
||||
"name": "hidden",
|
||||
"type": "boolean",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "roles",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"key": "krspuzq44fc",
|
||||
"name": "allowConfigure",
|
||||
"type": "boolean",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "roles",
|
||||
"parentKey": null,
|
||||
"reverseKey": null
|
||||
},
|
||||
{
|
||||
"key": "mv8vmxdj2wd",
|
||||
"name": "allowNewMenu",
|
||||
"type": "boolean",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "roles",
|
||||
"parentKey": null,
|
||||
"reverseKey": null
|
||||
},
|
||||
{
|
||||
"key": "c81x5luikwd",
|
||||
"name": "menuUiSchemas",
|
||||
"type": "belongsToMany",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "roles",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"target": "uiSchemas",
|
||||
"targetKey": "x-uid",
|
||||
"onDelete": "CASCADE",
|
||||
"foreignKey": "roleName",
|
||||
"sourceKey": "name",
|
||||
"otherKey": "uiSchemaXUid",
|
||||
"through": "rolesUischemas"
|
||||
},
|
||||
{
|
||||
"key": "lm9x55l6gxm",
|
||||
"name": "resources",
|
||||
"type": "hasMany",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "roles",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"target": "rolesResources",
|
||||
"sourceKey": "name",
|
||||
"targetKey": "name",
|
||||
"foreignKey": "roleName"
|
||||
},
|
||||
{
|
||||
"key": "5dnal1v7se3",
|
||||
"name": "snippets",
|
||||
"type": "set",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "roles",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"defaultValue": ["!ui.*", "!pm", "!pm.*"]
|
||||
},
|
||||
{
|
||||
"key": "foxkspl2zt7",
|
||||
"name": "users",
|
||||
"type": "belongsToMany",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "roles",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"target": "users",
|
||||
"foreignKey": "roleName",
|
||||
"otherKey": "userId",
|
||||
"onDelete": "CASCADE",
|
||||
"sourceKey": "name",
|
||||
"targetKey": "id",
|
||||
"through": "rolesUsers"
|
||||
},
|
||||
{
|
||||
"key": "n3937y2slrt",
|
||||
"name": "sort",
|
||||
"type": "sort",
|
||||
"interface": null,
|
||||
"description": null,
|
||||
"collectionName": "roles",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"hidden": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
@ -1,38 +0,0 @@
|
||||
import { Application, ApplicationOptions, CollectionManagerProvider } from '@nocobase/client';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import React, { ComponentType } from 'react';
|
||||
import collections from './collections.json';
|
||||
|
||||
export function createApp(Demo: ComponentType<any>, options: ApplicationOptions, mocks: Record<string, any> = {}) {
|
||||
const Provider = () => {
|
||||
return (
|
||||
<CollectionManagerProvider collections={collections as any}>
|
||||
<Demo />
|
||||
</CollectionManagerProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const app = new Application({
|
||||
apiClient: {
|
||||
baseURL: 'http://localhost:8000',
|
||||
},
|
||||
providers: [Provider],
|
||||
...options,
|
||||
});
|
||||
|
||||
const mock = new MockAdapter(app.apiClient.axios);
|
||||
|
||||
Object.entries(mocks).forEach(([url, data]) => {
|
||||
mock.onGet(url).reply(async (config) => {
|
||||
const res = typeof data === 'function' ? data(config) : data;
|
||||
return [200, res];
|
||||
});
|
||||
mock.onPost(url).reply(async (config) => {
|
||||
const res = typeof data === 'function' ? data(config) : data;
|
||||
return [200, res];
|
||||
});
|
||||
});
|
||||
|
||||
const Root = app.getRootComponent();
|
||||
return Root;
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
import React, { FC } from 'react';
|
||||
import {
|
||||
SchemaComponent,
|
||||
useDataBlockRequestV2,
|
||||
useBlockSettingsV2,
|
||||
withSchemaComponentProps,
|
||||
UseDataBlockProps,
|
||||
} from '@nocobase/client';
|
||||
import { createApp } from './createApp';
|
||||
import { Switch, Table, TableProps } from 'antd';
|
||||
|
||||
interface DemoTableRecordType {
|
||||
name: string;
|
||||
}
|
||||
type DemoTableProps = TableProps<DemoTableRecordType>;
|
||||
const DemoTable: FC<DemoTableProps> = withSchemaComponentProps((props) => {
|
||||
const { dn } = useBlockSettingsV2();
|
||||
return (
|
||||
<>
|
||||
<Switch
|
||||
defaultChecked={props.bordered}
|
||||
checkedChildren="Bordered"
|
||||
onChange={(v) => {
|
||||
dn.deepMerge({
|
||||
'x-decorator-props': {
|
||||
bordered: v,
|
||||
},
|
||||
});
|
||||
}}
|
||||
></Switch>
|
||||
<Table {...props}></Table>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
function useDemoTableProps(): DemoTableProps {
|
||||
const { data, loading } = useDataBlockRequestV2<{ data: DemoTableRecordType[]; total: number }>();
|
||||
const { props, changeSchemaProps } = useBlockSettingsV2<{
|
||||
rowKey?: string;
|
||||
params?: Record<string, any>;
|
||||
bordered?: boolean;
|
||||
}>();
|
||||
const { rowKey, params, bordered } = props;
|
||||
return {
|
||||
columns: [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
},
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
dataIndex: 'address',
|
||||
key: 'address',
|
||||
},
|
||||
],
|
||||
loading,
|
||||
dataSource: data?.data || [],
|
||||
rowKey,
|
||||
bordered,
|
||||
pagination: {
|
||||
pageSize: params.pageSize || 5,
|
||||
current: params.page || 1,
|
||||
total: data?.total,
|
||||
onChange(page, pageSize) {
|
||||
changeSchemaProps({
|
||||
params: {
|
||||
pageSize,
|
||||
page,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const collection = 'users';
|
||||
const action = 'list';
|
||||
|
||||
const useTableDataBlockDecoratorProps: UseDataBlockProps<'CollectionList'> = () => {
|
||||
return {
|
||||
params: {
|
||||
address: 'New York',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const schema = {
|
||||
type: 'void',
|
||||
name: 'hello',
|
||||
'x-decorator': 'DataBlockProviderV2',
|
||||
'x-use-decorator-props': 'useTableDataBlockDecoratorProps',
|
||||
'x-component': 'DemoTable',
|
||||
'x-use-component-props': 'useDemoTableProps',
|
||||
'x-decorator-props': {
|
||||
action: action,
|
||||
collection: collection,
|
||||
params: {
|
||||
pageSize: 5,
|
||||
page: 1,
|
||||
},
|
||||
rowKey: 'id',
|
||||
bordered: false,
|
||||
},
|
||||
};
|
||||
|
||||
const Demo = () => {
|
||||
return <SchemaComponent schema={schema}></SchemaComponent>;
|
||||
};
|
||||
|
||||
const mocks = {
|
||||
[`${collection}:${action}`]: function (config: any) {
|
||||
console.log('请求结果');
|
||||
const { page = 1, pageSize, address } = config.params;
|
||||
const fixedData = [];
|
||||
for (let i = 0; i < pageSize; i += 1) {
|
||||
fixedData.push({
|
||||
id: (page - 1) * pageSize + i + 1,
|
||||
name: ['Light', 'Bamboo', 'Little'][i % 3],
|
||||
address: `${address} No. ${i + 1} Lake Park`,
|
||||
});
|
||||
}
|
||||
return {
|
||||
data: fixedData,
|
||||
total: 200,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const Root = createApp(
|
||||
Demo,
|
||||
{ components: { DemoTable }, scopes: { useDemoTableProps, useTableDataBlockDecoratorProps } },
|
||||
mocks,
|
||||
);
|
||||
|
||||
export default Root;
|
@ -1,140 +0,0 @@
|
||||
import React, { FC } from 'react';
|
||||
import {
|
||||
RecordV2,
|
||||
RecordProviderV2,
|
||||
SchemaComponent,
|
||||
useDataBlockRequestV2,
|
||||
useBlockSettingsV2,
|
||||
withSchemaComponentProps,
|
||||
useDesignable,
|
||||
} from '@nocobase/client';
|
||||
import { createApp } from './createApp';
|
||||
import { Switch, Table, TableProps } from 'antd';
|
||||
|
||||
interface DemoTableRecordType {
|
||||
name: string;
|
||||
}
|
||||
type DemoTableProps = TableProps<DemoTableRecordType>;
|
||||
const DemoTable: FC<DemoTableProps> = withSchemaComponentProps((props) => {
|
||||
const { dn } = useDesignable();
|
||||
return (
|
||||
<>
|
||||
<Switch
|
||||
defaultChecked={props.bordered}
|
||||
checkedChildren="Bordered"
|
||||
onChange={(v) => {
|
||||
dn.deepMerge({
|
||||
'x-decorator-props': {
|
||||
bordered: v,
|
||||
},
|
||||
});
|
||||
}}
|
||||
></Switch>
|
||||
<Table {...props}></Table>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
function useDemoTableProps(): DemoTableProps {
|
||||
const { data, loading } = useDataBlockRequestV2<{ data: DemoTableRecordType[]; total: number }>();
|
||||
const { rowKey, params, bordered } = useBlockSettingsV2<{ rowKey?: string; params?: Record<string, any> }>();
|
||||
const { dn } = useDesignable();
|
||||
return {
|
||||
columns: [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
},
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
dataIndex: 'address',
|
||||
key: 'address',
|
||||
},
|
||||
],
|
||||
loading,
|
||||
dataSource: data?.data || [],
|
||||
rowKey,
|
||||
bordered,
|
||||
pagination: {
|
||||
pageSize: params.pageSize || 5,
|
||||
current: params.page || 1,
|
||||
total: data?.total,
|
||||
onChange(page, pageSize) {
|
||||
dn.deepMerge({
|
||||
'x-decorator-props': {
|
||||
params: {
|
||||
pageSize,
|
||||
page,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const collection = 'users';
|
||||
const action = 'list';
|
||||
const associationA = 'a';
|
||||
const associationB = 'b';
|
||||
const association = `${associationA}.${associationB}`;
|
||||
|
||||
const schema = {
|
||||
type: 'void',
|
||||
name: 'hello',
|
||||
'x-decorator': 'DataBlockProviderV2',
|
||||
'x-component': 'DemoTable',
|
||||
'x-use-component-props': 'useDemoTableProps',
|
||||
'x-decorator-props': {
|
||||
action: action,
|
||||
params: {
|
||||
pageSize: 5,
|
||||
page: 1,
|
||||
},
|
||||
rowKey: 'id',
|
||||
dragSort: false,
|
||||
resource: collection,
|
||||
showIndex: true,
|
||||
collection: collection,
|
||||
bordered: false,
|
||||
association,
|
||||
},
|
||||
};
|
||||
|
||||
const Demo = () => {
|
||||
const record = new RecordV2({ current: { sourceId: 1 } });
|
||||
return (
|
||||
<RecordProviderV2 record={record}>
|
||||
<SchemaComponent schema={schema}></SchemaComponent>
|
||||
</RecordProviderV2>
|
||||
);
|
||||
};
|
||||
|
||||
const mocks = {
|
||||
[`${associationA}/1/${associationB}:${action}`]: function (config: any) {
|
||||
console.log('请求结果');
|
||||
const { page = 1, pageSize } = config.params;
|
||||
const fixedData = [];
|
||||
for (let i = 0; i < pageSize; i += 1) {
|
||||
fixedData.push({
|
||||
id: (page - 1) * pageSize + i + 1,
|
||||
name: ['Light', 'Bamboo', 'Little'][i % 3],
|
||||
address: `New York No. ${i + 1} Lake Park`,
|
||||
});
|
||||
}
|
||||
return {
|
||||
data: fixedData,
|
||||
total: 200,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const Root = createApp(Demo, { components: { DemoTable }, scopes: { useDemoTableProps } }, mocks);
|
||||
|
||||
export default Root;
|
@ -1,23 +0,0 @@
|
||||
# Block Provider
|
||||
|
||||
## API
|
||||
|
||||
- collection-list
|
||||
|
||||
<code src="./demo1.tsx"></code>
|
||||
|
||||
- collection-get
|
||||
|
||||
<code src="./demo2.tsx"></code>
|
||||
|
||||
- collection-create
|
||||
|
||||
<code src="./demo3.tsx"></code>
|
||||
|
||||
- collection-record
|
||||
|
||||
<code src="./demo4.tsx"></code>
|
||||
|
||||
## 综合示例
|
||||
|
||||
<code src="./table/index.tsx"></code>
|
@ -1,47 +0,0 @@
|
||||
import { useField, useFieldSchema } from '@formily/react';
|
||||
import { useCollection, useCollectionManager, useCompile, useSchemaToolbarRender } from '@nocobase/client';
|
||||
import React, { useLayoutEffect } from 'react';
|
||||
import { isCollectionFieldComponent } from './Table.useProps';
|
||||
|
||||
export const useColumnSchema = () => {
|
||||
const { getField } = useCollection();
|
||||
const compile = useCompile();
|
||||
const columnSchema = useFieldSchema();
|
||||
const { getCollectionJoinField } = useCollectionManager();
|
||||
const fieldSchema = columnSchema.reduceProperties((buf, s) => {
|
||||
if (isCollectionFieldComponent(s)) {
|
||||
return s;
|
||||
}
|
||||
return buf;
|
||||
}, null);
|
||||
if (!fieldSchema) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const collectionField = getField(fieldSchema.name) || getCollectionJoinField(fieldSchema?.['x-collection-field']);
|
||||
return { columnSchema, fieldSchema, collectionField, uiSchema: compile(collectionField?.uiSchema) };
|
||||
};
|
||||
|
||||
export const TableColumnDecorator = () => {
|
||||
const field = useField();
|
||||
const { fieldSchema, uiSchema } = useColumnSchema();
|
||||
const { render } = useSchemaToolbarRender(fieldSchema);
|
||||
const compile = useCompile();
|
||||
useLayoutEffect(() => {
|
||||
if (field.title) {
|
||||
return;
|
||||
}
|
||||
if (!fieldSchema) {
|
||||
return;
|
||||
}
|
||||
if (uiSchema?.title) {
|
||||
field.title = uiSchema?.title;
|
||||
}
|
||||
}, [field, fieldSchema, uiSchema?.title]);
|
||||
return (
|
||||
<>
|
||||
{render()}
|
||||
<div role="button">{field?.title || compile(uiSchema?.title)}</div>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,130 +0,0 @@
|
||||
import { Schema } from '@formily/json-schema';
|
||||
import { useFieldSchema, useForm } from '@formily/react';
|
||||
import { SchemaInitializer, SchemaInitializerItemType, useCollectionManager, useCollectionV2 } from '@nocobase/client';
|
||||
|
||||
const quickEditField = [
|
||||
'attachment',
|
||||
'textarea',
|
||||
'markdown',
|
||||
'json',
|
||||
'richText',
|
||||
'polygon',
|
||||
'circle',
|
||||
'point',
|
||||
'lineString',
|
||||
];
|
||||
|
||||
export const findTableColumn = (schema: Schema, key: string, action: string, deepth = 0) => {
|
||||
return schema.reduceProperties((buf, s) => {
|
||||
if (s[key] === action) {
|
||||
return s;
|
||||
}
|
||||
const c = s.reduceProperties((buf, s) => {
|
||||
if (s[key] === action) {
|
||||
return s;
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
if (c) {
|
||||
return c;
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
};
|
||||
export const removeTableColumn = (schema, cb) => {
|
||||
cb(schema.parent);
|
||||
};
|
||||
export const useTableColumnInitializerFields = () => {
|
||||
const { name, fields = [] } = useCollectionV2();
|
||||
const { getInterface, getCollection } = useCollectionManager();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const isSubTable = fieldSchema['x-component'] === 'AssociationField.SubTable';
|
||||
const form = useForm();
|
||||
const isReadPretty = isSubTable ? form.readPretty : true;
|
||||
|
||||
return fields
|
||||
.filter(
|
||||
(field) => field?.interface && field?.interface !== 'subTable' && !field?.isForeignKey && !field?.treeChildren,
|
||||
)
|
||||
.map((field) => {
|
||||
const interfaceConfig = getInterface(field.interface);
|
||||
const isFileCollection = field?.target && getCollection(field?.target)?.template === 'file';
|
||||
const schema = {
|
||||
name: field.name,
|
||||
'x-collection-field': `${name}.${field.name}`,
|
||||
'x-component': 'CollectionField',
|
||||
'x-component-props': isFileCollection
|
||||
? {
|
||||
fieldNames: {
|
||||
label: 'preview',
|
||||
value: 'id',
|
||||
},
|
||||
}
|
||||
: {},
|
||||
'x-read-pretty': isReadPretty || field.uiSchema?.['x-read-pretty'],
|
||||
'x-decorator': isSubTable
|
||||
? quickEditField.includes(field.interface) || isFileCollection
|
||||
? 'QuickEdit'
|
||||
: 'FormItem'
|
||||
: null,
|
||||
'x-decorator-props': {
|
||||
labelStyle: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
};
|
||||
return {
|
||||
type: 'item',
|
||||
name: field.name,
|
||||
title: field?.uiSchema?.title || field.name,
|
||||
Component: 'TableCollectionFieldInitializer',
|
||||
find: findTableColumn,
|
||||
remove: removeTableColumn,
|
||||
schemaInitialize: (s) => {
|
||||
interfaceConfig?.schemaInitialize?.(s, {
|
||||
field,
|
||||
readPretty: isReadPretty,
|
||||
block: 'Table',
|
||||
targetCollection: getCollection(field.target),
|
||||
});
|
||||
},
|
||||
field,
|
||||
schema,
|
||||
} as SchemaInitializerItemType;
|
||||
});
|
||||
};
|
||||
|
||||
export const tableColumnInitializer = new SchemaInitializer({
|
||||
name: 'tableColumnInitializer',
|
||||
insertPosition: 'beforeEnd',
|
||||
icon: 'SettingOutlined',
|
||||
title: 'Configure columns',
|
||||
wrap: (s) => {
|
||||
if (s['x-action-column']) {
|
||||
return s;
|
||||
}
|
||||
return {
|
||||
type: 'void',
|
||||
'x-decorator': 'TableColumnDecorator',
|
||||
'x-settings': 'tableColumnSettings',
|
||||
'x-component': 'TableColumn',
|
||||
properties: {
|
||||
[s.name]: {
|
||||
...s,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
items: [
|
||||
{
|
||||
name: 'displayFields',
|
||||
type: 'itemGroup',
|
||||
title: '{{t("Display fields")}}',
|
||||
useChildren() {
|
||||
const columns = useTableColumnInitializerFields();
|
||||
console.log('columns', columns);
|
||||
return columns;
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
@ -1,17 +0,0 @@
|
||||
import { SchemaSettings } from '@nocobase/client';
|
||||
|
||||
export const tableColumnSettings = new SchemaSettings({
|
||||
name: 'tableColumnSettings',
|
||||
items: [
|
||||
{
|
||||
name: 'remove',
|
||||
type: 'remove',
|
||||
componentProps: {
|
||||
removeParentsIfNoChildren: true,
|
||||
breakRemoveOn: {
|
||||
'x-component': 'Grid',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
@ -1,7 +0,0 @@
|
||||
import { useField } from '@formily/react';
|
||||
import React from 'react';
|
||||
|
||||
export const TableColumn = () => {
|
||||
const field = useField();
|
||||
return <div role="button">{field.title}</div>;
|
||||
};
|
@ -1,9 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
export const useTableDecoratorProps = ({ fieldNames }) => {
|
||||
const [expandFlag, setExpandFlag] = useState(fieldNames ? true : false);
|
||||
return {
|
||||
expandFlag,
|
||||
setExpandFlag,
|
||||
};
|
||||
};
|
@ -1,20 +0,0 @@
|
||||
import { Plugin } from '@nocobase/client';
|
||||
|
||||
import { tableSettings } from './Table.settings';
|
||||
import { TableToolbar } from './Table.toolbar';
|
||||
import { useTableProps } from './Table.useProps';
|
||||
import { useTableDecoratorProps } from './Table.decoratorProps';
|
||||
import { tableColumnInitializer } from './Table.column.initializer';
|
||||
import { NocoBaseTable } from './Table';
|
||||
import { tableColumnSettings } from './Table.column.settings';
|
||||
import { TableColumn } from './Table.column';
|
||||
import { TableColumnDecorator } from './Table.column.decorator';
|
||||
|
||||
export class TablePlugin extends Plugin {
|
||||
async load() {
|
||||
this.app.schemaSettingsManager.add(tableSettings, tableColumnSettings);
|
||||
this.app.addComponents({ TableToolbar, NocoBaseTable, TableColumn, TableColumnDecorator });
|
||||
this.app.addScopes({ useTableProps, useTableDecoratorProps });
|
||||
this.app.schemaInitializerManager.add(tableColumnInitializer);
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
import { SchemaSettings, useBlockSettingsV2 } from '@nocobase/client';
|
||||
|
||||
export const tableSettings = new SchemaSettings({
|
||||
name: 'tableSettings',
|
||||
items: [
|
||||
{
|
||||
name: 'bordered',
|
||||
type: 'switch',
|
||||
useComponentProps() {
|
||||
const { dn, props } = useBlockSettingsV2<{ bordered?: boolean }>();
|
||||
return {
|
||||
title: 'Bordered',
|
||||
checked: !!props.bordered,
|
||||
onChange(v) {
|
||||
dn.deepMerge({
|
||||
'x-decorator-props': {
|
||||
bordered: v,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'remove',
|
||||
type: 'remove',
|
||||
componentProps: {
|
||||
removeParentsIfNoChildren: true,
|
||||
breakRemoveOn: {
|
||||
'x-component': 'Grid',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
@ -1,7 +0,0 @@
|
||||
import React from 'react';
|
||||
import { SchemaToolbar, useCollectionV2 } from '@nocobase/client';
|
||||
|
||||
export const TableToolbar = () => {
|
||||
const { name, title } = useCollectionV2();
|
||||
return <SchemaToolbar title={title || name} draggable={false} />;
|
||||
};
|
@ -1,4 +0,0 @@
|
||||
import { withSchemaComponentProps } from '@nocobase/client';
|
||||
import { Table } from 'antd';
|
||||
|
||||
export const NocoBaseTable = withSchemaComponentProps(Table);
|
@ -1,105 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useDataBlockRequestV2, useBlockSettingsV2, useDesignable, useSchemaInitializerRender } from '@nocobase/client';
|
||||
import { ISchema, RecursionField, Schema, useFieldSchema } from '@formily/react';
|
||||
import { TableProps } from 'antd';
|
||||
|
||||
interface TableRequest {
|
||||
data: any[];
|
||||
meta: {
|
||||
count: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalPage: number;
|
||||
};
|
||||
}
|
||||
|
||||
export const isCollectionFieldComponent = (schema: ISchema) => {
|
||||
return schema['x-component'] === 'CollectionField';
|
||||
};
|
||||
|
||||
export const isColumnComponent = (schema: Schema) => {
|
||||
return schema['x-component']?.endsWith('.Column') > -1;
|
||||
};
|
||||
|
||||
const useTableColumns = () => {
|
||||
const schema = useFieldSchema();
|
||||
const { designable } = useDesignable();
|
||||
const { exists, render } = useSchemaInitializerRender(schema['x-initializer'], schema['x-initializer-props']);
|
||||
const columns = schema
|
||||
.reduceProperties((buf, s) => {
|
||||
if (isColumnComponent(s)) {
|
||||
return buf.concat([s]);
|
||||
}
|
||||
return buf;
|
||||
}, [])
|
||||
?.map((s: Schema) => {
|
||||
const collectionFields = s.reduceProperties((buf, s) => {
|
||||
if (isCollectionFieldComponent(s)) {
|
||||
return buf.concat([s]);
|
||||
}
|
||||
}, []);
|
||||
const dataIndex = collectionFields?.length > 0 ? collectionFields[0].name : s.name;
|
||||
return {
|
||||
title: <RecursionField name={s.name} schema={s} onlyRenderSelf />,
|
||||
dataIndex,
|
||||
key: s.name,
|
||||
sorter: s['x-component-props']?.['sorter'],
|
||||
width: 200,
|
||||
...s['x-component-props'],
|
||||
render: (v, record) => {
|
||||
return v;
|
||||
},
|
||||
};
|
||||
});
|
||||
if (!exists) {
|
||||
return columns;
|
||||
}
|
||||
|
||||
const tableColumns = columns.concat({
|
||||
title: render(),
|
||||
dataIndex: 'TABLE_COLUMN_INITIALIZER',
|
||||
key: 'TABLE_COLUMN_INITIALIZER',
|
||||
render: designable ? () => <div style={{ minWidth: 300 }} /> : null,
|
||||
});
|
||||
return tableColumns;
|
||||
};
|
||||
|
||||
export function useTableProps(): TableProps<any> {
|
||||
const { data, loading } = useDataBlockRequestV2<TableRequest>();
|
||||
const { props, dn } = useBlockSettingsV2<{
|
||||
rowKey?: string;
|
||||
params?: Record<string, any>;
|
||||
bordered?: boolean;
|
||||
}>();
|
||||
const { rowKey, params, bordered } = props;
|
||||
const columns = useTableColumns();
|
||||
return {
|
||||
columns,
|
||||
loading,
|
||||
dataSource: data?.data || [],
|
||||
rowKey,
|
||||
bordered,
|
||||
pagination: {
|
||||
pageSize: params.pageSize || 5,
|
||||
current: params.page || 1,
|
||||
total: data?.meta?.count,
|
||||
onChange(page, pageSize) {
|
||||
dn.deepMerge({
|
||||
'x-decorator-props': {
|
||||
params: {
|
||||
pageSize,
|
||||
page,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
components: {
|
||||
body: {
|
||||
row: ({ children, ...props }) => {
|
||||
return <tr {...props}>{children}</tr>;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
CardItem,
|
||||
Application,
|
||||
SchemaComponent,
|
||||
DataBlockProviderV2,
|
||||
TableCollectionFieldInitializer,
|
||||
CollectionManagerProvider,
|
||||
} from '@nocobase/client';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import requestData from './requestData.json';
|
||||
import schema from './schema.json';
|
||||
import collections from '../collections.json';
|
||||
|
||||
import { TablePlugin } from './Table.plugin';
|
||||
|
||||
const Root = () => {
|
||||
return (
|
||||
<CollectionManagerProvider collections={collections as any}>
|
||||
<SchemaComponent schema={schema} />
|
||||
</CollectionManagerProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const app = new Application({
|
||||
apiClient: {
|
||||
baseURL: 'http://localhost:8000/api',
|
||||
},
|
||||
plugins: [TablePlugin],
|
||||
providers: [Root],
|
||||
components: {
|
||||
CardItem,
|
||||
DataBlockProviderV2,
|
||||
TableCollectionFieldInitializer,
|
||||
},
|
||||
designable: true,
|
||||
});
|
||||
|
||||
const mock = new MockAdapter(app.apiClient.axios);
|
||||
mock.onGet('users:list').reply(200, requestData);
|
||||
|
||||
export default app.getRootComponent();
|
@ -1,264 +0,0 @@
|
||||
{
|
||||
"_isJSONSchemaObject": true,
|
||||
"version": "2.0",
|
||||
"type": "void",
|
||||
"x-decorator": "TableBlockProvider",
|
||||
"x-acl-action": "users:list",
|
||||
"x-decorator-props": {
|
||||
"collection": "users",
|
||||
"resource": "users",
|
||||
"action": "list",
|
||||
"params": {
|
||||
"pageSize": 20
|
||||
},
|
||||
"rowKey": "id",
|
||||
"showIndex": true,
|
||||
"dragSort": false,
|
||||
"disableTemplate": false
|
||||
},
|
||||
"x-designer": "TableBlockDesigner",
|
||||
"x-component": "CardItem",
|
||||
"x-filter-targets": [],
|
||||
"properties": {
|
||||
"actions": {
|
||||
"_isJSONSchemaObject": true,
|
||||
"version": "2.0",
|
||||
"type": "void",
|
||||
"x-initializer": "TableActionInitializers",
|
||||
"x-component": "ActionBar",
|
||||
"x-component-props": {
|
||||
"style": {
|
||||
"marginBottom": "var(--nb-spacing)"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"a94r4fylw0f": {
|
||||
"_isJSONSchemaObject": true,
|
||||
"version": "2.0",
|
||||
"type": "void",
|
||||
"x-action": "create",
|
||||
"x-acl-action": "create",
|
||||
"title": "{{t('Add new')}}",
|
||||
"x-designer": "Action.Designer",
|
||||
"x-component": "Action",
|
||||
"x-decorator": "ACLActionProvider",
|
||||
"x-component-props": {
|
||||
"openMode": "drawer",
|
||||
"type": "primary",
|
||||
"component": "CreateRecordAction",
|
||||
"icon": "PlusOutlined"
|
||||
},
|
||||
"x-align": "right",
|
||||
"x-acl-action-props": {
|
||||
"skipScopeCheck": true
|
||||
},
|
||||
"properties": {
|
||||
"drawer": {
|
||||
"_isJSONSchemaObject": true,
|
||||
"version": "2.0",
|
||||
"type": "void",
|
||||
"title": "{{ t(\"Add record\") }}",
|
||||
"x-component": "Action.Container",
|
||||
"x-component-props": {
|
||||
"className": "nb-action-popup"
|
||||
},
|
||||
"properties": {
|
||||
"tabs": {
|
||||
"_isJSONSchemaObject": true,
|
||||
"version": "2.0",
|
||||
"type": "void",
|
||||
"x-component": "Tabs",
|
||||
"x-component-props": {},
|
||||
"x-initializer": "TabPaneInitializersForCreateFormBlock",
|
||||
"properties": {
|
||||
"tab1": {
|
||||
"_isJSONSchemaObject": true,
|
||||
"version": "2.0",
|
||||
"type": "void",
|
||||
"title": "{{t(\"Add new\")}}",
|
||||
"x-component": "Tabs.TabPane",
|
||||
"x-designer": "Tabs.Designer",
|
||||
"x-component-props": {},
|
||||
"properties": {
|
||||
"grid": {
|
||||
"_isJSONSchemaObject": true,
|
||||
"version": "2.0",
|
||||
"type": "void",
|
||||
"x-component": "Grid",
|
||||
"x-initializer": "CreateFormBlockInitializers",
|
||||
"x-uid": "rgh15rmpn51",
|
||||
"x-async": false,
|
||||
"x-index": 1
|
||||
}
|
||||
},
|
||||
"x-uid": "xcmqqdez9up",
|
||||
"x-async": false,
|
||||
"x-index": 1
|
||||
}
|
||||
},
|
||||
"x-uid": "b7zpfmevge2",
|
||||
"x-async": false,
|
||||
"x-index": 1
|
||||
}
|
||||
},
|
||||
"x-uid": "dvp2ppiiol3",
|
||||
"x-async": false,
|
||||
"x-index": 1
|
||||
}
|
||||
},
|
||||
"x-uid": "31ntpwf34wy",
|
||||
"x-async": false,
|
||||
"x-index": 2
|
||||
}
|
||||
},
|
||||
"x-uid": "m58frw0kwnm",
|
||||
"x-async": false,
|
||||
"x-index": 1
|
||||
},
|
||||
"ve197icvkz4": {
|
||||
"_isJSONSchemaObject": true,
|
||||
"version": "2.0",
|
||||
"type": "array",
|
||||
"x-initializer": "TableColumnInitializers",
|
||||
"x-component": "TableV2",
|
||||
"x-component-props": {
|
||||
"rowKey": "id",
|
||||
"rowSelection": {
|
||||
"type": "checkbox"
|
||||
},
|
||||
"useProps": "{{ useTableBlockProps }}"
|
||||
},
|
||||
"properties": {
|
||||
"actions": {
|
||||
"_isJSONSchemaObject": true,
|
||||
"version": "2.0",
|
||||
"type": "void",
|
||||
"title": "{{ t(\"Actions\") }}",
|
||||
"x-action-column": "actions",
|
||||
"x-decorator": "TableV2.Column.ActionBar",
|
||||
"x-component": "TableV2.Column",
|
||||
"x-designer": "TableV2.ActionColumnDesigner",
|
||||
"x-initializer": "TableActionColumnInitializers",
|
||||
"properties": {
|
||||
"actions": {
|
||||
"_isJSONSchemaObject": true,
|
||||
"version": "2.0",
|
||||
"type": "void",
|
||||
"x-decorator": "DndContext",
|
||||
"x-component": "Space",
|
||||
"x-component-props": {
|
||||
"split": "|"
|
||||
},
|
||||
"x-uid": "yu3vl95pjxe",
|
||||
"x-async": false,
|
||||
"x-index": 1
|
||||
}
|
||||
},
|
||||
"x-uid": "f174ti2ljhj",
|
||||
"x-async": false,
|
||||
"x-index": 1
|
||||
},
|
||||
"ptw1didvz8u": {
|
||||
"_isJSONSchemaObject": true,
|
||||
"version": "2.0",
|
||||
"type": "void",
|
||||
"x-decorator": "TableV2.Column.Decorator",
|
||||
"x-designer": "TableV2.Column.Designer",
|
||||
"x-component": "TableV2.Column",
|
||||
"properties": {
|
||||
"id": {
|
||||
"_isJSONSchemaObject": true,
|
||||
"version": "2.0",
|
||||
"x-collection-field": "users.id",
|
||||
"x-component": "CollectionField",
|
||||
"x-component-props": {},
|
||||
"x-read-pretty": true,
|
||||
"x-decorator": null,
|
||||
"x-decorator-props": {
|
||||
"labelStyle": {
|
||||
"display": "none"
|
||||
}
|
||||
},
|
||||
"x-uid": "btph12il4oe",
|
||||
"x-async": false,
|
||||
"x-index": 1
|
||||
}
|
||||
},
|
||||
"x-uid": "gtkofwm9ge1",
|
||||
"x-async": false,
|
||||
"x-index": 2
|
||||
},
|
||||
"ayc1c6i8lo3": {
|
||||
"_isJSONSchemaObject": true,
|
||||
"version": "2.0",
|
||||
"type": "void",
|
||||
"x-decorator": "TableV2.Column.Decorator",
|
||||
"x-designer": "TableV2.Column.Designer",
|
||||
"x-component": "TableV2.Column",
|
||||
"properties": {
|
||||
"nickname": {
|
||||
"_isJSONSchemaObject": true,
|
||||
"version": "2.0",
|
||||
"x-collection-field": "users.nickname",
|
||||
"x-component": "CollectionField",
|
||||
"x-component-props": {
|
||||
"ellipsis": true
|
||||
},
|
||||
"x-read-pretty": true,
|
||||
"x-decorator": null,
|
||||
"x-decorator-props": {
|
||||
"labelStyle": {
|
||||
"display": "none"
|
||||
}
|
||||
},
|
||||
"x-uid": "nrc2to382kx",
|
||||
"x-async": false,
|
||||
"x-index": 1
|
||||
}
|
||||
},
|
||||
"x-uid": "8ip9l7b0xfz",
|
||||
"x-async": false,
|
||||
"x-index": 3
|
||||
},
|
||||
"jg6znqow8ds": {
|
||||
"_isJSONSchemaObject": true,
|
||||
"version": "2.0",
|
||||
"type": "void",
|
||||
"x-decorator": "TableV2.Column.Decorator",
|
||||
"x-designer": "TableV2.Column.Designer",
|
||||
"x-component": "TableV2.Column",
|
||||
"properties": {
|
||||
"username": {
|
||||
"_isJSONSchemaObject": true,
|
||||
"version": "2.0",
|
||||
"x-collection-field": "users.username",
|
||||
"x-component": "CollectionField",
|
||||
"x-component-props": {
|
||||
"ellipsis": true
|
||||
},
|
||||
"x-read-pretty": true,
|
||||
"x-decorator": null,
|
||||
"x-decorator-props": {
|
||||
"labelStyle": {
|
||||
"display": "none"
|
||||
}
|
||||
},
|
||||
"x-uid": "a4y8vhd49n2",
|
||||
"x-async": false,
|
||||
"x-index": 1
|
||||
}
|
||||
},
|
||||
"x-uid": "yk1ydfk1gug",
|
||||
"x-async": false,
|
||||
"x-index": 4
|
||||
}
|
||||
},
|
||||
"x-uid": "fmq2qd3uthy",
|
||||
"x-async": false,
|
||||
"x-index": 2
|
||||
}
|
||||
},
|
||||
"x-uid": "mgsx7skkb50",
|
||||
"x-async": false,
|
||||
"x-index": 1
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"createdAt": "2023-12-04T09:42:52.953Z",
|
||||
"updatedAt": "2023-12-04T09:42:52.953Z",
|
||||
"appLang": null,
|
||||
"createdById": null,
|
||||
"email": "admin@nocobase.com",
|
||||
"id": 1,
|
||||
"nickname": "Super Admin",
|
||||
"phone": null,
|
||||
"systemSettings": {},
|
||||
"updatedById": null,
|
||||
"username": "nocobase"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"count": 1,
|
||||
"page": 1,
|
||||
"pageSize": 20,
|
||||
"totalPage": 1,
|
||||
"allowedActions": {
|
||||
"view": [1],
|
||||
"update": [1],
|
||||
"destroy": []
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
{
|
||||
"type": "void",
|
||||
"name": "root",
|
||||
"x-decorator": "DataBlockProviderV2",
|
||||
"x-use-decorator-props": "useTableDecoratorProps",
|
||||
"x-decorator-props": {
|
||||
"collection": "users",
|
||||
"action": "list",
|
||||
"params": {
|
||||
"pageSize": 20
|
||||
},
|
||||
"rowKey": "id"
|
||||
},
|
||||
"x-toolbar": "TableToolbar",
|
||||
"x-settings": "tableSettings",
|
||||
"x-component": "CardItem",
|
||||
"properties": {
|
||||
"ve197icvkz4": {
|
||||
"type": "array",
|
||||
"x-initializer": "tableColumnInitializer",
|
||||
"x-component": "NocoBaseTable",
|
||||
"x-use-component-props": "useTableProps",
|
||||
"ptw1didvz8u": {}
|
||||
}
|
||||
}
|
||||
}
|
@ -95,6 +95,35 @@
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"unique": true
|
||||
},
|
||||
{
|
||||
"key": "t09bauwm0wb",
|
||||
"name": "roles",
|
||||
"type": "belongsToMany",
|
||||
"interface": "m2m",
|
||||
"description": null,
|
||||
"collectionName": "users",
|
||||
"parentKey": null,
|
||||
"reverseKey": null,
|
||||
"target": "roles",
|
||||
"foreignKey": "userId",
|
||||
"otherKey": "roleName",
|
||||
"onDelete": "CASCADE",
|
||||
"sourceKey": "id",
|
||||
"targetKey": "name",
|
||||
"through": "rolesUsers",
|
||||
"uiSchema": {
|
||||
"type": "array",
|
||||
"title": "{{t(\"Roles\")}}",
|
||||
"x-component": "AssociationField",
|
||||
"x-component-props": {
|
||||
"multiple": true,
|
||||
"fieldNames": {
|
||||
"label": "title",
|
||||
"value": "name"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -43,8 +43,9 @@ Table 中的字段信息及列表数据,都是存储在数据库中的。
|
||||
|
||||
```tsx | pure
|
||||
const DataBlockProvider = (props) => {
|
||||
return <DataBlock.Provider value={props}>
|
||||
<CollectionProvider> / <AssociationProvider>
|
||||
return <DataBlockContextV2.Provider>
|
||||
<CollectionDataSourceProvider>
|
||||
<CollectionProvider> / <AssociationProvider>
|
||||
<BlockResourceProvider>
|
||||
<BlockRequestProvider>
|
||||
{action !== 'list' && <RecordProvider record={blocRequest.data}>
|
||||
@ -53,7 +54,8 @@ const DataBlockProvider = (props) => {
|
||||
</BlockRequestProvider>
|
||||
</BlockResourceProvider> / </AssociationProvider>
|
||||
</CollectionProvider>
|
||||
</DataBlock.Provider>
|
||||
</CollectionDataSourceProvider>
|
||||
</DataBlockContextV2.Provider>
|
||||
}
|
||||
```
|
||||
|
||||
@ -71,6 +73,7 @@ const DataBlockProvider = (props) => {
|
||||
'x-decorator': 'DataBlockProvider',
|
||||
'x-decorator-props': {
|
||||
collection: 'users',
|
||||
dataSource: 'main',
|
||||
action: 'list',
|
||||
tableProps: {
|
||||
bordered: true,
|
||||
@ -82,7 +85,7 @@ const DataBlockProvider = (props) => {
|
||||
|
||||
### 完整示例
|
||||
|
||||
<code src="./demos/data-block-provider/demo1.tsx"></code>
|
||||
<code src="./demos/data-block-provider/complete-demo.tsx"></code>
|
||||
|
||||
## 属性
|
||||
|
||||
@ -111,6 +114,7 @@ interface AllDataBlockProps {
|
||||
|
||||
- collection(`x-decorator-props`):区块的 collection 表名,用于获取区块的字段信息和区块数据
|
||||
- association(`x-decorator-props`):区块的关系字段名,用于获取区块的关系字段信息和关系字段数据
|
||||
- dataSource(`x-decorator-props`): 数据源,具体可参考 [Data Modeling](https://docs.nocobase.com/manual/data-modeling)
|
||||
- action(`x-decorator-props`):区块的请求类型,`list` 或 `get`
|
||||
- params(`x-decorator-props` 和 `x-use-decorator-props`):区块的请求参数,同时存在于
|
||||
- filterByTk(`x-use-decorator-props`):相当于 `params.filterByTk`,可理解为 `id`,用于获取单条数据
|
||||
@ -120,16 +124,18 @@ interface AllDataBlockProps {
|
||||
|
||||
```tsx | pure
|
||||
const DataBlockProvider = (props) => {
|
||||
return <DataBlock.Provider value={props}>
|
||||
<CollectionProvider name={props.collection}> / <CollectionProvider name={props.association}>
|
||||
<BlockResourceProvider {...props}>
|
||||
<BlockRequestProvider resource={resource}>
|
||||
{action !== 'list' && <RecordProvider record={blocRequest.data}>
|
||||
{props.children}
|
||||
</Record>}
|
||||
</BlockRequestProvider>
|
||||
</BlockResourceProvider>
|
||||
</CollectionProvider>
|
||||
return <DataBlockContextV2.Provider value={props}>
|
||||
<CollectionDataSourceProvider>
|
||||
<CollectionProvider name={props.collection}> / <CollectionProvider name={props.association}>
|
||||
<BlockResourceProvider {...props}>
|
||||
<BlockRequestProvider resource={resource}>
|
||||
{action !== 'list' && <RecordProvider record={blocRequest.data}>
|
||||
{props.children}
|
||||
</Record>}
|
||||
</BlockRequestProvider>
|
||||
</BlockResourceProvider>
|
||||
</CollectionProvider>
|
||||
</CollectionDataSourceProvider>
|
||||
</DataBlock.Provider>
|
||||
}
|
||||
```
|
||||
@ -212,18 +218,29 @@ const checked = props.tableProps.bordered;
|
||||
|
||||
<code src="./demos/data-block-provider/collection-table-list.tsx"></code>
|
||||
|
||||
#### Form get
|
||||
#### Form get & update
|
||||
|
||||
<code src="./demos/data-block-provider/collection-form-get-and-update.tsx"></code>
|
||||
|
||||
#### Form create
|
||||
|
||||
#### Form record
|
||||
<code src="./demos/data-block-provider/collection-form-create.tsx"></code>
|
||||
|
||||
#### Form record & update
|
||||
|
||||
<code src="./demos/data-block-provider/collection-form-record-and-update.tsx"></code>
|
||||
|
||||
### association
|
||||
|
||||
#### Table list
|
||||
association 与 collection 类似,只是需要提供 `sourceId`,我们以 `Table list` 为例。
|
||||
|
||||
#### Form get
|
||||
#### Table list & sourceId
|
||||
|
||||
#### Form create
|
||||
<code src="./demos/data-block-provider/association-table-list-and-source-id.tsx"></code>
|
||||
|
||||
#### Table list & parentRecord
|
||||
|
||||
如果不提供 `sourceId`,则需要提供 `parentRecord`,我们以 `Table list` 为例。
|
||||
|
||||
<code src="./demos/data-block-provider/association-table-list-and-parent-record.tsx"></code>
|
||||
|
||||
#### Form record
|
||||
|
@ -0,0 +1,98 @@
|
||||
import React from 'react';
|
||||
import { Select, Table, TableProps } from 'antd';
|
||||
import { SchemaComponent, UseDataBlockProps, useDataBlockRequestV2, withDynamicSchemaProps } from '@nocobase/client';
|
||||
import { ISchema } from '@formily/json-schema';
|
||||
|
||||
import { createApp } from '../../../collection/demos/createApp';
|
||||
import useUrlState from '@ahooksjs/use-url-state';
|
||||
|
||||
const collection = 'users';
|
||||
const associationField = 'roles';
|
||||
|
||||
const association = `${collection}.${associationField}`;
|
||||
const action = 'list';
|
||||
|
||||
const schema: ISchema = {
|
||||
type: 'void',
|
||||
name: 'root',
|
||||
'x-decorator': 'DataBlockProviderV2',
|
||||
'x-use-decorator-props': 'useBlockDecoratorProps',
|
||||
'x-decorator-props': {
|
||||
association,
|
||||
action,
|
||||
},
|
||||
'x-component': 'CardItem',
|
||||
properties: {
|
||||
demo: {
|
||||
type: 'array',
|
||||
'x-component': 'MyTable',
|
||||
'x-use-component-props': 'useTableProps',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const MyTable = withDynamicSchemaProps(Table);
|
||||
|
||||
function useTableProps(): TableProps<any> {
|
||||
const { data, loading } = useDataBlockRequestV2<any[]>();
|
||||
return {
|
||||
loading,
|
||||
dataSource: data?.data || [],
|
||||
columns: [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: 'Title',
|
||||
dataIndex: 'title',
|
||||
},
|
||||
{
|
||||
title: 'Description',
|
||||
dataIndex: 'description',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const useBlockDecoratorProps: UseDataBlockProps<'CollectionList'> = () => {
|
||||
const parentRecord = {
|
||||
id: 1,
|
||||
username: 'Tom',
|
||||
};
|
||||
return {
|
||||
parentRecord,
|
||||
};
|
||||
};
|
||||
|
||||
const Demo = () => {
|
||||
return <SchemaComponent schema={schema}></SchemaComponent>;
|
||||
};
|
||||
|
||||
const mocks = {
|
||||
[`${collection}/1/${associationField}:${action}`]: {
|
||||
data: [
|
||||
{
|
||||
name: 'admin',
|
||||
title: 'Admin',
|
||||
description: 'Admin description',
|
||||
},
|
||||
{
|
||||
name: 'developer',
|
||||
title: 'Developer',
|
||||
description: 'Developer description',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const Root = createApp(
|
||||
Demo,
|
||||
{
|
||||
components: { MyTable },
|
||||
scopes: { useTableProps, useBlockDecoratorProps },
|
||||
},
|
||||
mocks,
|
||||
);
|
||||
|
||||
export default Root;
|
@ -0,0 +1,132 @@
|
||||
import React from 'react';
|
||||
import { Select, Table, TableProps } from 'antd';
|
||||
import { SchemaComponent, UseDataBlockProps, useDataBlockRequestV2, withDynamicSchemaProps } from '@nocobase/client';
|
||||
import { ISchema } from '@formily/json-schema';
|
||||
|
||||
import { createApp } from '../../../collection/demos/createApp';
|
||||
import useUrlState from '@ahooksjs/use-url-state';
|
||||
|
||||
const collection = 'users';
|
||||
const associationField = 'roles';
|
||||
|
||||
const association = `${collection}.${associationField}`;
|
||||
const action = 'list';
|
||||
|
||||
const schema: ISchema = {
|
||||
type: 'void',
|
||||
name: 'root',
|
||||
'x-decorator': 'DataBlockProviderV2',
|
||||
'x-use-decorator-props': 'useBlockDecoratorProps',
|
||||
'x-decorator-props': {
|
||||
association,
|
||||
action,
|
||||
},
|
||||
'x-component': 'CardItem',
|
||||
properties: {
|
||||
demo: {
|
||||
type: 'array',
|
||||
'x-component': 'MyTable',
|
||||
'x-use-component-props': 'useTableProps',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const MyTable = withDynamicSchemaProps(Table);
|
||||
|
||||
function useTableProps(): TableProps<any> {
|
||||
const { data, loading } = useDataBlockRequestV2<any[]>();
|
||||
return {
|
||||
loading,
|
||||
dataSource: data?.data || [],
|
||||
columns: [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: 'Title',
|
||||
dataIndex: 'title',
|
||||
},
|
||||
{
|
||||
title: 'Description',
|
||||
dataIndex: 'description',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const useBlockDecoratorProps: UseDataBlockProps<'CollectionList'> = () => {
|
||||
const [state] = useUrlState({ userId: '1' });
|
||||
return {
|
||||
sourceId: state.userId,
|
||||
};
|
||||
};
|
||||
|
||||
const Demo = () => {
|
||||
const [state, setState] = useUrlState({ userId: '1' });
|
||||
return (
|
||||
<>
|
||||
<Select
|
||||
defaultValue={state.userId}
|
||||
options={[
|
||||
{ key: 1, value: '1', label: 'Tom' },
|
||||
{ key: 2, value: '2', label: 'Jack' },
|
||||
]}
|
||||
onChange={(v) => {
|
||||
setState({ userId: v });
|
||||
}}
|
||||
></Select>
|
||||
<SchemaComponent schema={schema}></SchemaComponent>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const mocks = {
|
||||
[`${collection}/1/${associationField}:${action}`]: {
|
||||
data: [
|
||||
{
|
||||
name: 'admin',
|
||||
title: 'Admin',
|
||||
description: 'Admin description',
|
||||
},
|
||||
{
|
||||
name: 'developer',
|
||||
title: 'Developer',
|
||||
description: 'Developer description',
|
||||
},
|
||||
],
|
||||
},
|
||||
[`${collection}/2/${associationField}:${action}`]: {
|
||||
data: [
|
||||
{
|
||||
name: 'developer',
|
||||
title: 'Developer',
|
||||
description: 'Developer description',
|
||||
},
|
||||
{
|
||||
name: 'tester',
|
||||
title: 'Tester',
|
||||
description: 'Tester description',
|
||||
},
|
||||
],
|
||||
},
|
||||
[`${collection}:get/1`]: {
|
||||
id: 1,
|
||||
username: 'Tom',
|
||||
},
|
||||
[`${collection}:get/2`]: {
|
||||
id: 1,
|
||||
username: 'Jack',
|
||||
},
|
||||
};
|
||||
|
||||
const Root = createApp(
|
||||
Demo,
|
||||
{
|
||||
components: { MyTable },
|
||||
scopes: { useTableProps, useBlockDecoratorProps },
|
||||
},
|
||||
mocks,
|
||||
);
|
||||
|
||||
export default Root;
|
@ -1,8 +1,9 @@
|
||||
import React, { FC } from 'react';
|
||||
import { SchemaComponent, useDataBlockResourceV2, withSchemaComponentProps } from '@nocobase/client';
|
||||
import { createApp } from './createApp';
|
||||
import { Button, Form, Input, InputNumber } from 'antd';
|
||||
import { FormProps } from 'antd/lib';
|
||||
import { Button, Form, FormProps, Input, InputNumber, notification } from 'antd';
|
||||
import { SchemaComponent, useDataBlockResourceV2, withDynamicSchemaProps } from '@nocobase/client';
|
||||
import { ISchema } from '@formily/json-schema';
|
||||
|
||||
import { createApp } from '../../../collection/demos/createApp';
|
||||
|
||||
interface DemoFormFieldType {
|
||||
id: number;
|
||||
@ -10,7 +11,7 @@ interface DemoFormFieldType {
|
||||
age: number;
|
||||
}
|
||||
type DemoFormProps = FormProps<DemoFormFieldType>;
|
||||
const DemoForm: FC<DemoFormProps> = withSchemaComponentProps((props) => {
|
||||
const DemoForm: FC<DemoFormProps> = withDynamicSchemaProps((props) => {
|
||||
return (
|
||||
<Form labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} style={{ maxWidth: 600 }} autoComplete="off" {...props}>
|
||||
<Form.Item<DemoFormFieldType>
|
||||
@ -38,24 +39,38 @@ const DemoForm: FC<DemoFormProps> = withSchemaComponentProps((props) => {
|
||||
|
||||
function useDemoFormProps(): DemoFormProps {
|
||||
const resource = useDataBlockResourceV2();
|
||||
const onFinish = async (values: DemoFormFieldType) => {
|
||||
console.log('values', values);
|
||||
await resource.create({
|
||||
values,
|
||||
});
|
||||
notification.success({
|
||||
message: 'Save successfully!',
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onFinish: (values) => {
|
||||
resource.create({ values });
|
||||
},
|
||||
onFinish,
|
||||
};
|
||||
}
|
||||
|
||||
const collection = 'users';
|
||||
|
||||
const schema = {
|
||||
const schema: ISchema = {
|
||||
type: 'void',
|
||||
name: 'hello',
|
||||
name: 'root',
|
||||
'x-decorator': 'DataBlockProviderV2',
|
||||
'x-component': 'DemoForm',
|
||||
'x-use-component-props': 'useDemoFormProps',
|
||||
'x-decorator-props': {
|
||||
collection: collection,
|
||||
},
|
||||
'x-component': 'CardItem',
|
||||
properties: {
|
||||
demo: {
|
||||
type: 'object',
|
||||
'x-component': 'DemoForm',
|
||||
'x-use-component-props': 'useDemoFormProps',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Demo = () => {
|
||||
@ -64,11 +79,17 @@ const Demo = () => {
|
||||
|
||||
const mocks = {
|
||||
[`${collection}:create`]: (config) => {
|
||||
console.log('请求结果', config.data);
|
||||
console.log('config.data', config.data);
|
||||
return [200, { msg: 'ok' }];
|
||||
},
|
||||
};
|
||||
|
||||
const Root = createApp(Demo, { components: { DemoForm }, scopes: { useDemoFormProps } }, mocks);
|
||||
const Root = createApp(
|
||||
Demo,
|
||||
{
|
||||
components: { DemoForm },
|
||||
scopes: { useDemoFormProps },
|
||||
},
|
||||
mocks,
|
||||
);
|
||||
|
||||
export default Root;
|
@ -1,15 +1,16 @@
|
||||
import React, { FC, useEffect, useState } from 'react';
|
||||
import React, { FC, useEffect } from 'react';
|
||||
import { Button, Form, FormProps, Input, InputNumber, Select, notification } from 'antd';
|
||||
import {
|
||||
RecordProviderV2,
|
||||
SchemaComponent,
|
||||
useDataBlockRequestV2,
|
||||
withSchemaComponentProps,
|
||||
UseDataBlockProps,
|
||||
useDataBlockResourceV2,
|
||||
useRecordDataV2,
|
||||
withDynamicSchemaProps,
|
||||
} from '@nocobase/client';
|
||||
import { createApp } from './createApp';
|
||||
import { Button, Form, Input, InputNumber, Select } from 'antd';
|
||||
import { FormProps } from 'antd/lib';
|
||||
import { ISchema } from '@formily/json-schema';
|
||||
import useUrlState from '@ahooksjs/use-url-state';
|
||||
|
||||
import { createApp } from '../../../collection/demos/createApp';
|
||||
|
||||
interface DemoFormFieldType {
|
||||
id: number;
|
||||
@ -17,7 +18,7 @@ interface DemoFormFieldType {
|
||||
age: number;
|
||||
}
|
||||
type DemoFormProps = FormProps<DemoFormFieldType>;
|
||||
const DemoForm: FC<DemoFormProps> = withSchemaComponentProps((props) => {
|
||||
const DemoForm: FC<DemoFormProps> = withDynamicSchemaProps((props) => {
|
||||
return (
|
||||
<Form labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} style={{ maxWidth: 600 }} autoComplete="off" {...props}>
|
||||
<Form.Item<DemoFormFieldType>
|
||||
@ -45,56 +46,78 @@ const DemoForm: FC<DemoFormProps> = withSchemaComponentProps((props) => {
|
||||
|
||||
function useDemoFormProps(): DemoFormProps {
|
||||
const data = useRecordDataV2<DemoFormFieldType>();
|
||||
const resource = useDataBlockResourceV2();
|
||||
|
||||
const [form] = Form.useForm();
|
||||
|
||||
useEffect(() => {
|
||||
form.setFieldsValue(data);
|
||||
}, [data, form]);
|
||||
|
||||
const onFinish = async (values: DemoFormFieldType) => {
|
||||
console.log('values', values);
|
||||
await resource.update({
|
||||
filterByTk: data.id,
|
||||
values,
|
||||
});
|
||||
notification.success({
|
||||
message: 'Save successfully!',
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
initialValues: data,
|
||||
preserve: true,
|
||||
onFinish,
|
||||
form,
|
||||
};
|
||||
}
|
||||
|
||||
const useFormBlockDecoratorProps: UseDataBlockProps<'CollectionGet'> = () => {
|
||||
const { filterByTk } = useRecordDataV2<{ filterByTk: number }>();
|
||||
const useBlockDecoratorProps: UseDataBlockProps<'CollectionGet'> = () => {
|
||||
const [state] = useUrlState({ id: '1' });
|
||||
return {
|
||||
filterByTk,
|
||||
filterByTk: state.id,
|
||||
};
|
||||
};
|
||||
|
||||
const collection = 'users';
|
||||
const action = 'get';
|
||||
|
||||
const schema = {
|
||||
const schema: ISchema = {
|
||||
type: 'void',
|
||||
name: 'hello',
|
||||
name: 'root',
|
||||
'x-decorator': 'DataBlockProviderV2',
|
||||
'x-use-decorator-props': 'useFormBlockDecoratorProps',
|
||||
'x-component': 'DemoForm',
|
||||
'x-use-component-props': 'useDemoFormProps',
|
||||
'x-use-decorator-props': 'useBlockDecoratorProps',
|
||||
'x-decorator-props': {
|
||||
collection: collection,
|
||||
action: action,
|
||||
},
|
||||
'x-component': 'CardItem',
|
||||
properties: {
|
||||
demo: {
|
||||
type: 'object',
|
||||
'x-component': 'DemoForm',
|
||||
'x-use-component-props': 'useDemoFormProps',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Demo = () => {
|
||||
const [id, setId] = useState(1);
|
||||
const [state, setState] = useUrlState({ id: '1' });
|
||||
|
||||
return (
|
||||
<RecordProviderV2 record={{ filterByTk: id }}>
|
||||
<>
|
||||
<Select
|
||||
defaultValue={id}
|
||||
defaultValue={state.id}
|
||||
options={[
|
||||
{ key: 1, value: 1, label: 'Bamboo' },
|
||||
{ key: 2, value: 2, label: 'Mary' },
|
||||
{ key: 1, value: '1', label: 'Bamboo' },
|
||||
{ key: 2, value: '2', label: 'Mary' },
|
||||
]}
|
||||
onChange={(v) => {
|
||||
setId(v);
|
||||
setState({ id: v });
|
||||
}}
|
||||
></Select>
|
||||
<SchemaComponent schema={schema}></SchemaComponent>
|
||||
</RecordProviderV2>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -103,7 +126,7 @@ const mocks = {
|
||||
const { filterByTk } = config.params;
|
||||
return {
|
||||
data:
|
||||
filterByTk === 1
|
||||
Number(filterByTk) === 1
|
||||
? {
|
||||
id: 1,
|
||||
username: 'Bamboo',
|
||||
@ -116,11 +139,19 @@ const mocks = {
|
||||
},
|
||||
};
|
||||
},
|
||||
[`${collection}:update`]: function (config) {
|
||||
console.log('config.data', config.data);
|
||||
return {
|
||||
data: 'ok',
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const Root = createApp(
|
||||
Demo,
|
||||
{ components: { DemoForm }, scopes: { useDemoFormProps, useFormBlockDecoratorProps } },
|
||||
{
|
||||
components: { DemoForm },
|
||||
scopes: { useDemoFormProps, useBlockDecoratorProps },
|
||||
},
|
||||
mocks,
|
||||
);
|
||||
|
@ -1,15 +1,16 @@
|
||||
import React, { FC, useEffect } from 'react';
|
||||
import { Button, Form, FormProps, Input, InputNumber, notification } from 'antd';
|
||||
import {
|
||||
RecordProviderV2,
|
||||
SchemaComponent,
|
||||
UseDataBlockProps,
|
||||
useDataBlockResourceV2,
|
||||
useRecordDataV2,
|
||||
useRecordV2,
|
||||
withSchemaComponentProps,
|
||||
withDynamicSchemaProps,
|
||||
} from '@nocobase/client';
|
||||
import { Button, Form, Input, InputNumber } from 'antd';
|
||||
import { FormProps } from 'antd/lib';
|
||||
import React, { FC, useEffect } from 'react';
|
||||
import { createApp } from './createApp';
|
||||
import { ISchema } from '@formily/json-schema';
|
||||
|
||||
import { createApp } from '../../../collection/demos/createApp';
|
||||
|
||||
interface DemoFormFieldType {
|
||||
id: number;
|
||||
@ -17,7 +18,7 @@ interface DemoFormFieldType {
|
||||
age: number;
|
||||
}
|
||||
type DemoFormProps = FormProps<DemoFormFieldType>;
|
||||
const DemoForm: FC<DemoFormProps> = withSchemaComponentProps((props) => {
|
||||
const DemoForm: FC<DemoFormProps> = withDynamicSchemaProps((props) => {
|
||||
return (
|
||||
<Form labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} style={{ maxWidth: 600 }} autoComplete="off" {...props}>
|
||||
<Form.Item<DemoFormFieldType>
|
||||
@ -45,51 +46,92 @@ const DemoForm: FC<DemoFormProps> = withSchemaComponentProps((props) => {
|
||||
|
||||
function useDemoFormProps(): DemoFormProps {
|
||||
const data = useRecordDataV2<DemoFormFieldType>();
|
||||
const resource = useDataBlockResourceV2();
|
||||
|
||||
const [form] = Form.useForm();
|
||||
|
||||
useEffect(() => {
|
||||
form.setFieldsValue(data);
|
||||
}, [data, form]);
|
||||
|
||||
const onFinish = async (values: DemoFormFieldType) => {
|
||||
console.log('values', values);
|
||||
await resource.update({
|
||||
filterByTk: data.id,
|
||||
values,
|
||||
});
|
||||
notification.success({
|
||||
message: 'Save successfully!',
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
initialValues: data,
|
||||
preserve: true,
|
||||
onFinish,
|
||||
form,
|
||||
};
|
||||
}
|
||||
|
||||
const useFormBlockDecoratorProps: UseDataBlockProps<'CollectionRecord'> = () => {
|
||||
const record = useRecordV2();
|
||||
const record = useRecordDataV2();
|
||||
return {
|
||||
record,
|
||||
};
|
||||
};
|
||||
|
||||
const collection = 'users';
|
||||
const action = 'get';
|
||||
|
||||
const schema = {
|
||||
const schema: ISchema = {
|
||||
type: 'void',
|
||||
name: 'hello',
|
||||
name: 'root',
|
||||
'x-decorator': 'DataBlockProviderV2',
|
||||
'x-use-decorator-props': 'useFormBlockDecoratorProps',
|
||||
'x-component': 'DemoForm',
|
||||
'x-use-component-props': 'useDemoFormProps',
|
||||
'x-decorator-props': {
|
||||
collection: collection,
|
||||
action: action,
|
||||
},
|
||||
'x-component': 'CardItem',
|
||||
properties: {
|
||||
demo: {
|
||||
type: 'object',
|
||||
'x-component': 'DemoForm',
|
||||
'x-use-component-props': 'useDemoFormProps',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const recordData = {
|
||||
id: 1,
|
||||
username: 'Bamboo',
|
||||
age: 18,
|
||||
};
|
||||
|
||||
const Demo = () => {
|
||||
return (
|
||||
<RecordProviderV2
|
||||
record={{
|
||||
id: 1,
|
||||
username: 'Bamboo',
|
||||
age: 18,
|
||||
}}
|
||||
>
|
||||
<SchemaComponent schema={schema}></SchemaComponent>
|
||||
</RecordProviderV2>
|
||||
<>
|
||||
<RecordProviderV2 record={recordData}>
|
||||
<SchemaComponent schema={schema}></SchemaComponent>
|
||||
</RecordProviderV2>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Root = createApp(Demo, { components: { DemoForm }, scopes: { useDemoFormProps, useFormBlockDecoratorProps } });
|
||||
const mocks = {
|
||||
[`${collection}:update`]: function (config) {
|
||||
console.log('config.data', config.data);
|
||||
return {
|
||||
data: 'ok',
|
||||
};
|
||||
},
|
||||
};
|
||||
const Root = createApp(
|
||||
Demo,
|
||||
{
|
||||
components: { DemoForm },
|
||||
scopes: { useDemoFormProps, useFormBlockDecoratorProps },
|
||||
},
|
||||
mocks,
|
||||
);
|
||||
|
||||
export default Root;
|
@ -221,7 +221,9 @@ export class CollectionV2 {
|
||||
}
|
||||
getField(name: SchemaKey) {
|
||||
const fieldsMap = this.getFieldsMap();
|
||||
|
||||
if (typeof name === 'string' && name.startsWith(`${this.name}.`)) {
|
||||
name = name.replace(`${this.name}.`, '');
|
||||
}
|
||||
if (String(name).split('.').length > 1) {
|
||||
const [fieldName, ...others] = String(name).split('.');
|
||||
const field = fieldsMap[fieldName];
|
||||
|
@ -61,7 +61,7 @@ export function useRecordV2<DataType = {}, ParentDataType = {}>(
|
||||
return context;
|
||||
}
|
||||
|
||||
export function useRecordDataV2<DataType>(showErrorWhenNotExists = true): DataType {
|
||||
export function useRecordDataV2<DataType = any>(showErrorWhenNotExists = true): DataType {
|
||||
const record = useRecordV2<DataType>(showErrorWhenNotExists);
|
||||
return record.data;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ export interface AllDataBlockProps {
|
||||
collection: string;
|
||||
association: string;
|
||||
dataSource?: string;
|
||||
sourceId: string | number;
|
||||
sourceId?: string | number;
|
||||
filterByTk: string | number;
|
||||
record: RecordV2;
|
||||
action?: 'list' | 'get';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useDeepCompareEffect } from 'ahooks';
|
||||
import React, { FC, createContext, useContext } from 'react';
|
||||
import React, { FC, createContext, useContext, useEffect } from 'react';
|
||||
|
||||
import { UseRequestResult, useAPIClient, useRequest } from '../../api-client';
|
||||
import { useDataBlockResourceV2 } from './DataBlockResourceProvider';
|
||||
@ -35,25 +35,33 @@ function useCurrentRequest<T>(options: Omit<AllDataBlockProps, 'type'>) {
|
||||
|
||||
// 因为修改 Schema 会导致 params 对象发生变化,所以这里使用 `DeepCompare`
|
||||
useDeepCompareEffect(() => {
|
||||
request.run();
|
||||
if (action) {
|
||||
request.run();
|
||||
}
|
||||
}, [params, action, record]);
|
||||
|
||||
useEffect(() => {
|
||||
if (action) {
|
||||
request.run();
|
||||
}
|
||||
}, [resource]);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
function useParentRequest<T>(options: Omit<AllDataBlockProps, 'type'>) {
|
||||
const { sourceId, association, parentRecord } = options;
|
||||
const api = useAPIClient();
|
||||
|
||||
return useRequest<T>(
|
||||
() => {
|
||||
async () => {
|
||||
if (parentRecord) return Promise.resolve({ data: parentRecord });
|
||||
if (!association) return Promise.resolve({ data: undefined });
|
||||
// "association": "Collection.Field"
|
||||
const arr = association.split('.');
|
||||
// <collection>:get/<filterByTk>
|
||||
const url = `${arr[0]}:get/${sourceId}`;
|
||||
return api.request({ url }).then((res) => res.data);
|
||||
const res = await api.request({ url });
|
||||
return res.data;
|
||||
},
|
||||
{
|
||||
refreshDeps: [association, parentRecord, sourceId],
|
||||
|
@ -3,7 +3,7 @@ import { IResource } from '@nocobase/sdk';
|
||||
|
||||
import { useAPIClient } from '../../api-client';
|
||||
import { useDataBlockPropsV2 } from './DataBlockProvider';
|
||||
import { DEFAULT_DATA_SOURCE_NAME, useCollectionManagerV2 } from '../collection';
|
||||
import { DEFAULT_DATA_SOURCE_NAME, RecordV2, useCollectionManagerV2 } from '../collection';
|
||||
|
||||
export const DataBlockResourceContextV2 = createContext<IResource>(null);
|
||||
DataBlockResourceContextV2.displayName = 'DataBlockResourceContextV2';
|
||||
@ -26,7 +26,8 @@ export const DataBlockResourceProviderV2: FC<{ children?: ReactNode }> = ({ chil
|
||||
if (association && parentRecord) {
|
||||
const associationCollection = cm.getCollection(association);
|
||||
if (associationCollection) {
|
||||
return parentRecord.data[associationCollection.sourceKey || 'id'];
|
||||
const parentRecordData = parentRecord instanceof RecordV2 ? parentRecord.data : parentRecord;
|
||||
return parentRecordData[associationCollection.sourceKey || 'id'];
|
||||
}
|
||||
}
|
||||
}, [sourceId, parentRecord]);
|
||||
|
Loading…
Reference in New Issue
Block a user