add action log tempalte (#239)

* feat: add action log tempalte

* feat: add action log tempalte

* refactor: refactor action log

* fix: fix translation

* fix: modify translation

* fix: modify Action Log

Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
SemmyWong 2022-03-28 15:38:29 +08:00 committed by GitHub
parent e932b993a4
commit c0a33b6e3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 910 additions and 14 deletions

View File

@ -0,0 +1,86 @@
import { ISchema, useField, useFieldSchema } from '@formily/react';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useCollection, useResourceActionContext } from '../collection-manager';
import { useCollectionFilterOptions } from '../collection-manager/action-hooks';
import { useDesignable } from '../schema-component/hooks';
import { GeneralSchemaDesigner, SchemaSettings } from '../schema-settings';
export const ActionLogDesigner = () => {
const { name, title } = useCollection();
const field = useField();
const fieldSchema = useFieldSchema();
const dataSource = useCollectionFilterOptions(name);
const ctx = useResourceActionContext();
const { t } = useTranslation();
const { dn } = useDesignable();
const defaultFilter = fieldSchema?.['x-decorator-props']?.request?.params?.filter || {};
return (
<GeneralSchemaDesigner title={title || name}>
<SchemaSettings.ModalItem
title={'设置数据范围'}
schema={
{
type: 'object',
title: '设置数据范围',
properties: {
filter: {
default: defaultFilter,
title: '数据范围',
enum: dataSource,
'x-component': 'Filter',
'x-component-props': {},
},
},
} as ISchema
}
onSubmit={({ filter }) => {
const params = field.decoratorProps.request.params || {};
params.filter = filter;
field.decoratorProps.request.params = params;
fieldSchema['x-decorator-props']['request']['params'] = params;
ctx.run({ ...ctx.params?.[0], filter });
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
'x-decorator-props': fieldSchema['x-decorator-props'],
},
});
}}
/>
<SchemaSettings.SelectItem
title={'每页显示'}
value={field.decoratorProps.request.params?.pageSize || 20}
options={[
{ label: '10', value: 10 },
{ label: '20', value: 20 },
{ label: '50', value: 50 },
{ label: '100', value: 100 },
{ label: '200', value: 200 },
]}
onChange={(pageSize) => {
const params = field.decoratorProps.request.params || {};
params.pageSize = pageSize;
field.decoratorProps.request.params = params;
fieldSchema['x-decorator-props']['request']['params'] = params;
ctx.run({ ...ctx.params?.[0], pageSize });
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
'x-decorator-props': fieldSchema['x-decorator-props'],
},
});
}}
/>
<SchemaSettings.Divider />
<SchemaSettings.Remove
removeParentsIfNoChildren
breakRemoveOn={{
'x-component': 'Grid',
}}
/>
</GeneralSchemaDesigner>
);
};

View File

@ -0,0 +1,58 @@
import { Field } from '@formily/core';
import { observer, RecursionField, useField, useFieldSchema } from '@formily/react';
import React from 'react';
import { useCollection } from '..';
import { SchemaComponent } from '../schema-component';
import { ActionLogDesigner } from './ActionLog.Designer';
import { createSchema } from './utils';
export const ActionLog = () => {
const schema = createSchema();
return <SchemaComponent schema={schema} name={schema.name} />;
};
ActionLog.Designer = ActionLogDesigner;
ActionLog.Field = observer((props: any) => {
const { value } = props;
return <div>{value?.uiSchema?.title || value?.name}</div>;
});
ActionLog.FieldValue = observer((props: any) => {
const field = useField<Field>();
const fieldSchema = useFieldSchema();
const { getField } = useCollection();
const collectionField = getField(fieldSchema.name);
if (!collectionField.uiSchema) {
if (!field.value) {
return <div></div>;
}
if (typeof field.value === 'boolean') {
return <div>{field.value ? 'true' : 'false'}</div>;
}
if (typeof field.value === 'string' || typeof field.value === 'number') {
return <div>{field.value}</div>;
}
return <pre>{JSON.stringify(field.value, null, 2)}</pre>;
}
return (
<RecursionField
name="actionLogFieldValue"
schema={
{
type: 'void',
properties: {
[collectionField.name as string]: {
...collectionField.uiSchema,
default: field.value,
'x-decorator': null,
'x-read-pretty': true,
},
},
} as any
}
/>
);
});

View File

@ -0,0 +1,25 @@
import { TableOutlined } from '@ant-design/icons';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { SchemaInitializer } from '../schema-initializer';
export const ActionLogBlockInitializer = (props) => {
const { insert } = props;
const { t } = useTranslation();
return (
<SchemaInitializer.Item
{...props}
icon={<TableOutlined />}
onClick={({ item }) => {
insert({ type: 'void', 'x-component': 'ActionLog', 'x-component-props': {} });
}}
items={[
{
type: 'item',
name: 'ActionLog',
title: t('Action Logs'),
},
]}
/>
);
};

View File

@ -0,0 +1,19 @@
import React from 'react';
import { SchemaInitializerProvider } from '../schema-initializer';
import { initializes } from '../schema-initializer/Initializers';
export const ActionLogProvider = (props: any) => {
const actionLogsItemGroup = {
type: 'itemGroup',
title: '{{t("Others")}}',
children: [
{
type: 'item',
title: '{{t("Action logs")}}',
component: 'ActionLogBlockInitializer',
},
],
};
initializes.BlockInitializers.items.push({ ...actionLogsItemGroup });
return <SchemaInitializerProvider initializers={initializes}>{props.children}</SchemaInitializerProvider>;
};

View File

@ -0,0 +1,88 @@
import { ISchema } from '@formily/react';
import {
ActionLog,
ActionLogProvider,
AntdSchemaComponentProvider,
APIClient,
APIClientProvider,
CollectionManagerProvider,
SchemaComponent,
SchemaComponentProvider,
SchemaInitializerProvider,
} from '@nocobase/client';
import MockAdapter from 'axios-mock-adapter';
import React from 'react';
import { mockData } from './mockData';
const apiClient = new APIClient();
const mock = new MockAdapter(apiClient.axios);
mock.onGet('/action_logs:list').reply(200, mockData);
const schema = {
type: 'void',
name: 'ActionLog',
'x-component': 'ActionLog',
};
const collections = [
{
name: 'action_logs',
fields: [
{
type: 'integer',
name: 'id',
interface: 'input',
uiSchema: {
title: 'ID',
type: 'number',
'x-component': 'InputNumber',
required: true,
description: 'description1',
} as ISchema,
},
{
type: 'string',
name: 'created_at',
interface: 'createdAt',
uiSchema: {
title: 'Created At',
type: 'number',
'x-component': 'DatePicker',
required: true,
description: 'Date Picker',
} as ISchema,
},
{
type: 'string',
name: 'collection_name',
interface: 'input',
uiSchema: {
title: 'collection name',
type: 'number',
'x-component': 'Input',
description: 'collection name',
} as ISchema,
},
],
},
];
export default () => {
return (
<APIClientProvider apiClient={apiClient}>
<SchemaInitializerProvider>
<SchemaComponentProvider components={{ ActionLog }}>
<AntdSchemaComponentProvider>
<CollectionManagerProvider collections={collections}>
<ActionLogProvider>
<SchemaComponent schema={schema} />
</ActionLogProvider>
</CollectionManagerProvider>
</AntdSchemaComponentProvider>
</SchemaComponentProvider>
</SchemaInitializerProvider>
</APIClientProvider>
);
};

View File

@ -0,0 +1,285 @@
export const mockData = {
data: [
{
id: 41,
created_at: '2022-03-04T09:14:45.173Z',
collection_name: 'users',
type: 'create',
index: 7,
user_id: 1,
user: {
id: 1,
nickname: 'Super Admin',
email: 'admin@nocobase.com',
appLang: null,
token: '6287df8c3f623ba2cb12',
reset_token: null,
sort: 1,
created_at: '2022-02-17T05:31:09.172Z',
updated_at: '2022-02-17T05:31:32.031Z',
},
collection: {
name: 'users',
logging: true,
title: '{{t("Users")}}',
privilege: 'undelete',
sortable: 'sort',
options: { createdBy: false, updatedBy: false },
sort: 2,
created_at: '2021-09-02T15:07:56.914Z',
updated_at: '2021-09-15T00:59:20.676Z',
ui_schema_key: null,
},
},
{
id: 40,
created_at: '2022-03-04T09:14:25.133Z',
collection_name: 'users',
type: 'create',
index: 5,
user_id: 1,
user: {
id: 1,
nickname: 'Super Admin',
email: 'admin@nocobase.com',
appLang: null,
token: '6287df8c3f623ba2cb12',
reset_token: null,
sort: 1,
created_at: '2022-02-17T05:31:09.172Z',
updated_at: '2022-02-17T05:31:32.031Z',
},
collection: {
name: 'users',
logging: true,
title: '{{t("Users")}}',
privilege: 'undelete',
sortable: 'sort',
options: { createdBy: false, updatedBy: false },
sort: 2,
created_at: '2021-09-02T15:07:56.914Z',
updated_at: '2021-09-15T00:59:20.676Z',
ui_schema_key: null,
},
},
{
id: 39,
created_at: '2022-03-04T09:13:36.399Z',
collection_name: 'users',
type: 'create',
index: 2,
user_id: 1,
user: {
id: 1,
nickname: 'Super Admin',
email: 'admin@nocobase.com',
appLang: null,
token: '6287df8c3f623ba2cb12',
reset_token: null,
sort: 1,
created_at: '2022-02-17T05:31:09.172Z',
updated_at: '2022-02-17T05:31:32.031Z',
},
collection: {
name: 'users',
logging: true,
title: '{{t("Users")}}',
privilege: 'undelete',
sortable: 'sort',
options: { createdBy: false, updatedBy: false },
sort: 2,
created_at: '2021-09-02T15:07:56.914Z',
updated_at: '2021-09-15T00:59:20.676Z',
ui_schema_key: null,
},
},
{
id: 38,
created_at: '2022-03-02T09:16:07.590Z',
collection_name: 't_2uhu4szs1kq',
type: 'update',
index: 3,
user_id: 1,
user: {
id: 1,
nickname: 'Super Admin',
email: 'admin@nocobase.com',
appLang: null,
token: '6287df8c3f623ba2cb12',
reset_token: null,
sort: 1,
created_at: '2022-02-17T05:31:09.172Z',
updated_at: '2022-02-17T05:31:32.031Z',
},
collection: {
name: 't_2uhu4szs1kq',
logging: true,
title: '任务',
privilege: null,
sortable: 'sort',
options: {},
sort: 3,
created_at: '2021-09-03T08:17:30.495Z',
updated_at: '2021-09-18T04:15:42.572Z',
ui_schema_key: null,
},
},
{
id: 37,
created_at: '2022-03-02T09:16:06.611Z',
collection_name: 't_2uhu4szs1kq',
type: 'update',
index: 3,
user_id: null,
user: null,
collection: {
name: 't_2uhu4szs1kq',
logging: true,
title: '任务',
privilege: null,
sortable: 'sort',
options: {},
sort: 3,
created_at: '2021-09-03T08:17:30.495Z',
updated_at: '2021-09-18T04:15:42.572Z',
ui_schema_key: null,
},
},
{
id: 36,
created_at: '2022-03-02T09:16:06.526Z',
collection_name: 't_2uhu4szs1kq',
type: 'update',
index: 3,
user_id: 1,
user: {
id: 1,
nickname: 'Super Admin',
email: 'admin@nocobase.com',
appLang: null,
token: '6287df8c3f623ba2cb12',
reset_token: null,
sort: 1,
created_at: '2022-02-17T05:31:09.172Z',
updated_at: '2022-02-17T05:31:32.031Z',
},
collection: {
name: 't_2uhu4szs1kq',
logging: true,
title: '任务',
privilege: null,
sortable: 'sort',
options: {},
sort: 3,
created_at: '2021-09-03T08:17:30.495Z',
updated_at: '2021-09-18T04:15:42.572Z',
ui_schema_key: null,
},
},
{
id: 35,
created_at: '2022-03-02T09:16:05.930Z',
collection_name: 't_2uhu4szs1kq',
type: 'update',
index: 3,
user_id: null,
user: null,
collection: {
name: 't_2uhu4szs1kq',
logging: true,
title: '任务',
privilege: null,
sortable: 'sort',
options: {},
sort: 3,
created_at: '2021-09-03T08:17:30.495Z',
updated_at: '2021-09-18T04:15:42.572Z',
ui_schema_key: null,
},
},
{
id: 34,
created_at: '2022-03-02T09:16:05.842Z',
collection_name: 't_2uhu4szs1kq',
type: 'update',
index: 3,
user_id: 1,
user: {
id: 1,
nickname: 'Super Admin',
email: 'admin@nocobase.com',
appLang: null,
token: '6287df8c3f623ba2cb12',
reset_token: null,
sort: 1,
created_at: '2022-02-17T05:31:09.172Z',
updated_at: '2022-02-17T05:31:32.031Z',
},
collection: {
name: 't_2uhu4szs1kq',
logging: true,
title: '任务',
privilege: null,
sortable: 'sort',
options: {},
sort: 3,
created_at: '2021-09-03T08:17:30.495Z',
updated_at: '2021-09-18T04:15:42.572Z',
ui_schema_key: null,
},
},
{
id: 33,
created_at: '2022-03-02T09:16:04.209Z',
collection_name: 't_2uhu4szs1kq',
type: 'update',
index: 3,
user_id: 1,
user: {
id: 1,
nickname: 'Super Admin',
email: 'admin@nocobase.com',
appLang: null,
token: '6287df8c3f623ba2cb12',
reset_token: null,
sort: 1,
created_at: '2022-02-17T05:31:09.172Z',
updated_at: '2022-02-17T05:31:32.031Z',
},
collection: {
name: 't_2uhu4szs1kq',
logging: true,
title: '任务',
privilege: null,
sortable: 'sort',
options: {},
sort: 3,
created_at: '2021-09-03T08:17:30.495Z',
updated_at: '2021-09-18T04:15:42.572Z',
ui_schema_key: null,
},
},
{
id: 32,
created_at: '2022-03-02T09:16:02.289Z',
collection_name: 't_2uhu4szs1kq',
type: 'update',
index: 5,
user_id: null,
user: null,
collection: {
name: 't_2uhu4szs1kq',
logging: true,
title: '任务',
privilege: null,
sortable: 'sort',
options: {},
sort: 3,
created_at: '2021-09-03T08:17:30.495Z',
updated_at: '2021-09-18T04:15:42.572Z',
ui_schema_key: null,
},
},
],
meta: { count: 41, page: 1, per_page: 10 },
};

View File

@ -0,0 +1,12 @@
---
nav:
path: /client
group:
path: /client
---
# ActionLogs
## Examples
<code src="./demos/demo1.tsx"/>

View File

@ -0,0 +1,3 @@
export * from './ActionLog';
export * from './ActionLogBlockInitializer';
export * from './ActionLogProvider';

View File

@ -0,0 +1,303 @@
import { uid } from '@formily/shared';
export const createSchema = () => {
const filterSchema = {
'x-component': 'Action',
'x-component-props': {
popover: true,
},
type: 'void',
title: '{{t("Filter")}}',
properties: {
popover: {
type: 'void',
'x-decorator': 'Form',
'x-decorator-props': {},
'x-component': 'Action.Popover',
'x-component-props': {
trigger: 'click',
placement: 'bottomLeft',
},
properties: {
filter: {
type: 'object',
default: {
$and: [{}],
},
'x-component': 'Filter',
'x-component-props': {
useDataSource: '{{cm.useFilterDataSource}}',
},
},
footer: {
type: 'void',
'x-component': 'Action.Popover.Footer',
properties: {
actions: {
type: 'void',
'x-component': 'ActionBar',
properties: {
saveDefault: {
type: 'void',
'x-component': 'Filter.SaveDefaultValue',
'x-component-props': {},
},
cancel: {
type: 'void',
title: '{{t("Cancel")}}',
'x-component': 'Action',
'x-component-props': {
useAction: '{{cm.useCancelFilterAction}}',
},
},
submit: {
type: 'void',
title: '{{t("Submit")}}',
'x-component': 'Action',
'x-component-props': {
type: 'primary',
useAction: '{{cm.useFilterAction}}',
},
},
},
},
},
},
},
},
},
};
const schema = {
type: 'void',
name: 'actionLog',
'x-decorator': 'ResourceActionProvider',
'x-decorator-props': {
collection: 'action_logs',
dragSort: false,
request: {
resource: 'action_logs',
action: 'list',
params: {
pageSize: 20,
filter: {},
appends: [],
},
},
},
'x-designer': 'ActionLog.Designer',
'x-component': 'CardItem',
properties: {
actions: {
type: 'void',
'x-component': 'ActionBar',
'x-component-props': {
layout: 'one-column',
style: {
marginBottom: 16,
},
},
properties: {
[uid()]: { ...filterSchema },
},
},
table: {
type: 'void',
'x-component': 'Table.Void',
'x-component-props': {
rowKey: 'id',
useDataSource: '{{cm.useDataSourceFromRAC}}',
},
properties: {
column1: {
type: 'void',
title: "{{t('Created at')}}",
'x-component': 'Table.Column',
properties: {
created_at: {
type: 'string',
'x-component': 'DatePicker',
'x-read-pretty': true,
'x-component-props': {
format: 'YYYY-MM-DD HH:mm:ss',
},
},
},
},
column2: {
type: 'void',
title: "{{t('Created by')}}",
'x-component': 'Table.Column',
properties: {
'user.nickname': {
type: 'string',
'x-component': 'Input',
'x-read-pretty': true,
},
},
},
column3: {
type: 'void',
title: "{{t('Collection display name')}}",
'x-component': 'Table.Column',
properties: {
'collection.title': {
type: 'string',
'x-component': 'Input',
'x-read-pretty': true,
},
},
},
column4: {
type: 'void',
title: "{{t('Action type')}}",
'x-component': 'Table.Column',
properties: {
type: {
type: 'string',
'x-component': 'Select',
'x-read-pretty': true,
enum: [
{ label: "{{ t('Insert') }}", value: 'create', color: 'green' },
{ label: "{{ t('Update') }}", value: 'update', color: 'blue' },
{ label: "{{ t('Delete') }}", value: 'destroy', color: 'red' },
],
},
},
},
[uid()]: {
type: 'void',
title: "{{t('Actions')}}",
'x-component': 'Table.Column',
'x-component-props': {
width: 60,
align: 'center',
},
properties: {
[uid()]: {
title: '{{ t("View") }}',
type: 'void',
'x-action': 'view',
'x-component': 'Action.Link',
'x-component-props': {
openMode: 'drawer',
},
properties: {
drawer: {
type: 'void',
'x-component': 'Action.Container',
'x-component-props': {
className: 'nb-action-popup',
},
title: '{{ t("View record") }}',
properties: {
created_at: {
type: 'string',
title: "{{t('Created at')}}",
'x-decorator': 'FormItem',
'x-component': 'DatePicker',
'x-read-pretty': true,
'x-component-props': {
format: 'YYYY-MM-DD HH:mm:ss',
},
},
'user.nickname': {
type: 'string',
title: "{{t('Created by')}}",
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-read-pretty': true,
},
'collection.title': {
type: 'string',
title: "{{t('Collection display name')}}",
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-read-pretty': true,
},
type: {
type: 'string',
title: "{{t('Action type')}}",
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-read-pretty': true,
enum: [
{
label: "{{t('Insert')}}",
value: 'create',
color: 'green',
},
{
label: "{{t('Update')}}",
value: 'update',
color: 'blue',
},
{
label: "{{t('Delete')}}",
value: 'destroy',
color: 'red',
},
],
},
changes: {
type: 'array',
title: "{{t('Data changes')}}",
'x-decorator': 'FormItem',
'x-component': 'Table.Array',
'x-component-props': {
pagination: false,
showIndex: true,
},
items: {
type: 'object',
properties: {
column1: {
type: 'void',
'x-component': 'Table.Column',
'x-component-props': { title: "{{t('Field display name')}}" },
properties: {
field: {
type: 'string',
'x-decorator': 'FormilyFormItem',
'x-component': 'ActionLog.Field',
},
},
},
column2: {
type: 'void',
'x-component': 'Table.Column',
'x-component-props': { title: "{{ t('Before change') }}" },
properties: {
before: {
type: 'string',
'x-decorator': 'FormilyFormItem',
'x-component': 'ActionLog.FieldValue',
},
},
},
column3: {
type: 'void',
'x-component': 'Table.Column',
'x-component-props': { title: "{{ t('After change') }}" },
properties: {
after: {
type: 'string',
'x-decorator': 'FormilyFormItem',
'x-component': 'ActionLog.FieldValue',
},
},
},
},
},
},
},
},
},
},
},
},
},
},
},
};
return schema;
};

View File

@ -1,6 +1,7 @@
import './global.less';
export * from './acl';
export * from './action-logs';
export * from './antd-config-provider';
export * from './api-client';
export * from './application';
@ -22,3 +23,4 @@ export * from './settings-form';
export * from './system-settings';
export * from './user';
export * from './workflow';

View File

@ -277,5 +277,6 @@
"Default": "Default",
"Add card": "Add card",
"edit title": "Edit Title",
"turn page": "Turn Page"
"Turn the page": "Turn the page",
"Others": "Others"
}

View File

@ -279,7 +279,8 @@
"Add card": "添加卡片",
"edit title": "修改标题",
"turn page": "翻页",
"Turn pages": "翻页",
"Others": "其他",
"Save as template": "保存为模板",
"Block templates": "区块模板",
"Convert reference to duplicate": "模板引用转为复制",

View File

@ -31,10 +31,10 @@ export const ActionInitializer = observer((props: any) => {
},
{
type: 'item',
title: t('Turn page'),
title: t('Turn the page'),
component: InitializeAction,
schema: {
title: t('Turn page'),
title: t('Turn the page'),
'x-component': 'Calendar.Nav',
'x-action': `calendar:nav`,
'x-align': 'left',

View File

@ -23,7 +23,7 @@ export const CalendarActionInitializers = {
title: 'Turn pages',
component: 'ActionInitializer',
schema: {
title: 'Turn page',
title: 'Turn the page',
'x-component': 'Calendar.Nav',
'x-action': `calendar:nav`,
'x-align': 'left',

View File

@ -8,14 +8,16 @@ import { gridRowColWrap } from './utils';
const useRelationFields = () => {
const { fields } = useCollection();
return fields.filter(field => field.interface === 'linkTo').map((field) => {
return {
type: 'item',
field,
title: field?.uiSchema?.title || field.name,
component: 'RecordRelationBlockInitializer',
};
}) as any;
return fields
.filter((field) => field.interface === 'linkTo')
.map((field) => {
return {
type: 'item',
field,
title: field?.uiSchema?.title || field.name,
component: 'RecordRelationBlockInitializer',
};
}) as any;
};
// 当前行记录所在面板的添加区块
@ -61,6 +63,17 @@ export const RecordBlockInitializers = (props: any) => {
},
],
},
{
type: 'itemGroup',
title: '{{t("Others")}}',
children: [
{
type: 'item',
title: '{{t("Action logs")}}',
component: 'ActionLogBlockInitializer',
},
],
},
]}
/>
);

View File

@ -4,7 +4,7 @@ import { SchemaComponentOptions } from '../schema-component';
import { initializes as globals, items } from './Initializers';
import { SchemaInitializer } from './SchemaInitializer';
const SchemaInitializerContext = createContext<any>({});
export const SchemaInitializerContext = createContext<any>({});
export interface SchemaInitializerProviderProps {
components?: any;