feat/approval-0702 (#1264)
Reviewed-on: daoyoucloud/tachybase#1264 Reviewed-by: sealday <zhanglin@daoyoucloud.com> Co-authored-by: bai.zixv <bai.zixv@foxmail.com> Co-committed-by: bai.zixv <bai.zixv@foxmail.com>
This commit is contained in:
parent
c8f99dd228
commit
0d8bbba1da
@ -13,9 +13,9 @@ import { AssigneesSelect } from './approval-config/VC.AssigneesSelect';
|
|||||||
import { ContentTooltip } from './approval-config/VC.ContentTooltip';
|
import { ContentTooltip } from './approval-config/VC.ContentTooltip';
|
||||||
import { NegotiationConfig } from './approval-config/VC.NegotiationConfig';
|
import { NegotiationConfig } from './approval-config/VC.NegotiationConfig';
|
||||||
import { SchemaConfigButtonApprover } from './approval-config/VC.SchemaConfigButtonApprover';
|
import { SchemaConfigButtonApprover } from './approval-config/VC.SchemaConfigButtonApprover';
|
||||||
|
import { ApprovalInstructionNode } from './ApprovalNode.component';
|
||||||
import { ApproverInterfaceComponent } from './approver-interface/ApproverInterface.schema';
|
import { ApproverInterfaceComponent } from './approver-interface/ApproverInterface.schema';
|
||||||
import { isApprovalReturnFunc } from './utils';
|
import { isApprovalReturnFunc } from './utils';
|
||||||
import { ApprovalInstructionNode } from './VC.ApprovalInstructionNode';
|
|
||||||
|
|
||||||
// 工作流节点 nodes - 人工处理->审批
|
// 工作流节点 nodes - 人工处理->审批
|
||||||
export class ApprovalInstruction extends Instruction {
|
export class ApprovalInstruction extends Instruction {
|
||||||
@ -42,13 +42,6 @@ export class ApprovalInstruction extends Instruction {
|
|||||||
];
|
];
|
||||||
// 审批节点表单设置
|
// 审批节点表单设置
|
||||||
fieldset = {
|
fieldset = {
|
||||||
collection: {
|
|
||||||
type: 'string',
|
|
||||||
title: '{{t("Collection")}}',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'DataSourceCollectionCascader',
|
|
||||||
// 'x-disabled': '{{ useWorkflowAnyExecuted() }}',
|
|
||||||
},
|
|
||||||
branchMode: {
|
branchMode: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
title: `{{t("Pass mode", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Pass mode", { ns: "${NAMESPACE}" })}}`,
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import { useActionContext } from '@tachybase/client';
|
import { useActionContext } from '@tachybase/client';
|
||||||
|
|
||||||
import { ConfigButtonView } from '../../trigger/ConfigButton.view';
|
import { ConfigButtonView } from '../../trigger/ConfigButton.view';
|
||||||
import { ContextApproverConfig } from '../Pd.ContextApproverConfig';
|
import { ContextApproverConfig } from '../ApproverConfig.context';
|
||||||
|
|
||||||
// 审批人操作界面->进入配置按钮
|
// 审批人操作界面->进入配置按钮
|
||||||
export const SchemaConfigButtonApprover = (props) => {
|
export const SchemaConfigButtonApprover = (props) => {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { gridRowColWrap, SchemaInitializer, usePlugin } from '@tachybase/client';
|
import { gridRowColWrap, SchemaInitializer, useDataSourceManager, usePlugin } from '@tachybase/client';
|
||||||
|
|
||||||
import { useFlowContext } from '../../../../../FlowContext';
|
import { useFlowContext } from '../../../../../FlowContext';
|
||||||
import { useAvailableUpstreams, useNodeContext } from '../../../../../nodes';
|
import { useAvailableUpstreams, useNodeContext } from '../../../../../nodes';
|
||||||
import { PluginWorkflow } from '../../../../../Plugin';
|
import { PluginWorkflow } from '../../../../../Plugin';
|
||||||
import { useTrigger } from '../../../../../triggers';
|
import { useTrigger } from '../../../../../triggers';
|
||||||
import { NAMESPACE } from '../../../locale';
|
import { NAMESPACE } from '../../../locale';
|
||||||
import { EditableForm } from './EditableForm.schema';
|
import { approvalFormOptions, ApprovalFormType } from '../forms/Approval.options';
|
||||||
import { ApproverAddBlockComponent } from './VC.ApproverAddBlock';
|
import { ApproverAddBlockComponent } from './VC.ApproverAddBlock';
|
||||||
import { ApproverAddBlockKit } from './VC.ApproverAddBlockKit';
|
import { ApproverAddBlockKit } from './VC.ApproverAddBlockKit';
|
||||||
|
|
||||||
@ -33,19 +33,6 @@ export const ApproverAddBlockInitializer = new SchemaInitializer({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'forms',
|
|
||||||
type: 'itemGroup',
|
|
||||||
title: '{{t("Form")}}',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
name: 'form',
|
|
||||||
type: 'item',
|
|
||||||
title: `{{t("Apply form", { ns: "${NAMESPACE}" })}}`,
|
|
||||||
Component: EditableForm,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: 'itemGroup',
|
type: 'itemGroup',
|
||||||
name: 'dataBlocks',
|
name: 'dataBlocks',
|
||||||
@ -79,6 +66,22 @@ export const ApproverAddBlockInitializer = new SchemaInitializer({
|
|||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'itemGroup',
|
||||||
|
name: 'form',
|
||||||
|
title: '{{t("Form")}}',
|
||||||
|
useChildren() {
|
||||||
|
const dm = useDataSourceManager();
|
||||||
|
const allCollections = dm.getAllCollections();
|
||||||
|
const values = Array.from(approvalFormOptions.getValues());
|
||||||
|
return values.map((item: ApprovalFormType) => {
|
||||||
|
// NOTE: 这里通过赋别名,避免 eslint 检查 hooks 语法,无法提交
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
const { useInitializer: getInitializer } = item.config;
|
||||||
|
return getInitializer({ allCollections });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'others',
|
name: 'others',
|
||||||
type: 'itemGroup',
|
type: 'itemGroup',
|
@ -8,7 +8,7 @@ import { Spin } from 'antd';
|
|||||||
import { DetailsBlockProvider, SimpleDesigner } from '../../../../../components';
|
import { DetailsBlockProvider, SimpleDesigner } from '../../../../../components';
|
||||||
import { FormBlockProvider } from '../../../common/Pd.FormBlock';
|
import { FormBlockProvider } from '../../../common/Pd.FormBlock';
|
||||||
import { flatSchemaArray } from '../../../constants';
|
import { flatSchemaArray } from '../../../constants';
|
||||||
import { ContextApproverConfig } from '../Pd.ContextApproverConfig';
|
import { ContextApproverConfig } from '../ApproverConfig.context';
|
||||||
import { ActionBarProvider } from './Pd.ActionBarProvider';
|
import { ActionBarProvider } from './Pd.ActionBarProvider';
|
||||||
import { ApprovalActionProvider } from './Pd.ApprovalActionProvider';
|
import { ApprovalActionProvider } from './Pd.ApprovalActionProvider';
|
||||||
import { ContextApproverBlock } from './Pd.SchemaConfigButtonContext';
|
import { ContextApproverBlock } from './Pd.SchemaConfigButtonContext';
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import {
|
|
||||||
createEditFormBlockUISchema,
|
|
||||||
createFormBlockSchema,
|
|
||||||
parseCollectionName,
|
|
||||||
SchemaInitializerItem,
|
|
||||||
useRecordCollectionDataSourceItems,
|
|
||||||
useSchemaInitializer,
|
|
||||||
useSchemaInitializerItem,
|
|
||||||
useSchemaTemplateManager,
|
|
||||||
} from '@tachybase/client';
|
|
||||||
import { useForm } from '@tachybase/schema';
|
|
||||||
|
|
||||||
import { APPROVAL_STATUS, flatSchemaArray } from '../../../constants';
|
|
||||||
|
|
||||||
export const EditableForm = () => {
|
|
||||||
const { insert } = useSchemaInitializer();
|
|
||||||
const { getTemplateSchemaByMode } = useSchemaTemplateManager();
|
|
||||||
const { values } = useForm();
|
|
||||||
const [dataSource, collectionName] = parseCollectionName(values.collection);
|
|
||||||
|
|
||||||
const itemConfig = useSchemaInitializerItem();
|
|
||||||
const items = useRecordCollectionDataSourceItems('FormItem');
|
|
||||||
|
|
||||||
const onClick = async ({ item }) => {
|
|
||||||
const template = item.template ? await getTemplateSchemaByMode(item) : null;
|
|
||||||
const formSchema = createEditFormBlockUISchema({
|
|
||||||
dataSource: dataSource,
|
|
||||||
collectionName: collectionName,
|
|
||||||
templateSchema: template,
|
|
||||||
});
|
|
||||||
const [key] = Object.keys(formSchema.properties);
|
|
||||||
const [targetSchema] = flatSchemaArray(
|
|
||||||
formSchema.properties[key],
|
|
||||||
(property) => property['x-component'] === 'ActionBar',
|
|
||||||
);
|
|
||||||
|
|
||||||
targetSchema['x-decorator'] = 'ActionBarProvider';
|
|
||||||
targetSchema['x-component-props'].style = { marginTop: '1.5em', flexWrap: 'wrap' };
|
|
||||||
|
|
||||||
insert(formSchema);
|
|
||||||
};
|
|
||||||
|
|
||||||
return <SchemaInitializerItem {...itemConfig} onClick={onClick} items={items} />;
|
|
||||||
};
|
|
@ -1,3 +1,4 @@
|
|||||||
export const ApprovalActionProvider = (props) => {
|
export const ApprovalActionProvider = (props) => {
|
||||||
|
console.log('%c Line:2 🍰 props', 'font-size:18px;color:#4fff4B;background:#7f2b82', props);
|
||||||
return props.children;
|
return props.children;
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,102 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { CompatibleSchemaInitializer, InitializerWithSwitch, useSchemaInitializerItem } from '@tachybase/client';
|
||||||
|
|
||||||
|
import { JOB_STATUS } from '../../../../../constants';
|
||||||
|
import { APPROVAL_ACTION_STATUS } from '../../../constants';
|
||||||
|
import { NAMESPACE } from '../../../locale';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
// NOTE: 似乎没用, 但删除又报错, 先保留着
|
||||||
|
const addActionButton_deprecated = new CompatibleSchemaInitializer({
|
||||||
|
name: 'ApprovalAddActionButton',
|
||||||
|
title: '{{t("Configure actions")}}',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'jobStatusResolved',
|
||||||
|
title: `{{t("Continue the process", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
Component: ActionInitializer,
|
||||||
|
action: JOB_STATUS.RESOLVED,
|
||||||
|
actionProps: {
|
||||||
|
type: 'primary',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'jobStatusRejected',
|
||||||
|
title: `{{t("Terminate the process", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
Component: ActionInitializer,
|
||||||
|
action: JOB_STATUS.REJECTED,
|
||||||
|
actionProps: {
|
||||||
|
danger: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'jobStatusPending',
|
||||||
|
title: `{{t("Save temporarily", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
Component: ActionInitializer,
|
||||||
|
action: JOB_STATUS.PENDING,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ApprovalAddActionButton = new CompatibleSchemaInitializer(
|
||||||
|
{
|
||||||
|
name: 'ApprovalAddActionButton',
|
||||||
|
title: '{{t("Configure actions")}}',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'approvalStatusResolved',
|
||||||
|
title: `{{t("Continue the process", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
Component: ActionInitializer,
|
||||||
|
statusApproval: APPROVAL_ACTION_STATUS.APPROVED,
|
||||||
|
actionProps: {
|
||||||
|
type: 'primary',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'approvalStatusRejected',
|
||||||
|
title: `{{t("Terminate the process", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
Component: ActionInitializer,
|
||||||
|
statusApproval: APPROVAL_ACTION_STATUS.REJECTED,
|
||||||
|
actionProps: {
|
||||||
|
danger: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'approvalStatusPending',
|
||||||
|
title: `{{t("Save temporarily", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
Component: ActionInitializer,
|
||||||
|
statusApproval: APPROVAL_ACTION_STATUS.PENDING,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
addActionButton_deprecated,
|
||||||
|
);
|
||||||
|
|
||||||
|
function ActionInitializer() {
|
||||||
|
const itemConfig = useSchemaInitializerItem();
|
||||||
|
const { statusApproval, actionProps, ...others } = itemConfig;
|
||||||
|
return (
|
||||||
|
<InitializerWithSwitch
|
||||||
|
{...others}
|
||||||
|
item={itemConfig}
|
||||||
|
schema={{
|
||||||
|
type: 'void',
|
||||||
|
title: others.title,
|
||||||
|
'x-decorator': 'ApprovalActionProvider',
|
||||||
|
'x-decorator-props': {
|
||||||
|
status: statusApproval,
|
||||||
|
},
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
...actionProps,
|
||||||
|
useAction: `{{ () => useSubmit({source: "updateRecord"}) }}`,
|
||||||
|
},
|
||||||
|
'x-designer': 'Action.Designer',
|
||||||
|
'x-action': `${statusApproval}`,
|
||||||
|
}}
|
||||||
|
type="x-action"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
import { SchemaInitializerItemType } from '@tachybase/client';
|
||||||
|
import { Registry } from '@tachybase/utils/client';
|
||||||
|
|
||||||
|
import { JOB_STATUS } from '../../../../../constants';
|
||||||
|
import updateFormConfig from './update';
|
||||||
|
|
||||||
|
type ValueOf<T> = T[keyof T];
|
||||||
|
|
||||||
|
export type FormType = {
|
||||||
|
type: 'create' | 'update' | 'custom';
|
||||||
|
title: string;
|
||||||
|
actions: ValueOf<typeof JOB_STATUS>[];
|
||||||
|
collection:
|
||||||
|
| string
|
||||||
|
| {
|
||||||
|
name: string;
|
||||||
|
fields: any[];
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ApprovalFormType = {
|
||||||
|
title: string;
|
||||||
|
config: {
|
||||||
|
useInitializer: ({ allCollections }?: { allCollections: any[] }) => SchemaInitializerItemType;
|
||||||
|
initializers?: {
|
||||||
|
[key: string]: React.FC;
|
||||||
|
};
|
||||||
|
components?: {
|
||||||
|
[key: string]: React.FC;
|
||||||
|
};
|
||||||
|
parseFormOptions(root: ISchema): { [key: string]: FormType };
|
||||||
|
};
|
||||||
|
block: {
|
||||||
|
scope?: {
|
||||||
|
[key: string]: () => any;
|
||||||
|
};
|
||||||
|
components?: {
|
||||||
|
[key: string]: React.FC;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const approvalFormOptions = new Registry<ApprovalFormType>();
|
||||||
|
approvalFormOptions.register('updateForm', updateFormConfig as ApprovalFormType);
|
@ -0,0 +1,67 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
CollectionProvider_deprecated,
|
||||||
|
createFormBlockSchema,
|
||||||
|
Plugin,
|
||||||
|
SchemaInitializerItem,
|
||||||
|
SchemaInitializerItemType,
|
||||||
|
useRecordCollectionDataSourceItems,
|
||||||
|
useSchemaInitializer,
|
||||||
|
useSchemaInitializerItem,
|
||||||
|
useSchemaTemplateManager,
|
||||||
|
} from '@tachybase/client';
|
||||||
|
|
||||||
|
import { traverseSchema } from '../../../../../utils';
|
||||||
|
import { ApprovalAddActionButton } from './AddActionButton.setting';
|
||||||
|
|
||||||
|
export class KitApprovalAddActionButton extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.schemaInitializerManager.add(ApprovalAddActionButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function InternalFormBlockInitializer({ schema, ...others }) {
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
const { getTemplateSchemaByMode } = useSchemaTemplateManager();
|
||||||
|
const items = useRecordCollectionDataSourceItems('FormItem') as SchemaInitializerItemType[];
|
||||||
|
const onClick = async (item) => {
|
||||||
|
const template = item.template ? await getTemplateSchemaByMode(item) : null;
|
||||||
|
const result = createFormBlockSchema({
|
||||||
|
actionInitializers: 'ApprovalAddActionButton',
|
||||||
|
...schema,
|
||||||
|
template,
|
||||||
|
});
|
||||||
|
delete result['x-acl-action-props'];
|
||||||
|
delete result['x-acl-action'];
|
||||||
|
const [formKey] = Object.keys(result.properties);
|
||||||
|
//获取actionBar的schemakey
|
||||||
|
const actionKey =
|
||||||
|
Object.entries(result.properties[formKey].properties).find(([key, f]) => f['x-component'] === 'ActionBar')?.[0] ||
|
||||||
|
'actions';
|
||||||
|
result.properties[formKey].properties[actionKey]['x-decorator'] = 'ActionBarProvider';
|
||||||
|
result.properties[formKey].properties[actionKey]['x-component-props'].style = {
|
||||||
|
marginTop: '1.5em',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
};
|
||||||
|
traverseSchema(result, (node) => {
|
||||||
|
if (node['x-uid']) {
|
||||||
|
delete node['x-uid'];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
insert(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
return <SchemaInitializerItem {...others} onClick={onClick} items={items} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FormBlockFactory = () => {
|
||||||
|
const itemConfig = useSchemaInitializerItem();
|
||||||
|
return (
|
||||||
|
<CollectionProvider_deprecated
|
||||||
|
dataSource={itemConfig.schema?.dataSource}
|
||||||
|
collection={itemConfig.schema?.collection}
|
||||||
|
>
|
||||||
|
<InternalFormBlockInitializer {...itemConfig} />
|
||||||
|
</CollectionProvider_deprecated>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,109 @@
|
|||||||
|
import { useMemo, useState } from 'react';
|
||||||
|
import { useMenuSearch } from '@tachybase/client';
|
||||||
|
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
import { FilterDynamicComponent } from '../../../../../components';
|
||||||
|
import { NAMESPACE } from '../../../locale';
|
||||||
|
import { FormBlockFactory } from './FormBlock.factory';
|
||||||
|
|
||||||
|
const updateFormConfig = {
|
||||||
|
title: `{{t("Update record form", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
config: {
|
||||||
|
initializers: {},
|
||||||
|
components: {
|
||||||
|
FilterDynamicComponent,
|
||||||
|
},
|
||||||
|
useInitializer: ({ allCollections }) => {
|
||||||
|
const childItems = useMemo(() => {
|
||||||
|
const results = allCollections.map(({ key, displayName, collections }) => ({
|
||||||
|
key,
|
||||||
|
name: key,
|
||||||
|
label: displayName,
|
||||||
|
type: 'subMenu',
|
||||||
|
children: collections.map(({ name, title, tableName }) => ({
|
||||||
|
name: _.camelCase(`updateRecordForm-child-${name}`),
|
||||||
|
type: 'item',
|
||||||
|
title: title || tableName,
|
||||||
|
schema: {
|
||||||
|
collection: name,
|
||||||
|
dataSource: key,
|
||||||
|
formType: 'update',
|
||||||
|
},
|
||||||
|
Component: FormBlockFactory,
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
return results;
|
||||||
|
}, [allCollections]);
|
||||||
|
const [openMenuKeys, setOpenMenuKeys] = useState([]);
|
||||||
|
const searchedChildren = useMenuSearch({ data: childItems, openKeys: openMenuKeys });
|
||||||
|
return {
|
||||||
|
name: 'updateRecordForm',
|
||||||
|
key: 'updateRecordForm',
|
||||||
|
type: 'subMenu',
|
||||||
|
title: `{{t("Update record form", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
componentProps: {
|
||||||
|
onOpenChange(keys) {
|
||||||
|
setOpenMenuKeys(keys);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
children: searchedChildren,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
parseFormOptions: (root) => {
|
||||||
|
const forms = {};
|
||||||
|
const formBlocks: any[] = findSchema(
|
||||||
|
root,
|
||||||
|
(item) => item['x-decorator'] === 'FormBlockProvider' && item['x-decorator-props'].formType === 'update',
|
||||||
|
);
|
||||||
|
|
||||||
|
formBlocks.forEach((formBlock) => {
|
||||||
|
const [formKey] = Object.keys(formBlock.properties);
|
||||||
|
const formSchema = formBlock.properties[formKey];
|
||||||
|
//获取actionBar的schemakey
|
||||||
|
const actionKey =
|
||||||
|
Object.entries(formSchema.properties).find(([key, f]) => f['x-component'] === 'ActionBar')?.[0] || 'actions';
|
||||||
|
forms[formKey] = {
|
||||||
|
...formBlock['x-decorator-props'],
|
||||||
|
type: 'update',
|
||||||
|
title: formBlock['x-component-props']?.title || formKey,
|
||||||
|
actions: findSchema(formSchema.properties[actionKey], (item) => item['x-component'] === 'Action').map(
|
||||||
|
(item) => ({
|
||||||
|
status: item['x-decorator-props'].value,
|
||||||
|
values: item['x-action-settings']?.assignedValues?.values,
|
||||||
|
key: item.name,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return forms;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
block: {
|
||||||
|
scope: {},
|
||||||
|
components: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// utils
|
||||||
|
function findSchema(schema, filter, onlyLeaf = false) {
|
||||||
|
const result = [];
|
||||||
|
|
||||||
|
if (!schema) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter(schema) && (!onlyLeaf || !schema.properties)) {
|
||||||
|
result.push(schema);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schema.properties) {
|
||||||
|
Object.keys(schema.properties).forEach((key) => {
|
||||||
|
result.push(...findSchema(schema.properties[key], filter));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default updateFormConfig;
|
@ -1,11 +1,16 @@
|
|||||||
import { Plugin } from '@tachybase/client';
|
import { Plugin } from '@tachybase/client';
|
||||||
|
|
||||||
import { PluginWorkflow } from '../../../../Plugin';
|
import { PluginWorkflow } from '../../../../Plugin';
|
||||||
|
import { ApprovalInstruction } from './Approval.node';
|
||||||
|
import { ApproverAddBlockInitializer } from './approver-interface/ApproverAddBlock.initializer';
|
||||||
import { ApproverActionConfigInitializer } from './approver-interface/Iz.ApproverActionConfig';
|
import { ApproverActionConfigInitializer } from './approver-interface/Iz.ApproverActionConfig';
|
||||||
import { ApproverAddBlockInitializer } from './approver-interface/Iz.ApproverAddBlock';
|
import { KitApprovalAddActionButton } from './forms/FormBlock.factory';
|
||||||
import { ApprovalInstruction } from './node.ApprovalInstruction';
|
|
||||||
|
|
||||||
export class KitApprovalInstruction extends Plugin {
|
export class KitApprovalInstruction extends Plugin {
|
||||||
|
async afterAdd(): Promise<void> {
|
||||||
|
this.pm.add(KitApprovalAddActionButton);
|
||||||
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
const pluginWorkflow = this.app.pm.get(PluginWorkflow);
|
const pluginWorkflow = this.app.pm.get(PluginWorkflow);
|
||||||
pluginWorkflow.registerInstruction('approval', ApprovalInstruction);
|
pluginWorkflow.registerInstruction('approval', ApprovalInstruction);
|
||||||
|
@ -5,8 +5,25 @@ import { useForm } from '@tachybase/schema';
|
|||||||
import { NAMESPACE } from '../../../locale';
|
import { NAMESPACE } from '../../../locale';
|
||||||
import { SchemaAddBlock } from './SchemaAddBlock.component';
|
import { SchemaAddBlock } from './SchemaAddBlock.component';
|
||||||
|
|
||||||
|
// 触发器-发起人的操作界面
|
||||||
|
export const LauncherInterface = () => {
|
||||||
|
const { values } = useForm();
|
||||||
|
const [dataSource, name] = parseCollectionName(values.collection);
|
||||||
|
const schema = getSchemaLauncherInterface({ values, dataSource, name });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SchemaComponent
|
||||||
|
components={{
|
||||||
|
SchemaAddBlock,
|
||||||
|
}}
|
||||||
|
schema={schema}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// 发起人操作界面
|
// 发起人操作界面
|
||||||
const getSchemaLauncherInterface = ({ values, dataSource, name }) => ({
|
function getSchemaLauncherInterface({ values, dataSource, name }) {
|
||||||
|
return {
|
||||||
name: values.collection,
|
name: values.collection,
|
||||||
type: 'void',
|
type: 'void',
|
||||||
properties: {
|
properties: {
|
||||||
@ -34,19 +51,5 @@ const getSchemaLauncherInterface = ({ values, dataSource, name }) => ({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}); // 触发器-发起人的操作界面
|
};
|
||||||
|
}
|
||||||
export const LauncherInterface = () => {
|
|
||||||
const { values } = useForm();
|
|
||||||
const [dataSource, name] = parseCollectionName(values.collection);
|
|
||||||
const schema = getSchemaLauncherInterface({ values, dataSource, name });
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SchemaComponent
|
|
||||||
components={{
|
|
||||||
SchemaAddBlock,
|
|
||||||
}}
|
|
||||||
schema={schema}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { JOB_STATUS } from '../../constants';
|
import { JOB_STATUS } from '../../constants';
|
||||||
import { lang, NAMESPACE } from './locale';
|
import { lang, NAMESPACE } from './locale';
|
||||||
|
|
||||||
// 审批状态
|
// 审批发起的状态
|
||||||
export const APPROVAL_STATUS = {
|
export const APPROVAL_STATUS = {
|
||||||
DRAFT: 0,
|
DRAFT: 0,
|
||||||
RETURNED: 1,
|
RETURNED: 1,
|
||||||
@ -11,6 +11,7 @@ export const APPROVAL_STATUS = {
|
|||||||
REJECTED: -1,
|
REJECTED: -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 审批执行的状态
|
||||||
export const APPROVAL_ACTION_STATUS = {
|
export const APPROVAL_ACTION_STATUS = {
|
||||||
ASSIGNED: null,
|
ASSIGNED: null,
|
||||||
PENDING: 0,
|
PENDING: 0,
|
||||||
|
@ -16,8 +16,6 @@ export function useDestroyAction() {
|
|||||||
return {
|
return {
|
||||||
async run() {
|
async run() {
|
||||||
try {
|
try {
|
||||||
console.log('%c Line:16 🍓 run', 'font-size:18px;color:#6ec1c2;background:#93c0a4');
|
|
||||||
|
|
||||||
_.set(field, ['data', 'loading'], true);
|
_.set(field, ['data', 'loading'], true);
|
||||||
|
|
||||||
await apiClient.resource('approvals').destroy({
|
await apiClient.resource('approvals').destroy({
|
||||||
|
@ -27,7 +27,6 @@ export function useWithdrawAction() {
|
|||||||
field.data.loading = false;
|
field.data.loading = false;
|
||||||
refreshTable();
|
refreshTable();
|
||||||
} catch (v) {
|
} catch (v) {
|
||||||
console.log('%c Line:33 🌽 v', 'font-size:18px;color:#33a5ff;background:#fca650', v);
|
|
||||||
if (field.data) {
|
if (field.data) {
|
||||||
field.data.loading = false;
|
field.data.loading = false;
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,12 @@ import React, { createContext, useContext } from 'react';
|
|||||||
|
|
||||||
import { useContextApprovalExecutions } from './Pd.ApprovalExecutions';
|
import { useContextApprovalExecutions } from './Pd.ApprovalExecutions';
|
||||||
|
|
||||||
export interface ApprovalAction {
|
interface ApprovalAction {
|
||||||
status: number | null;
|
status: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ContextApprovalAction = createContext<Partial<ApprovalAction>>({});
|
const ContextApprovalAction = createContext<Partial<ApprovalAction>>({});
|
||||||
|
|
||||||
export function useContextApprovalAction() {
|
|
||||||
return useContext(ContextApprovalAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ApprovalActionProvider({ children, ...props }) {
|
export function ApprovalActionProvider({ children, ...props }) {
|
||||||
const { status } = useContextApprovalExecutions();
|
const { status } = useContextApprovalExecutions();
|
||||||
|
|
||||||
@ -21,3 +17,8 @@ export function ApprovalActionProvider({ children, ...props }) {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useContextApprovalAction() {
|
||||||
|
const context = useContext(ContextApprovalAction);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
import { useAPIClient } from '@tachybase/client';
|
import { useAPIClient, useCollection } from '@tachybase/client';
|
||||||
import { useField, useForm } from '@tachybase/schema';
|
import { useField, useForm } from '@tachybase/schema';
|
||||||
|
|
||||||
import { useHandleRefresh } from '../../common/useHandleRefresh';
|
import { useHandleRefresh } from '../../common/useHandleRefresh';
|
||||||
import { useContextApprovalAction } from '../Pd.ApprovalAction';
|
import { useContextApprovalAction } from '../Pd.ApprovalAction';
|
||||||
import { useContextApprovalExecutions } from '../Pd.ApprovalExecutions';
|
import { useContextApprovalExecutions } from '../Pd.ApprovalExecutions';
|
||||||
|
|
||||||
export function useSubmit() {
|
export function useSubmit(props: any = {}) {
|
||||||
const { refreshTable } = useHandleRefresh();
|
const { refreshTable } = useHandleRefresh();
|
||||||
|
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
|
const collection = useCollection();
|
||||||
const approvalExecutions = useContextApprovalExecutions();
|
const approvalExecutions = useContextApprovalExecutions();
|
||||||
const { status } = useContextApprovalAction();
|
const { status } = useContextApprovalAction();
|
||||||
|
const { source } = props;
|
||||||
|
const needUpdateRecord = source === 'updateRecord';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
run: async () => {
|
run: async () => {
|
||||||
@ -20,14 +22,27 @@ export function useSubmit() {
|
|||||||
if (form.values.status) {
|
if (form.values.status) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await form.submit();
|
await form.submit();
|
||||||
field.data = field.data ?? {};
|
field.data = field.data ?? {};
|
||||||
field.data.loading = true;
|
field.data.loading = true;
|
||||||
|
|
||||||
|
if (needUpdateRecord) {
|
||||||
|
const collectionName = collection.name;
|
||||||
|
const targetId = form.values.id;
|
||||||
|
|
||||||
|
await api.resource(collectionName).update({
|
||||||
|
filterByTk: targetId,
|
||||||
|
values: form.values,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await api.resource('approvalRecords').submit({
|
await api.resource('approvalRecords').submit({
|
||||||
filterByTk: approvalExecutions.id,
|
filterByTk: approvalExecutions.id,
|
||||||
values: { ...form.values, status },
|
values: {
|
||||||
|
status,
|
||||||
|
needUpdateRecord,
|
||||||
|
data: form.values,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
field.data.loading = false;
|
field.data.loading = false;
|
||||||
|
@ -234,6 +234,7 @@ const approvalRecords = {
|
|||||||
async submit(context, next) {
|
async submit(context, next) {
|
||||||
const repository = utils.getRepositoryFromParams(context);
|
const repository = utils.getRepositoryFromParams(context);
|
||||||
const { filterByTk, values } = context.action.params;
|
const { filterByTk, values } = context.action.params;
|
||||||
|
const { data, status, needUpdateRecord } = values || {};
|
||||||
const { currentUser } = context.state;
|
const { currentUser } = context.state;
|
||||||
if (!currentUser) {
|
if (!currentUser) {
|
||||||
return context.throw(401);
|
return context.throw(401);
|
||||||
@ -254,13 +255,13 @@ const approvalRecords = {
|
|||||||
approvalRecord.execution.status ||
|
approvalRecord.execution.status ||
|
||||||
approvalRecord.job.status ||
|
approvalRecord.job.status ||
|
||||||
approvalRecord.status !== APPROVAL_ACTION_STATUS.PENDING ||
|
approvalRecord.status !== APPROVAL_ACTION_STATUS.PENDING ||
|
||||||
!(approvalRecord.node.config.actions ?? []).includes(values.status)
|
(!needUpdateRecord && !(approvalRecord.node.config.actions ?? []).includes(status))
|
||||||
) {
|
) {
|
||||||
return context.throw(400);
|
return context.throw(400);
|
||||||
}
|
}
|
||||||
await approvalRecord.update({
|
await approvalRecord.update({
|
||||||
status: values.status,
|
status: status,
|
||||||
comment: values.comment,
|
comment: data.comment,
|
||||||
snapshot: approvalRecord.approval.data,
|
snapshot: approvalRecord.approval.data,
|
||||||
summary: approvalRecord.approval.summary,
|
summary: approvalRecord.approval.summary,
|
||||||
collectionName: approvalRecord.approval.collectionName,
|
collectionName: approvalRecord.approval.collectionName,
|
||||||
|
Loading…
Reference in New Issue
Block a user