Junyi 2c9ce09304
feat(plugin-workflow): manual forms (#1748)
* feat(plugin-workflow): add create record form for manual node

* feat(plugin-workflow): add update form for manual node

* fix(plugin-workflow): fix antd version and menu

* refactor(plugin-workflow): change collection config modal to initializer sub-menu

* test(plugin-workflow): add test case

* fix(plugin-workflow): fix todo components based on forms

* fix(plugin-workflow): fix cycling trigger when create or update

* fix(plugin-workflow): fix transaction in manual processor

* refactor(plugin-workflow): optimize todo list data loading

* fix(plugin-workflow): fix uncommitted manual action effects

* fix(plugin-workflow): fix save multiple forms and only submit one

* chore(plugin-workflow): fix lint

* fix(plugin-workflow): fix lint error

* refactor(plugin-workflow): abstract workflow scoped FormBlockProvider

* fix(plugin-workflow): adjust designable api to use current schema in refresh

* fix(plugin-workflow): fix schema config and support block template

* fix(plugin-workflow): fix lint and build error

* fix(plugin-workflow): adjust components and scope to inner schema component

* fix(plugin-workflow): fix ref template in todo drawer

* fix(plugin-workflow): fix todo form undefined

* fix(plugin-workflow): fix manual form schema

* fix(plugin-workflow): fix manual createdBy/updatedBy user

* fix(plugin-workflow): disable save to template on create form

* fix(plugin-workflow): fix manual form variables

* fix(plugin-workflow): fix FormBlockProvider for default field value

* fix(plugin-workflow): fix manual node variables
2023-06-05 16:52:43 +08:00

156 lines
4.5 KiB
TypeScript

import { BlockInitializers, SchemaInitializerItemOptions, useCollectionManager } from '@nocobase/client';
import { CollectionBlockInitializer } from '../../components/CollectionBlockInitializer';
import { CollectionFieldInitializers } from '../../components/CollectionFieldInitializers';
import { filterTypedFields, useCollectionFieldOptions } from '../../variable';
import { NAMESPACE } from '../../locale';
import { SchemaConfig, SchemaConfigButton } from './SchemaConfig';
import { ModeConfig } from './ModeConfig';
import { AssigneesSelect } from './AssigneesSelect';
const MULTIPLE_ASSIGNED_MODE = {
SINGLE: Symbol('single'),
ALL: Symbol('all'),
ANY: Symbol('any'),
ALL_PERCENTAGE: Symbol('all percentage'),
ANY_PERCENTAGE: Symbol('any percentage'),
};
// TODO(optimize): change to register way
const initializerGroup = BlockInitializers.items.find((group) => group.key === 'media');
if (!initializerGroup.children.find((item) => item.key === 'workflowTodos')) {
initializerGroup.children.push({
key: 'workflowTodos',
type: 'item',
title: `{{t("Workflow todos", { ns: "${NAMESPACE}" })}}`,
component: 'WorkflowTodoBlockInitializer',
icon: 'CheckSquareOutlined',
} as any);
}
export default {
title: `{{t("Manual", { ns: "${NAMESPACE}" })}}`,
type: 'manual',
group: 'manual',
description: `{{t("Could be used for manually submitting data, and determine whether to continue or exit. Workflow will generate a todo item for assigned user when it reaches a manual node, and continue processing after user submits the form.", { ns: "${NAMESPACE}" })}}`,
fieldset: {
assignees: {
type: 'array',
title: `{{t("Assignees", { ns: "${NAMESPACE}" })}}`,
'x-decorator': 'FormItem',
'x-component': 'AssigneesSelect',
'x-component-props': {
// multiple: true,
},
required: true,
default: [],
},
mode: {
type: 'number',
title: `{{t("Mode", { ns: "${NAMESPACE}" })}}`,
'x-decorator': 'FormItem',
'x-component': 'ModeConfig',
default: 1,
'x-reactions': {
dependencies: ['assignees'],
fulfill: {
state: {
visible: '{{$deps[0].length > 1}}',
},
},
},
},
schema: {
type: 'void',
title: `{{t("User interface", { ns: "${NAMESPACE}" })}}`,
'x-decorator': 'FormItem',
'x-component': 'SchemaConfigButton',
properties: {
schema: {
type: 'object',
'x-component': 'SchemaConfig',
default: null,
},
},
},
forms: {
type: 'object',
default: {},
},
},
view: {},
scope: {},
components: {
SchemaConfigButton,
SchemaConfig,
ModeConfig,
AssigneesSelect,
},
useVariables({ config }, { types }) {
const formKeys = Object.keys(config.forms ?? {});
if (!formKeys.length) {
return null;
}
const options = formKeys
.map((formKey) => {
const form = config.forms[formKey];
// eslint-disable-next-line react-hooks/rules-of-hooks
const options = useCollectionFieldOptions({
fields: form.collection?.fields,
collection: form.collection,
types,
});
return options.length
? {
key: formKey,
value: formKey,
label: form.title || formKey,
title: form.title || formKey,
children: options,
}
: null;
})
.filter(Boolean);
return options.length ? options : null;
},
useInitializers(node): SchemaInitializerItemOptions | null {
const { getCollection } = useCollectionManager();
const formKeys = Object.keys(node.config.forms ?? {});
if (!formKeys.length || node.config.mode) {
return null;
}
const forms = formKeys
.map((formKey) => {
const form = node.config.forms[formKey];
const { fields = [] } = getCollection(form.collection);
return fields.length
? ({
type: 'item',
title: form.title ?? formKey,
component: CollectionBlockInitializer,
collection: form.collection,
dataSource: `{{$jobsMapByNodeId.${node.id}.${formKey}}}`,
} as SchemaInitializerItemOptions)
: null;
})
.filter(Boolean);
return forms.length
? {
key: 'forms',
type: 'subMenu',
title: node.title,
children: forms,
}
: null;
},
initializers: {
CollectionFieldInitializers,
},
};