From 0d8bbba1dac36ec6ac04c537dad04eb2e06b25f6 Mon Sep 17 00:00:00 2001
From: "bai.zixv" <bai.zixv@foxmail.com>
Date: Wed, 3 Jul 2024 13:49:47 +0800
Subject: [PATCH] feat/approval-0702 (#1264)

Reviewed-on: https://git.daoyoucloud.com/daoyoucloud/tachybase/pulls/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>
---
 ...rovalInstruction.tsx => Approval.node.tsx} |   9 +-
 ...ionNode.tsx => ApprovalNode.component.tsx} |   0
 ...rConfig.tsx => ApproverConfig.context.tsx} |   0
 .../VC.SchemaConfigButtonApprover.tsx         |   2 +-
 ...k.tsx => ApproverAddBlock.initializer.tsx} |  33 +++---
 .../approver-interface/ApproverBlock.view.tsx |   2 +-
 .../EditableForm.schema.tsx                   |  45 --------
 .../Pd.ApprovalActionProvider.tsx             |   1 +
 .../forms/AddActionButton.setting.tsx         | 102 ++++++++++++++++
 .../instruction/forms/Approval.options.ts     |  45 ++++++++
 .../instruction/forms/FormBlock.factory.tsx   |  67 +++++++++++
 .../instruction/forms/update.tsx              | 109 ++++++++++++++++++
 .../configuration/instruction/plugin.ts       |   9 +-
 .../LauncherInterface.schema.tsx              |  65 ++++++-----
 .../src/client/features/approval/constants.ts |   3 +-
 .../launch/hooks/useDestroyAction.tsx         |   2 -
 .../launch/hooks/useWithdrawAction.tsx        |   1 -
 .../todos/Pd.ApprovalAction.tsx               |  11 +-
 .../approval-block/todos/hooks/useSubmit.tsx  |  25 +++-
 .../src/server/features/approval/actions.ts   |   7 +-
 20 files changed, 418 insertions(+), 120 deletions(-)
 rename packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/{node.ApprovalInstruction.tsx => Approval.node.tsx} (95%)
 rename packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/{VC.ApprovalInstructionNode.tsx => ApprovalNode.component.tsx} (100%)
 rename packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/{Pd.ContextApproverConfig.tsx => ApproverConfig.context.tsx} (100%)
 rename packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/{Iz.ApproverAddBlock.tsx => ApproverAddBlock.initializer.tsx} (77%)
 delete mode 100644 packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/EditableForm.schema.tsx
 create mode 100644 packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/forms/AddActionButton.setting.tsx
 create mode 100644 packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/forms/Approval.options.ts
 create mode 100644 packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/forms/FormBlock.factory.tsx
 create mode 100644 packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/forms/update.tsx

diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/node.ApprovalInstruction.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/Approval.node.tsx
similarity index 95%
rename from packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/node.ApprovalInstruction.tsx
rename to packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/Approval.node.tsx
index e7001c0d7..c7d3f2dae 100644
--- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/node.ApprovalInstruction.tsx
+++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/Approval.node.tsx
@@ -13,9 +13,9 @@ import { AssigneesSelect } from './approval-config/VC.AssigneesSelect';
 import { ContentTooltip } from './approval-config/VC.ContentTooltip';
 import { NegotiationConfig } from './approval-config/VC.NegotiationConfig';
 import { SchemaConfigButtonApprover } from './approval-config/VC.SchemaConfigButtonApprover';
+import { ApprovalInstructionNode } from './ApprovalNode.component';
 import { ApproverInterfaceComponent } from './approver-interface/ApproverInterface.schema';
 import { isApprovalReturnFunc } from './utils';
-import { ApprovalInstructionNode } from './VC.ApprovalInstructionNode';
 
 // 工作流节点 nodes - 人工处理->审批
 export class ApprovalInstruction extends Instruction {
@@ -42,13 +42,6 @@ export class ApprovalInstruction extends Instruction {
   ];
   // 审批节点表单设置
   fieldset = {
-    collection: {
-      type: 'string',
-      title: '{{t("Collection")}}',
-      'x-decorator': 'FormItem',
-      'x-component': 'DataSourceCollectionCascader',
-      // 'x-disabled': '{{ useWorkflowAnyExecuted() }}',
-    },
     branchMode: {
       type: 'boolean',
       title: `{{t("Pass mode", { ns: "${NAMESPACE}" })}}`,
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/VC.ApprovalInstructionNode.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/ApprovalNode.component.tsx
similarity index 100%
rename from packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/VC.ApprovalInstructionNode.tsx
rename to packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/ApprovalNode.component.tsx
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/Pd.ContextApproverConfig.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/ApproverConfig.context.tsx
similarity index 100%
rename from packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/Pd.ContextApproverConfig.tsx
rename to packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/ApproverConfig.context.tsx
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approval-config/VC.SchemaConfigButtonApprover.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approval-config/VC.SchemaConfigButtonApprover.tsx
index a584324e4..996fcd459 100644
--- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approval-config/VC.SchemaConfigButtonApprover.tsx
+++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approval-config/VC.SchemaConfigButtonApprover.tsx
@@ -2,7 +2,7 @@ import React from 'react';
 import { useActionContext } from '@tachybase/client';
 
 import { ConfigButtonView } from '../../trigger/ConfigButton.view';
-import { ContextApproverConfig } from '../Pd.ContextApproverConfig';
+import { ContextApproverConfig } from '../ApproverConfig.context';
 
 // 审批人操作界面->进入配置按钮
 export const SchemaConfigButtonApprover = (props) => {
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/Iz.ApproverAddBlock.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/ApproverAddBlock.initializer.tsx
similarity index 77%
rename from packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/Iz.ApproverAddBlock.tsx
rename to packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/ApproverAddBlock.initializer.tsx
index 8ebf31306..c3fd1d566 100644
--- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/Iz.ApproverAddBlock.tsx
+++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/ApproverAddBlock.initializer.tsx
@@ -1,11 +1,11 @@
-import { gridRowColWrap, SchemaInitializer, usePlugin } from '@tachybase/client';
+import { gridRowColWrap, SchemaInitializer, useDataSourceManager, usePlugin } from '@tachybase/client';
 
 import { useFlowContext } from '../../../../../FlowContext';
 import { useAvailableUpstreams, useNodeContext } from '../../../../../nodes';
 import { PluginWorkflow } from '../../../../../Plugin';
 import { useTrigger } from '../../../../../triggers';
 import { NAMESPACE } from '../../../locale';
-import { EditableForm } from './EditableForm.schema';
+import { approvalFormOptions, ApprovalFormType } from '../forms/Approval.options';
 import { ApproverAddBlockComponent } from './VC.ApproverAddBlock';
 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',
       name: 'dataBlocks',
@@ -79,6 +66,22 @@ export const ApproverAddBlockInitializer = new SchemaInitializer({
         ].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',
       type: 'itemGroup',
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/ApproverBlock.view.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/ApproverBlock.view.tsx
index cfe151c8f..aa835f190 100644
--- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/ApproverBlock.view.tsx
+++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/ApproverBlock.view.tsx
@@ -8,7 +8,7 @@ import { Spin } from 'antd';
 import { DetailsBlockProvider, SimpleDesigner } from '../../../../../components';
 import { FormBlockProvider } from '../../../common/Pd.FormBlock';
 import { flatSchemaArray } from '../../../constants';
-import { ContextApproverConfig } from '../Pd.ContextApproverConfig';
+import { ContextApproverConfig } from '../ApproverConfig.context';
 import { ActionBarProvider } from './Pd.ActionBarProvider';
 import { ApprovalActionProvider } from './Pd.ApprovalActionProvider';
 import { ContextApproverBlock } from './Pd.SchemaConfigButtonContext';
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/EditableForm.schema.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/EditableForm.schema.tsx
deleted file mode 100644
index 9fc52f527..000000000
--- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/EditableForm.schema.tsx
+++ /dev/null
@@ -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} />;
-};
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/Pd.ApprovalActionProvider.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/Pd.ApprovalActionProvider.tsx
index dcf362b53..378a8634d 100644
--- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/Pd.ApprovalActionProvider.tsx
+++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/approver-interface/Pd.ApprovalActionProvider.tsx
@@ -1,3 +1,4 @@
 export const ApprovalActionProvider = (props) => {
+  console.log('%c Line:2 🍰 props', 'font-size:18px;color:#4fff4B;background:#7f2b82', props);
   return props.children;
 };
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/forms/AddActionButton.setting.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/forms/AddActionButton.setting.tsx
new file mode 100644
index 000000000..078db4f1b
--- /dev/null
+++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/forms/AddActionButton.setting.tsx
@@ -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"
+    />
+  );
+}
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/forms/Approval.options.ts b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/forms/Approval.options.ts
new file mode 100644
index 000000000..715a8f99e
--- /dev/null
+++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/forms/Approval.options.ts
@@ -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);
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/forms/FormBlock.factory.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/forms/FormBlock.factory.tsx
new file mode 100644
index 000000000..0a9b6b020
--- /dev/null
+++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/forms/FormBlock.factory.tsx
@@ -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>
+  );
+};
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/forms/update.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/forms/update.tsx
new file mode 100644
index 000000000..b88ac7c29
--- /dev/null
+++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/forms/update.tsx
@@ -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;
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/plugin.ts b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/plugin.ts
index 07eb4c56e..cba308129 100644
--- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/plugin.ts
+++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/instruction/plugin.ts
@@ -1,11 +1,16 @@
 import { Plugin } from '@tachybase/client';
 
 import { PluginWorkflow } from '../../../../Plugin';
+import { ApprovalInstruction } from './Approval.node';
+import { ApproverAddBlockInitializer } from './approver-interface/ApproverAddBlock.initializer';
 import { ApproverActionConfigInitializer } from './approver-interface/Iz.ApproverActionConfig';
-import { ApproverAddBlockInitializer } from './approver-interface/Iz.ApproverAddBlock';
-import { ApprovalInstruction } from './node.ApprovalInstruction';
+import { KitApprovalAddActionButton } from './forms/FormBlock.factory';
 
 export class KitApprovalInstruction extends Plugin {
+  async afterAdd(): Promise<void> {
+    this.pm.add(KitApprovalAddActionButton);
+  }
+
   async load() {
     const pluginWorkflow = this.app.pm.get(PluginWorkflow);
     pluginWorkflow.registerInstruction('approval', ApprovalInstruction);
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/trigger/launcher-interface/LauncherInterface.schema.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/trigger/launcher-interface/LauncherInterface.schema.tsx
index d41f7dc7d..82e2b7e5d 100644
--- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/trigger/launcher-interface/LauncherInterface.schema.tsx
+++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/configuration/trigger/launcher-interface/LauncherInterface.schema.tsx
@@ -5,37 +5,7 @@ import { useForm } from '@tachybase/schema';
 import { NAMESPACE } from '../../../locale';
 import { SchemaAddBlock } from './SchemaAddBlock.component';
 
-// 发起人操作界面
-const getSchemaLauncherInterface = ({ values, dataSource, name }) => ({
-  name: values.collection,
-  type: 'void',
-  properties: {
-    drawer: {
-      type: 'void',
-      title: `{{t("Initiator's interface", { ns: "${NAMESPACE}" })}}`,
-      'x-component': 'Action.Drawer',
-      'x-component-props': {
-        className: css`
-          .ant-drawer-body {
-            background: const(--tb-box-bg);
-          }
-        `,
-      },
-      properties: {
-        applyForm: {
-          type: 'string',
-          'x-decorator': 'CollectionProvider_deprecated',
-          'x-decorator-props': {
-            dataSource,
-            name,
-          },
-          'x-component': 'SchemaAddBlock',
-        },
-      },
-    },
-  },
-}); // 触发器-发起人的操作界面
-
+// 触发器-发起人的操作界面
 export const LauncherInterface = () => {
   const { values } = useForm();
   const [dataSource, name] = parseCollectionName(values.collection);
@@ -50,3 +20,36 @@ export const LauncherInterface = () => {
     />
   );
 };
+
+// 发起人操作界面
+function getSchemaLauncherInterface({ values, dataSource, name }) {
+  return {
+    name: values.collection,
+    type: 'void',
+    properties: {
+      drawer: {
+        type: 'void',
+        title: `{{t("Initiator's interface", { ns: "${NAMESPACE}" })}}`,
+        'x-component': 'Action.Drawer',
+        'x-component-props': {
+          className: css`
+            .ant-drawer-body {
+              background: const(--tb-box-bg);
+            }
+          `,
+        },
+        properties: {
+          applyForm: {
+            type: 'string',
+            'x-decorator': 'CollectionProvider_deprecated',
+            'x-decorator-props': {
+              dataSource,
+              name,
+            },
+            'x-component': 'SchemaAddBlock',
+          },
+        },
+      },
+    },
+  };
+}
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/constants.ts b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/constants.ts
index 5047428a7..523ac9c98 100644
--- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/constants.ts
+++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/constants.ts
@@ -1,7 +1,7 @@
 import { JOB_STATUS } from '../../constants';
 import { lang, NAMESPACE } from './locale';
 
-// 审批状态
+// 审批发起的状态
 export const APPROVAL_STATUS = {
   DRAFT: 0,
   RETURNED: 1,
@@ -11,6 +11,7 @@ export const APPROVAL_STATUS = {
   REJECTED: -1,
 };
 
+// 审批执行的状态
 export const APPROVAL_ACTION_STATUS = {
   ASSIGNED: null,
   PENDING: 0,
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/usage/approval-block/launch/hooks/useDestroyAction.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/usage/approval-block/launch/hooks/useDestroyAction.tsx
index 3794f871f..de9cacdd0 100644
--- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/usage/approval-block/launch/hooks/useDestroyAction.tsx
+++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/usage/approval-block/launch/hooks/useDestroyAction.tsx
@@ -16,8 +16,6 @@ export function useDestroyAction() {
   return {
     async run() {
       try {
-        console.log('%c Line:16 🍓 run', 'font-size:18px;color:#6ec1c2;background:#93c0a4');
-
         _.set(field, ['data', 'loading'], true);
 
         await apiClient.resource('approvals').destroy({
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/usage/approval-block/launch/hooks/useWithdrawAction.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/usage/approval-block/launch/hooks/useWithdrawAction.tsx
index 326024a6b..6c606d6b7 100644
--- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/usage/approval-block/launch/hooks/useWithdrawAction.tsx
+++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/usage/approval-block/launch/hooks/useWithdrawAction.tsx
@@ -27,7 +27,6 @@ export function useWithdrawAction() {
         field.data.loading = false;
         refreshTable();
       } catch (v) {
-        console.log('%c Line:33 🌽 v', 'font-size:18px;color:#33a5ff;background:#fca650', v);
         if (field.data) {
           field.data.loading = false;
         }
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/usage/approval-block/todos/Pd.ApprovalAction.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/usage/approval-block/todos/Pd.ApprovalAction.tsx
index c392e86e9..1335797d6 100644
--- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/usage/approval-block/todos/Pd.ApprovalAction.tsx
+++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/usage/approval-block/todos/Pd.ApprovalAction.tsx
@@ -2,16 +2,12 @@ import React, { createContext, useContext } from 'react';
 
 import { useContextApprovalExecutions } from './Pd.ApprovalExecutions';
 
-export interface ApprovalAction {
+interface ApprovalAction {
   status: number | null;
 }
 
 const ContextApprovalAction = createContext<Partial<ApprovalAction>>({});
 
-export function useContextApprovalAction() {
-  return useContext(ContextApprovalAction);
-}
-
 export function ApprovalActionProvider({ children, ...props }) {
   const { status } = useContextApprovalExecutions();
 
@@ -21,3 +17,8 @@ export function ApprovalActionProvider({ children, ...props }) {
 
   return null;
 }
+
+export function useContextApprovalAction() {
+  const context = useContext(ContextApprovalAction);
+  return context;
+}
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/usage/approval-block/todos/hooks/useSubmit.tsx b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/usage/approval-block/todos/hooks/useSubmit.tsx
index 0955ebe16..ad6a05f36 100644
--- a/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/usage/approval-block/todos/hooks/useSubmit.tsx
+++ b/packages/plugins/@tachybase/plugin-workflow/src/client/features/approval/usage/approval-block/todos/hooks/useSubmit.tsx
@@ -1,18 +1,20 @@
-import { useAPIClient } from '@tachybase/client';
+import { useAPIClient, useCollection } from '@tachybase/client';
 import { useField, useForm } from '@tachybase/schema';
 
 import { useHandleRefresh } from '../../common/useHandleRefresh';
 import { useContextApprovalAction } from '../Pd.ApprovalAction';
 import { useContextApprovalExecutions } from '../Pd.ApprovalExecutions';
 
-export function useSubmit() {
+export function useSubmit(props: any = {}) {
   const { refreshTable } = useHandleRefresh();
-
   const field = useField();
   const api = useAPIClient();
   const form = useForm();
+  const collection = useCollection();
   const approvalExecutions = useContextApprovalExecutions();
   const { status } = useContextApprovalAction();
+  const { source } = props;
+  const needUpdateRecord = source === 'updateRecord';
 
   return {
     run: async () => {
@@ -20,14 +22,27 @@ export function useSubmit() {
         if (form.values.status) {
           return;
         }
-
         await form.submit();
         field.data = field.data ?? {};
         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({
           filterByTk: approvalExecutions.id,
-          values: { ...form.values, status },
+          values: {
+            status,
+            needUpdateRecord,
+            data: form.values,
+          },
         });
 
         field.data.loading = false;
diff --git a/packages/plugins/@tachybase/plugin-workflow/src/server/features/approval/actions.ts b/packages/plugins/@tachybase/plugin-workflow/src/server/features/approval/actions.ts
index 5cb5c9cf4..1aa9e75c3 100644
--- a/packages/plugins/@tachybase/plugin-workflow/src/server/features/approval/actions.ts
+++ b/packages/plugins/@tachybase/plugin-workflow/src/server/features/approval/actions.ts
@@ -234,6 +234,7 @@ const approvalRecords = {
   async submit(context, next) {
     const repository = utils.getRepositoryFromParams(context);
     const { filterByTk, values } = context.action.params;
+    const { data, status, needUpdateRecord } = values || {};
     const { currentUser } = context.state;
     if (!currentUser) {
       return context.throw(401);
@@ -254,13 +255,13 @@ const approvalRecords = {
       approvalRecord.execution.status ||
       approvalRecord.job.status ||
       approvalRecord.status !== APPROVAL_ACTION_STATUS.PENDING ||
-      !(approvalRecord.node.config.actions ?? []).includes(values.status)
+      (!needUpdateRecord && !(approvalRecord.node.config.actions ?? []).includes(status))
     ) {
       return context.throw(400);
     }
     await approvalRecord.update({
-      status: values.status,
-      comment: values.comment,
+      status: status,
+      comment: data.comment,
       snapshot: approvalRecord.approval.data,
       summary: approvalRecord.approval.summary,
       collectionName: approvalRecord.approval.collectionName,