Feat(plugin workflow): add changed fields to model trigger config (#332)

* feat(plugin-workflow): add changed fields to model trigger config

* fix(plugin-workflow): avoid system fields to input
This commit is contained in:
Junyi 2022-04-28 10:18:44 +08:00 committed by GitHub
parent 27399f4e5e
commit f5cc368cef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 88 additions and 53 deletions

View File

@ -395,12 +395,7 @@ export const CollectionFieldset = observer(({ value, onChange, useProps }: any)
}
`}>
{fields.length
? fields
.filter(field => (
!field.hidden
&& (field.uiSchema ? !field.uiSchema['x-read-pretty'] : true)
))
.map(field => {
? fields.map(field => {
const operand = typeof value[field.name] === 'string'
? parseStringValue(value[field.name], VTypes)
: { type: 'constant', value: value[field.name] };

View File

@ -12,8 +12,8 @@ export default {
group: 'model',
fieldset: {
'config.collection': {
name: 'config.collection',
...collection,
name: 'config.collection'
},
// multiple: {
// type: 'boolean',

View File

@ -1,13 +1,13 @@
import { CloseOutlined, DeleteOutlined } from '@ant-design/icons';
import { css, cx } from '@emotion/css';
import { Field } from '@formily/core';
import { ISchema, RecursionField, useField, useForm } from '@formily/react';
import { ISchema, useForm } from '@formily/react';
import { Registry } from '@nocobase/utils';
import { Button, Modal, Tag } from 'antd';
import React, { useContext } from 'react';
import { SchemaComponent, useActionContext, useAPIClient, useCollection, useRequest, useResourceActionContext } from '../..';
import { nodeBlockClass, nodeCardClass, nodeClass, nodeHeaderClass, nodeTitleClass } from '../style';
import { AddButton, useFlowContext } from '../WorkflowCanvas';
import calculation from './calculation';
import condition from './condition';
import create from './create';
@ -18,10 +18,6 @@ import update from './update';
export interface Instruction {
title: string;
type: string;
@ -148,14 +144,6 @@ export function RemoveButton() {
);
}
function Fieldset({ schema }) {
const field = useField<Field>();
return (
<RecursionField basePath={field.address} schema={schema} />
);
}
export function NodeDefaultView(props) {
const { data, children } = props;
const instruction = instructions.get(data.type);
@ -174,10 +162,7 @@ export function NodeDefaultView(props) {
</div>
<SchemaComponent
scope={instruction.scope}
components={{
...instruction.components,
Fieldset
}}
components={instruction.components}
schema={{
type: 'void',
properties: {
@ -197,9 +182,9 @@ export function NodeDefaultView(props) {
'x-decorator': 'Form',
'x-decorator-props': {
useValues(options) {
const data = useNodeContext();
const d = useNodeContext();
return useRequest(() => {
return Promise.resolve({ data });
return Promise.resolve({ data: d });
}, options);
}
},

View File

@ -29,7 +29,11 @@ export const values = {
useProps() {
const { getCollectionFields } = useCollectionManager();
const { values: form } = useForm();
const fields = getCollectionFields(form?.config?.collection);
const fields = getCollectionFields(form?.config?.collection)
.filter(field => (
!field.hidden
&& (field.uiSchema ? !field.uiSchema['x-read-pretty'] : false)
));
return { fields };
}
}
@ -50,7 +54,7 @@ export const filter = {
'x-component-props': {
useProps() {
const { values } = useForm();
const options = useCollectionFilterOptions(values.config.collection);
const options = useCollectionFilterOptions(values.config?.collection);
return {
options,
className: css`

View File

@ -3,7 +3,7 @@ import { ISchema, useForm } from "@formily/react";
import { cx } from "@emotion/css";
import { Registry } from "@nocobase/utils";
import { SchemaComponent, useActionContext, useAPIClient, useRecord, useResourceActionContext } from '../../';
import { SchemaComponent, useActionContext, useAPIClient, useRecord, useRequest, useResourceActionContext } from '../../';
import model from './model';
import { nodeCardClass } from "../style";
@ -19,12 +19,7 @@ function useUpdateConfigAction() {
await form.submit();
await api.resource('workflows', record.id).update({
filterByTk: record.id,
values: {
config: {
...record.config,
...form.values
}
},
values: form.values
});
ctx.setVisible(false);
refresh();
@ -72,10 +67,16 @@ export const TriggerConfig = () => {
'x-component': 'Action.Drawer',
'x-decorator': 'Form',
'x-decorator-props': {
initialValue: config
initialValue: { config }
},
properties: {
...fieldset,
config: {
type: 'void',
name: 'config',
'x-component': 'fieldset',
'x-component-props': {},
properties: fieldset
},
actions: {
type: 'void',
'x-component': 'Action.Drawer.Footer',

View File

@ -1,5 +1,7 @@
import React from 'react';
import { Select } from 'antd';
import { action } from '@formily/reactive';
import { useForm } from '@formily/react';
import { useCollectionDataSource, useCollectionManager } from '../../collection-manager';
import { useCompile } from '../../schema-component';
@ -8,15 +10,36 @@ import { useFlowContext } from '../WorkflowCanvas';
import { BaseTypeSet } from '../calculators';
import { collection, filter } from '../schemas/collection';
function useCollectionFieldsDataSource() {
const compile = useCompile();
const { getCollectionFields } = useCollectionManager();
const { values } = useForm();
const fields = getCollectionFields(values?.config?.collection);
return (field: any) => {
action.bound((data: any) => {
field.dataSource = data
.filter(field => (
!field.hidden
&& (field.uiSchema ? !field.uiSchema['x-read-pretty'] : true)
))
.map(field => ({
label: compile(field.uiSchema?.title),
value: field.name
}));
})(fields);
};
}
export default {
title: '数据表事件',
type: 'model',
fieldset: {
collection,
mode: {
'config.collection': collection,
'config.mode': {
type: 'number',
title: '触发时机',
name: 'mode',
name: 'config.mode',
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {
@ -28,13 +51,29 @@ export default {
]
}
},
filter: {
'config.changed': {
type: 'array',
name: 'changed',
title: '发生变动的字段',
description: '只有被选中的某个字段发生变动时才会触发。如果不选择,则表示任何字段变动时都会触发。新增或删除数据时,任意字段都被认为发生变动。',
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {
mode: 'multiple',
},
'x-reactions': [
'{{useCollectionFieldsDataSource()}}'
]
},
'config.condition': {
...filter,
name: 'config.condition',
title: '满足条件'
}
},
scope: {
useCollectionDataSource
useCollectionDataSource,
useCollectionFieldsDataSource
},
getter({ type, options, onChange }) {
const compile = useCompile();

View File

@ -4,7 +4,7 @@ export interface ModelChangeTriggerConfig {
collection: string;
mode: number;
// TODO: ICondition
filter: any;
condition: any;
}
const MODE_BITMAP = {
@ -18,26 +18,37 @@ MODE_BITMAP_EVENTS.set(MODE_BITMAP.CREATE, 'afterCreate');
MODE_BITMAP_EVENTS.set(MODE_BITMAP.UPDATE, 'afterUpdate');
MODE_BITMAP_EVENTS.set(MODE_BITMAP.DESTROY, 'afterDestroy');
// async function, should return promise
function bindHandler(this: WorkflowModel, callback: Function) {
const { condition, changed } = this.config;
return (data: any, options) => {
// NOTE: if no configured fields changed, do not trigger
if (changed && changed.length && changed.every(name => !data.changed(name))) {
return;
}
// NOTE: if no configured condition match, do not trigger
if (condition && condition.$and.length) {
// TODO: check all conditions in condition against data
// const calculation = toCalculation(condition);
}
return callback({ data: data.get() }, options);
};
}
export default {
name: 'model',
on(this: WorkflowModel, callback: Function) {
const { database } = <typeof WorkflowModel>this.constructor;
const { collection, mode, filter } = this.config;
const { collection, mode } = this.config;
const Collection = database.getCollection(collection);
if (!Collection) {
return;
}
// async function, should return promise
const handler = (data: any, options) => {
if (filter) {
// TODO: check all conditions in filter against data
}
return callback({ data: data.get() }, options);
};
// TODO: duplication when mode change should be considered
for (let [key, event] of MODE_BITMAP_EVENTS.entries()) {
if (mode & key) {
Collection.model.addHook(event, this.getHookId(), handler);
Collection.model.addHook(event, this.getHookId(), bindHandler.call(this, callback));
}
}
},