perf(users): optimize performance for rendering the user management table (#5276)
Some checks failed
auto-merge / push-commit (push) Has been cancelled
Build Docker Image / build-and-push (push) Has been cancelled
Build Pro Image / build-and-push (push) Has been cancelled
E2E / Build (push) Has been cancelled
NocoBase Backend Test / sqlite-test (20, false) (push) Has been cancelled
NocoBase Backend Test / sqlite-test (20, true) (push) Has been cancelled
NocoBase Backend Test / postgres-test (public, 20, nocobase, false) (push) Has been cancelled
NocoBase Backend Test / postgres-test (public, 20, nocobase, true) (push) Has been cancelled
NocoBase Backend Test / postgres-test (public, 20, public, false) (push) Has been cancelled
NocoBase Backend Test / postgres-test (public, 20, public, true) (push) Has been cancelled
NocoBase Backend Test / postgres-test (user_schema, 20, nocobase, false) (push) Has been cancelled
NocoBase Backend Test / postgres-test (user_schema, 20, nocobase, true) (push) Has been cancelled
NocoBase Backend Test / postgres-test (user_schema, 20, public, false) (push) Has been cancelled
NocoBase Backend Test / postgres-test (user_schema, 20, public, true) (push) Has been cancelled
NocoBase Backend Test / mysql-test (20, false) (push) Has been cancelled
NocoBase Backend Test / mysql-test (20, true) (push) Has been cancelled
NocoBase Backend Test / mariadb-test (20, false) (push) Has been cancelled
NocoBase Backend Test / mariadb-test (20, true) (push) Has been cancelled
NocoBase FrontEnd Test / frontend-test (18) (push) Has been cancelled
Test on Windows / build (push) Has been cancelled
E2E / Core and plugins (push) Has been cancelled
E2E / plugin-workflow (push) Has been cancelled
E2E / plugin-workflow-approval (push) Has been cancelled
E2E / plugin-data-source-main (push) Has been cancelled
E2E / Comment on PR (push) Has been cancelled

* perf(users): optimize render performance of the user management table

* fix: bug
This commit is contained in:
YANG QIA 2024-09-15 20:49:18 +08:00 committed by GitHub
parent 00301815d5
commit f032c732f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 131 additions and 70 deletions

View File

@ -7,27 +7,88 @@
* For more information, please refer to: https://www.nocobase.com/agreement. * For more information, please refer to: https://www.nocobase.com/agreement.
*/ */
import { SchemaComponent, SchemaComponentContext, useSchemaComponentContext } from '@nocobase/client'; import {
import React from 'react'; SchemaComponent,
SchemaComponentContext,
useActionContext,
useCollection,
useCollectionRecordData,
useDataBlockRequest,
useDataBlockResource,
useSchemaComponentContext,
} from '@nocobase/client';
import React, { useEffect, useMemo } from 'react';
import { usersSchema } from './schemas/users'; import { usersSchema } from './schemas/users';
import { Card } from 'antd';
import { UserRolesField } from './UserRolesField';
import { useUsersTranslation } from './locale'; import { useUsersTranslation } from './locale';
import { useFilterActionProps } from './hooks';
import { PasswordField } from './PasswordField'; import { PasswordField } from './PasswordField';
import { App } from 'antd';
import { useForm } from '@formily/react';
import { createForm } from '@formily/core';
const useCancelActionProps = () => {
const { setVisible } = useActionContext();
return {
type: 'default',
onClick() {
setVisible(false);
},
};
};
const useSubmitActionProps = () => {
const { setVisible } = useActionContext();
const { message } = App.useApp();
const form = useForm();
const resource = useDataBlockResource();
const { runAsync } = useDataBlockRequest();
const { t } = useUsersTranslation();
const collection = useCollection();
return {
type: 'primary',
async onClick() {
await form.submit();
const values = form.values;
console.log('values:', values);
if (values[collection.filterTargetKey]) {
await resource.update({
values,
filterByTk: values[collection.filterTargetKey],
});
} else {
await resource.create({ values });
}
await runAsync();
message.success(t('Saved successfully'));
setVisible(false);
},
};
};
const useEditFormProps = () => {
const recordData = useCollectionRecordData();
const form = useMemo(
() =>
createForm({
initialValues: recordData,
}),
[recordData],
);
return {
form,
};
};
export const UsersManagement: React.FC = () => { export const UsersManagement: React.FC = () => {
const { t } = useUsersTranslation(); const { t } = useUsersTranslation();
const scCtx = useSchemaComponentContext(); const scCtx = useSchemaComponentContext();
return ( return (
<SchemaComponentContext.Provider value={{ ...scCtx, designable: false }}> <SchemaComponentContext.Provider value={{ ...scCtx, designable: false }}>
<Card>
<SchemaComponent <SchemaComponent
schema={usersSchema} schema={usersSchema}
scope={{ t, useFilterActionProps }} scope={{ t, useCancelActionProps, useSubmitActionProps, useEditFormProps }}
components={{ UserRolesField, PasswordField }} components={{ PasswordField }}
/> />
</Card>
</SchemaComponentContext.Provider> </SchemaComponentContext.Provider>
); );
}; };

View File

@ -132,26 +132,28 @@ export const usersSchema: ISchema = {
properties: { properties: {
block1: { block1: {
type: 'void', type: 'void',
'x-decorator': 'ResourceActionProvider', 'x-component': 'CardItem',
'x-component-props': {
heightMode: 'fullHeight',
},
'x-decorator': 'TableBlockProvider',
'x-decorator-props': { 'x-decorator-props': {
collection: userCollection, dataSource: 'main',
resourceName: 'users', rowKey: 'id',
request: { collection: 'users',
resource: 'users',
action: 'list', action: 'list',
params: { params: {
pageSize: 50, pageSize: 20,
appends: [],
},
}, },
}, },
'x-use-decorator-props': 'useTableBlockDecoratorProps',
properties: { properties: {
actions: { actions: {
type: 'void', type: 'void',
'x-component': 'ActionBar', 'x-component': 'ActionBar',
'x-component-props': { 'x-component-props': {
style: { style: {
marginBottom: 16, marginBottom: 'var(--nb-spacing)',
}, },
}, },
properties: { properties: {
@ -166,18 +168,28 @@ export const usersSchema: ISchema = {
}, },
'x-align': 'left', 'x-align': 'left',
}, },
refresh: {
type: 'void',
title: '{{ t("Refresh") }}',
'x-action': 'refresh',
'x-component': 'Action',
'x-component-props': {
icon: 'ReloadOutlined',
},
'x-use-component-props': 'useRefreshActionProps',
},
delete: { delete: {
type: 'void', type: 'void',
title: '{{ t("Delete") }}', title: '{{ t("Delete") }}',
'x-component': 'Action', 'x-component': 'Action',
'x-component-props': { 'x-component-props': {
useAction: '{{ cm.useBulkDestroyAction }}',
confirm: { confirm: {
title: "{{t('Delete users')}}", title: "{{t('Delete users')}}",
content: "{{t('Are you sure you want to delete it?')}}", content: "{{t('Are you sure you want to delete it?')}}",
}, },
icon: 'DeleteOutlined', icon: 'DeleteOutlined',
}, },
'x-use-component-props': 'useBulkDestroyActionProps',
}, },
create: { create: {
type: 'void', type: 'void',
@ -191,7 +203,7 @@ export const usersSchema: ISchema = {
drawer: { drawer: {
type: 'void', type: 'void',
'x-component': 'Action.Drawer', 'x-component': 'Action.Drawer',
'x-decorator': 'Form', 'x-decorator': 'FormV2',
title: '{{t("Add user")}}', title: '{{t("Add user")}}',
properties: { properties: {
nickname: { nickname: {
@ -232,17 +244,15 @@ export const usersSchema: ISchema = {
cancel: { cancel: {
title: '{{t("Cancel")}}', title: '{{t("Cancel")}}',
'x-component': 'Action', 'x-component': 'Action',
'x-component-props': { 'x-use-component-props': 'useCancelActionProps',
useAction: '{{ cm.useCancelAction }}',
},
}, },
submit: { submit: {
title: '{{t("Submit")}}', title: '{{t("Submit")}}',
'x-component': 'Action', 'x-component': 'Action',
'x-component-props': { 'x-component-props': {
type: 'primary', type: 'primary',
useAction: '{{ cm.useCreateAction }}',
}, },
'x-use-component-props': 'useSubmitActionProps',
}, },
}, },
}, },
@ -253,21 +263,21 @@ export const usersSchema: ISchema = {
}, },
}, },
table: { table: {
type: 'void', type: 'array',
'x-uid': 'input', 'x-uid': 'input',
'x-component': 'Table.Void', 'x-component': 'TableV2',
'x-component-props': { 'x-component-props': {
rowKey: 'id', rowKey: 'id',
rowSelection: { rowSelection: {
type: 'checkbox', type: 'checkbox',
}, },
useDataSource: '{{ cm.useDataSourceFromRAC }}',
}, },
'x-use-component-props': 'useTableBlockProps',
properties: { properties: {
column1: { column1: {
type: 'void', type: 'void',
'x-decorator': 'Table.Column.Decorator', 'x-decorator': 'TableV2.Column.Decorator',
'x-component': 'Table.Column', 'x-component': 'TableV2.Column',
properties: { properties: {
nickname: { nickname: {
type: 'number', type: 'number',
@ -278,8 +288,8 @@ export const usersSchema: ISchema = {
}, },
column2: { column2: {
type: 'void', type: 'void',
'x-decorator': 'Table.Column.Decorator', 'x-decorator': 'TableV2.Column.Decorator',
'x-component': 'Table.Column', 'x-component': 'TableV2.Column',
properties: { properties: {
username: { username: {
type: 'string', type: 'string',
@ -290,8 +300,8 @@ export const usersSchema: ISchema = {
}, },
column3: { column3: {
type: 'void', type: 'void',
'x-decorator': 'Table.Column.Decorator', 'x-decorator': 'TableV2.Column.Decorator',
'x-component': 'Table.Column', 'x-component': 'TableV2.Column',
properties: { properties: {
email: { email: {
type: 'string', type: 'string',
@ -302,20 +312,28 @@ export const usersSchema: ISchema = {
}, },
column4: { column4: {
type: 'void', type: 'void',
'x-decorator': 'Table.Column.Decorator', 'x-decorator': 'TableV2.Column.Decorator',
'x-component': 'Table.Column', 'x-component': 'TableV2.Column',
title: '{{t("Roles")}}', title: '{{t("Roles")}}',
properties: { properties: {
roles: { roles: {
type: 'array', type: 'array',
'x-component': 'UserRolesField', 'x-component': 'CollectionField',
'x-component-props': {
mode: 'Tag',
enableLink: false,
ellipsis: true,
},
'x-read-pretty': true,
'x-collection-field': 'users.roles',
}, },
}, },
}, },
column5: { column5: {
type: 'void', type: 'void',
title: '{{t("Actions")}}', title: '{{t("Actions")}}',
'x-component': 'Table.Column', 'x-component': 'TableV2.Column',
'x-decorator': 'TableV2.Column.ActionBar',
properties: { properties: {
actions: { actions: {
type: 'void', type: 'void',
@ -337,23 +355,8 @@ export const usersSchema: ISchema = {
drawer: { drawer: {
type: 'void', type: 'void',
'x-component': 'Action.Drawer', 'x-component': 'Action.Drawer',
'x-decorator': 'Form', 'x-decorator': 'FormV2',
'x-decorator-props': { 'x-use-decorator-props': 'useEditFormProps',
useValues: (options) => {
const record = useRecord();
const result = useRequest(() => Promise.resolve({ data: record }), {
...options,
manual: true,
});
const ctx = useActionContext();
useEffect(() => {
if (ctx.visible) {
result.run();
}
}, [ctx.visible]);
return result;
},
},
title: '{{t("Edit profile")}}', title: '{{t("Edit profile")}}',
properties: { properties: {
nickname: { nickname: {
@ -389,17 +392,15 @@ export const usersSchema: ISchema = {
cancel: { cancel: {
title: '{{t("Cancel")}}', title: '{{t("Cancel")}}',
'x-component': 'Action', 'x-component': 'Action',
'x-component-props': { 'x-use-component-props': 'useCancelActionProps',
useAction: '{{ cm.useCancelAction }}',
},
}, },
submit: { submit: {
title: '{{t("Submit")}}', title: '{{t("Submit")}}',
'x-component': 'Action', 'x-component': 'Action',
'x-component-props': { 'x-component-props': {
type: 'primary', type: 'primary',
useAction: '{{ cm.useUpdateAction }}',
}, },
'x-use-component-props': 'useSubmitActionProps',
}, },
}, },
}, },
@ -420,7 +421,8 @@ export const usersSchema: ISchema = {
drawer: { drawer: {
type: 'void', type: 'void',
'x-component': 'Action.Drawer', 'x-component': 'Action.Drawer',
'x-decorator': 'Form', 'x-decorator': 'FormV2',
'x-use-decorator-props': 'useEditFormProps',
title: '{{t("Change password")}}', title: '{{t("Change password")}}',
properties: { properties: {
password: { password: {
@ -438,17 +440,15 @@ export const usersSchema: ISchema = {
cancel: { cancel: {
title: '{{t("Cancel")}}', title: '{{t("Cancel")}}',
'x-component': 'Action', 'x-component': 'Action',
'x-component-props': { 'x-use-component-props': 'useCancelActionProps',
useAction: '{{ cm.useCancelAction }}',
},
}, },
submit: { submit: {
title: '{{t("Submit")}}', title: '{{t("Submit")}}',
'x-component': 'Action', 'x-component': 'Action',
'x-component-props': { 'x-component-props': {
type: 'primary', type: 'primary',
useAction: '{{ cm.useUpdateAction }}',
}, },
'x-use-component-props': 'useSubmitActionProps',
}, },
}, },
}, },
@ -468,8 +468,8 @@ export const usersSchema: ISchema = {
title: "{{t('Delete')}}", title: "{{t('Delete')}}",
content: "{{t('Are you sure you want to delete it?')}}", content: "{{t('Are you sure you want to delete it?')}}",
}, },
useAction: '{{cm.useDestroyAction}}',
}, },
'x-use-component-props': 'useDestroyActionProps',
}, },
}, },
}, },

View File

@ -48,7 +48,7 @@ export const listExcludeRole = async (ctx: Context, next: Next) => {
const [rows, count] = await repo.findAndCount({ const [rows, count] = await repo.findAndCount({
context: ctx, context: ctx,
offset: (page - 1) * pageSize, offset: (page - 1) * pageSize,
limit: pageSize, limit: +pageSize,
filter, filter,
}); });
ctx.body = { ctx.body = {